有没有办法将绑定到1​​27.0.0.1的Docker容器端口公开给主机?

时间:2018-09-26 08:31:54

标签: docker docker-compose

我在绑定到127.0.0.1:8888的容器内运行服务。
我想将此端口公开给主机。
docker-compose支持吗?

我在docker-compose.yml中尝试了以下操作,但没有用。

expose: 
  - "8888"
ports:
  - "8888:8888"

P.S。在我的情况下,无法将服务绑定到容器内部的0.0.0.0。


更新:提供一个简单的示例:

docker-compose.yml

version: '3'
services:  
  myservice:
    expose: 
      - "8888"
    ports:
      - "8888:8888"
    build: .

Dockerfile

FROM centos:7
RUN yum install -y nmap-ncat
CMD ["nc", "-l", "-k", "localhost", "8888"]

命令:

$> docker-compose up --build
$> # Starting test1_myservice_1 ... done
$> # Attaching to test1_myservice_1

$> nc -v -v localhost 8888
$> # Connection to localhost 8888 port [tcp/*] succeeded!
TEST
$>

在控制台中输入TEST后,连接已关闭,这意味着该端口并未真正暴露,尽管最初显示了成功消息。我的真实服务也会出现相同的问题。
但是,如果我在容器内绑定到0.0.0.0(而不是localhost),则一切正常。

3 个答案:

答案 0 :(得分:3)

通常答案是否定的,并且在几乎每种情况下,您都应该重新配置应用程序以侦听0.0.0.0。避免更改应用程序以监听容器内所有接口的任何尝试都应被视为增加项目技术负担的黑客。


为了进一步说明我的意见,每个容器默认都在其自己的网络名称空间中运行。容器内部的环回接口与主机和其他容器中的环回接口是分开的。因此,如果您在容器内侦听127.0.0.1,则该网络名称空间之外的任何内容都无法访问该端口。这与在VM上侦听环回并尝试从另一个VM连接到该端口没有什么不同,Linux不允许您连接。

有一些解决方法:

  1. 您可以破解iptables来转发连接,但是我个人会避免这种情况。 Docker很大程度上基于对iptables规则的自动更改,因此您的风险会与该自动化发生冲突或在下次重新创建容器时被破坏。
  2. 您可以在容器内设置一个代理,以在所有接口上侦听并转发到回送接口。像nginx这样的东西会起作用。
  3. 您可以在同一网络名称空间中获取内容。

最后一个有两种实现方式。在容器之间,可以在另一个容器的网络名称空间中运行一个容器。这通常是在调试网络时完成的,也是Pod在Kubernetes中的工作方式。这是运行第二个容器的示例:

$ docker run -it --rm --net container:$(docker ps -lq) nicolaka/netshoot /bin/sh
/ # ss -lnt
State       Recv-Q Send-Q        Local Address:Port    Peer Address:Port
LISTEN      0      10                127.0.0.1:8888                  *:*
LISTEN      0      128             127.0.0.11:41469                  *:*
/ # nc -v -v localhost 8888
Connection to localhost 8888 port [tcp/8888] succeeded!
TEST
/ #

请注意--net container:...(我使用docker ps -lq来获取实验室中上次启动的容器ID)。这使得两个单独的容器在同一名称空间中运行。

如果需要从docker外部访问此文件,则可以删除网络命名空间,并将容器直接附加到主机网络。对于一次性容器,可以使用

docker run --net host ...

在撰写时,它看起来像:

version: '3'
services:  
  myservice:
    network_mode: "host"
    build: .

您可以看到docker compose documentation on this option here。群集模式不支持此功能,并且您不尝试以这种模式发布端口,因为您将尝试在相同网络名称空间之间发布端口。

旁注,不需要任何expose。它仅用于提供文档和一些自动化工具,但不会影响容器到容器的网络连接,也不会影响发布特定端口的能力。

答案 1 :(得分:1)

根据@BMitch投票的答案:“如果容器使用其自己的网络名称空间运行,则无法直接从外部访问此端口”。

基于此,我认为值得针对此问题提供解决方法

一种方法是在运行服务之前在容器内设置iptables规则,以进行端口重定向。但是,这似乎要求将iptables模块显式加载到主机(according to this)上。这在某种程度上破坏了可移植性。

我的方式(使用socat:将*:8889转发到127.0.0.1:8888。)

Dockerfile

...
yum install -y socat
RUN echo -e '#!/bin/bash\n./localservice &\nsocat TCP4-LISTEN:8889,fork 
TCP4:127.0.0.1:8888\n' >> service.sh
RUN chmod u+x service.sh
ENTRYPOINT ["./service.sh"]

docker-compose.yml

version: '3'
services:  
  laax-pdfjs:
    ports:
        # Switch back to 8888 on host
      - "8888:8889"
    build: .

答案 2 :(得分:0)

检查您的docker compose版本并根据该版本进行配置。

  

未声明版本的撰写文件被视为“版本1”。在这些文件中,所有服务都在文档的根目录声明。

Reference

这是我设置端口的方法:

version: "3"

services:
  myservice:
    image: myimage:latest
    ports:
      - "80:80"

如果您可以共享docker-compose.yaml的其余部分,我们可以为您提供进一步的帮助。