繁体   English   中英

如何从node.js应用程序获取docker映射端口?

[英]How to get docker mapped ports from node.js application?

我想从node.js应用程序中获取映射端口。

恩。

docker-compose

my-app:
    build:
        context: ./my-app
    ports:
        - "80"     
    volumes:
        - /var/run/docker.sock:/tmp/docker.sock
    networks:
        - private

docker ps -a

1234 my-app "/usr/local/bin/dock…"   About a minute ago   Up About a minute   0.0.0.0:33962->80/tcp

我想从node.js应用程序获得33962端口。

有任何想法吗?

所以第一件事是,目前,docker没有以任何直接方式在容器内提供元数据信息。 虽然有相同的解决方法

现在,在容器内部传递一个docker socket,当涉及开发环境但不涉及prod环境时,你可以使用它

因此,您要做的是构建自己的元数据服务,然后让它将信息提供给您的容器,而不会暴露docker socket本身。

现在要获取此元数据,您可以使用两种方法

基于容器ID的元数据服务

在这种情况下,您创建一个简单的端点,如/metadata/container_id ,然后返回所需的数据。

此服务可以在主主机本身上运行,也可以在具有docker socket映射的容器上运行。

现在,在NodeJS服务中,您将获得当前容器ID,然后使用相同的方法来调用此服务。 这可以通过以下线程中列出的不同方式完成

Docker,如何从容器中获取容器信息?

您可以限制返回的数据,如果我只想要暴露端口,我只会返回端口的详细信息。

这种方法的问题在于,您仍然让服务找出自己的容器ID,同时也相信它正在发送正确的容器ID

元数据服务,不传递任何信息

为了在不传递信息的情况下获取元数据信息,我们需要确定哪个pad调用了元数据服务器

这可以通过使用源IP来识别。 然后我们需要确定哪个容器属于这个IP。 一旦我们拥有容器ID,我们就可以返回元数据。

下面是一个简单的bash脚本,它也是如此

#!/bin/bash
CONTAINER_IP=$SOCAT_PEERADDR
CONTAINER_ID=$(docker ps -q | xargs docker inspect -f '{{.Id}}|{{range .NetworkSettings.Networks}}{{.IPAddress}}|{{end}}' | grep "|$CONTAINER_IP|" | cut -d '|' -f 1)
echo -e "HTTP/1.1 200 OK\r\nContent-Type: application/json;\r\nServer: $SOCAT_SOCKADDR:$SOCAT_SOCKPORT\r\nClient: $SOCAT_PEERADDR:$SOCAT_PEERPORT\r\nConnection: close\r\n";
METADATA=$(docker inspect -f '{{ json .NetworkSettings.Ports }}' $CONTAINER_ID)
echo $METADATA

现在要将其转换为Web服务器,我们可以使用socat

sudo socat -T 1 TCP-L:10081,pktinfo,reuseaddr,fork EXEC:./return_metadata.sh

当我们调用此元数据服务器时,现在在容器内部

root@a9cf6dabdfb4:/# curl 192.168.33.100:10081
{"80/tcp":[{"HostIp":"0.0.0.0","HostPort":"8080"}]}

和docker ps一样

CONTAINER ID        IMAGE                 COMMAND                  CREATED             STATUS              PORTS                  NAMES
a9cf6dabdfb4        nginx                 "nginx -g 'daemon of…"   3 hours ago         Up 3 hours          0.0.0.0:8080->80/tcp   nginx

现在,您如何创建这样的元数据服务器。 你可以使用任何方法

您还可以在extra_hosts docker-compose.yml使用extra_hosts添加服务的IP,并像curl metaserver:10081一样进行调用curl metaserver:10081

这应该是一种体面安全的方法,不会影响您的需求

您可以使用docker port

docker port my-app 80

那将包括听众IP。 如果您需要将其剥离,可以使用shell执行此操作:

docker port my-app 80 | cut -f2 -d:

不幸的是,这只适用于访问docker socket。 我不建议在容器内进行此级别的访问。

通常,大多数人通过将变量传递给他们的应用程序并控制在其docker-compose文件中发布的端口来解决此问题。 例如:

my-app:
    build:
        context: ./my-app
    ports:
        - "8080:80"     
    environment:
        - PUBLISHED_PORT=8080
    volumes:
        - /var/run/docker.sock:/tmp/docker.sock
    networks:
        - private

然后应用程序将查看环境变量以重新配置自身。

我为此使用了dockerode库。

解:

const Docker = require( 'dockerode' );
const os = require( 'os' );

const docker = new Docker( { socketPath: '/tmp/docker.sock' } );
const container = docker.getContainer( os.hostname() );

container.inspect( function( error, data ) {
    if ( error ) {
        return null;
    }

    const mappedPort = data.NetworkSettings.Ports[ '80/tcp' ][ 0 ].HostPort
} );

那么使用shelljs呢?

const shelljs = require('shelljs');

const output = shelljs.exec('docker ps --format {{.Ports}}', {silent: true}).stdout.trim();

console.log(output); // 80/tcp
                     // 19999/tcp
                     // 9000/tcp
                     // 8080/tcp, 50000/tcp

输出是一个字符串,现在让我们映射响应。

const shelljs = require('shelljs');

const output = shelljs.exec('docker ps --format {{.Ports}}', {silent: true}).stdout.trim();

const ports = output.split('\n').map(portList => portList.split(',').map(port => Number(port.split('/')[0].trim())));

这样ports返回包含端口的数组数组:

[ [ 80 ], [ 19999 ], [ 9000 ], [ 8080, 50000 ] ]

在您的情况下,您需要以下数字:-> 所以你可以这样做:

const shelljs = require('shelljs');

const output = shelljs
  .exec('docker ps --format {{.Ports}}', { silent: true })
  .stdout.trim();

const aoa = output.split('\n').map(portList => portList.split(','));

console.log(aoa); // [ [ '9000/tcp', ' 0.0.0.0:9000->123/tcp ' ], [ ' 80/tcp' ] ]

let ports = [];

aoa.forEach(arr => {
  arr.forEach(p => {
    // match strings which contains :PORT-> pattern;
    const match = p.match(/:(\d+)->/);
    if (match) {
      ports.push(Number(match[1]));
    }
  });
});

console.log(ports); // [ 9000 ]

最后,您需要在容器内安装docker并按照此处的说明连接docker socks。

更新29/06/2019:

正如@Tarun Lalwani所说,如果共享docker socks是一个问题,你可以创建一个与你的主应用程序共享网络的应用程序,并有一个返回ports的GET方法。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM