我想从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 app获得33962
端口。
有什么想法吗?
答案 0 :(得分:8)
所以第一件事是,当前,docker并未以任何直接方式在容器内部提供元数据信息。虽然有一些解决方法
现在,当容器涉及开发环境而不涉及产品环境时,您可以随便使用一个Docker套接字
因此,您要做的是构建自己的元数据服务,然后让它在不暴露docker套接字本身的情况下将信息返回给您的容器。
现在要获取此元数据,您可以使用两种方法
在这种情况下,您将创建一个简单的端点,例如/metadata/container_id
,然后返回所需的数据。
此服务可以在主主机本身上运行,也可以在映射了docker套接字的容器上运行。
现在,在NodeJS服务中,您将获得当前的容器ID,然后使用该ID来调用此服务。可以按照以下线程中列出的不同方式完成
Docker, how to get container information from within the container?
您可以限制返回的数据,以防万一,我只希望公开端口,仅返回端口的详细信息。
此方法的问题在于,您仍然要让服务找出其自己的容器ID,同时还要信任该服务正在发送正确的容器ID
要获取元数据信息而又不传递信息,我们需要确定哪个垫向元数据服务器发出了呼叫
这可以通过使用源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
添加服务的IP,然后像curl metaserver:10081
那样拨打电话
这应该是一种相当安全的方法,而不会损害您的需求
答案 1 :(得分:2)
您可以使用docker port
。
docker port my-app 80
那将包括监听器IP。如果您需要关闭它,可以使用shell执行此操作:
docker port my-app 80 | cut -f2 -d:
不幸的是,这只适用于访问docker socket。我不建议安装该插座,只是在容器内的这种访问级别。
通常,大多数人通过将变量传递给他们的应用程序并控制其docker-compose文件中发布的端口来解决此问题。 E.g:
my-app:
build:
context: ./my-app
ports:
- "8080:80"
environment:
- PUBLISHED_PORT=8080
volumes:
- /var/run/docker.sock:/tmp/docker.sock
networks:
- private
然后应用程序会查看环境变量以重新配置自己。
答案 2 :(得分:2)
使用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 ]
最后,您需要按照示例here的说明将docker安装在容器中并连接docker袜子。
更新2019年6月29日:
如@Tarun Lalwani所说,如果共享docker袜子是一个问题,则可以创建一个与主应用程序共享网络并具有返回ports
的GET方法的应用程序。
答案 3 :(得分:0)
我为此使用了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
} );