【问题标题】:Forward host port to docker container将主机端口转发到 docker 容器
【发布时间】:2013-07-20 04:53:28
【问题描述】:

是否可以让 Docker 容器访问主机开放的端口?具体来说,我在主机上运行了 MongoDB 和 RabbitMQ,我想在 Docker 容器中运行一个进程来监听队列并(可选地)写入数据库。

我知道我可以将一个端口从容器转发到主机(通过 -p 选项)并从 Docker 容器内连接到外部世界(即互联网),但我不想公开 RabbitMQ以及从主机到外部世界的 MongoDB 端口。

编辑:一些澄清:

Starting Nmap 5.21 ( http://nmap.org ) at 2013-07-22 22:39 CEST
Nmap scan report for localhost (127.0.0.1)
Host is up (0.00027s latency).
PORT     STATE SERVICE
6311/tcp open  unknown

joelkuiper@vps20528 ~ % docker run -i -t base /bin/bash
root@f043b4b235a7:/# apt-get install nmap
root@f043b4b235a7:/# nmap 172.16.42.1 -p 6311 # IP found via docker inspect -> gateway

Starting Nmap 6.00 ( http://nmap.org ) at 2013-07-22 20:43 UTC
Nmap scan report for 172.16.42.1
Host is up (0.000060s latency).
PORT     STATE    SERVICE
6311/tcp filtered unknown
MAC Address: E2:69:9C:11:42:65 (Unknown)

Nmap done: 1 IP address (1 host up) scanned in 13.31 seconds

我必须用这个技巧来获得与容器的任何互联网连接:My firewall is blocking network connections from the docker container to outside

编辑:最终我使用pipework 创建了一个自定义网桥,并让服务在网桥IP 上侦听。我采用了这种方法,而不是让 MongoDB 和 RabbitMQ 在 docker 桥上监听,因为它提供了更大的灵活性。

【问题讨论】:

    标签: linux networking port docker


    【解决方案1】:

    TLDR;

    仅用于本地开发,请执行以下操作:

    1. 在您的笔记本电脑/计算机/PC/Mac 上启动服务或 SSH 隧道。
    2. 构建/运行您的 Docker 映像/容器以连接到主机名 host.docker.internal:<hostPort>

    注意:还有gateway.docker.internal,我没试过。

    END_TLDR;

    例如,如果您在容器中使用它:

    PGPASSWORD=password psql -h localhost -p 5432 -d mydb -U myuser
    

    改成这样:

    PGPASSWORD=password psql -h host.docker.internal -p 5432 -d mydb -U myuser
    

    这会神奇地连接到我的主机上运行的服务。您不需要使用--net=host-p "hostPort:ContainerPort"-P

    背景

    详情见:https://docs.docker.com/docker-for-mac/networking/#use-cases-and-workarounds

    我在 Windows 10 上通过 SSH 隧道连接到 AWS RDS Postgres 实例。我只需将容器中的 localhost:containerPort 更改为 host.docker.internal:hostPort

    【讨论】:

      【解决方案2】:

      正如其中一个 cmets 所述,这适用于 Mac(可能也适用于 Windows/Linux):

      我想从容器连接到主机上的服务

      主机有一个不断变化的 IP 地址(如果您没有网络访问权限,则没有)。我们建议您连接到特殊的 DNS 名称 host.docker.internal,它解析为主机使用的内部 IP 地址。这是出于开发目的,不适用于 Docker Desktop for Mac 之外的生产环境。

      您也可以使用gateway.docker.internal 访问网关。

      引自https://docs.docker.com/docker-for-mac/networking/

      这对我有用,但不使用 --net=host

      【讨论】:

      • 这是 Mac 最简单的解决方案。
      • 这适用于 Windows,但不适用于 Linux(问题是专门针对的)。在具有足够新版本 Docker 的 Linux 上,有一个解决方法:stackoverflow.com/a/62431165/424381
      【解决方案3】:

      如果 MongoDB 和 RabbitMQ 在 Host 上运行,那么端口应该已经暴露,因为它不在 Docker 中。

      您不需要-p 选项即可将端口从容器公开到主机。默认情况下,所有端口都是公开的。 -p 选项允许您将一个端口从容器公开到主机外部。

      所以,我猜你根本不需要-p,它应该可以正常工作:)

      【讨论】:

      • 我知道,但似乎我遗漏了一些信息:请参阅最近的编辑,因为我无法访问主机上的端口。
      • 你需要设置 rabbitmq 和 mongodb 来监听网桥,而不仅仅是你的主网络接口。
      • @creack 如何让rabbitmq和mongodb在桥上监听?
      • @RyanWalls 试试docker inspect network bridge。查看 IPAM -> 配置 -> 网关。就我而言,它是172.17.0.1
      【解决方案4】:

      我在从 docker 容器访问 LDAP 服务器时遇到了类似的问题。 我给容器设置了固定IP,并添加了防火墙规则。

      docker-compose.yml:

      version: '2'
      services:
        containerName:
          image: dockerImageName:latest
          extra_hosts:
            - "dockerhost:192.168.50.1"
          networks:
            my_net:
              ipv4_address: 192.168.50.2
      networks:
        my_net:
          ipam:
            config:
            - subnet: 192.168.50.0/24
      

      iptables 规则:

      iptables -A INPUT -j ACCEPT -p tcp -s 192.168.50.2 -d $192.168.50.1 --dport portnumberOnHost

      容器内访问dockerhost:portnumberOnHost

      【讨论】:

        【解决方案5】:

        您也可以创建一个 ssh 隧道。

        docker-compose.yml:

        ---
        
        version: '2'
        
        services:
          kibana:
            image: "kibana:4.5.1"
            links:
              - elasticsearch
            volumes:
              - ./config/kibana:/opt/kibana/config:ro
        
          elasticsearch:
            build:
              context: .
              dockerfile: ./docker/Dockerfile.tunnel
            entrypoint: ssh
            command: "-N elasticsearch -L 0.0.0.0:9200:localhost:9200"
        

        docker/Dockerfile.tunnel:

        FROM buildpack-deps:jessie
        
        RUN apt-get update && \
            DEBIAN_FRONTEND=noninteractive \
            apt-get -y install ssh && \
            apt-get clean && \
            rm -rf /var/lib/apt/lists/*
        
        COPY ./config/ssh/id_rsa /root/.ssh/id_rsa
        COPY ./config/ssh/config /root/.ssh/config
        COPY ./config/ssh/known_hosts /root/.ssh/known_hosts
        RUN chmod 600 /root/.ssh/id_rsa && \
            chmod 600 /root/.ssh/config && \
            chown $USER:$USER -R /root/.ssh
        

        config/ssh/config:

        # Elasticsearch Server
        Host elasticsearch
            HostName jump.host.czerasz.com
            User czerasz
            ForwardAgent yes
            IdentityFile ~/.ssh/id_rsa
        

        这样elasticsearch 就拥有一条通往正在运行服务(Elasticsearch、MongoDB、PostgreSQL)的服务器的隧道,并通过该服务公开端口 9200。

        【讨论】:

        • 您基本上是将私钥放在 Docker 映像中。秘密不应该进入 Docker 映像。
        • 这是迄今为止唯一可用的理智解决方案。
        • @TeohHanHui IMO 对于早期集成测试或本地开发,此解决方案是完美的。我们可以模拟这些服务,稍后将在生产环境中可用。我想为 Cosmos DB 模拟器执行此操作,我们只获得 localhost CERT,但通过端口转发,我们也可以在“远程”主机上使用它。
        【解决方案6】:

        一个简单但相对不安全的方法是使用--net=host 选项到docker run

        此选项使容器使用主机的网络堆栈。然后,您只需使用“localhost”作为主机名即可连接到主机上运行的服务。

        这更容易配置,因为您不必将服务配置为接受来自 docker 容器的 IP 地址的连接,并且您不必告诉 docker 容器特定的 IP 地址或主机名来连接到,只是一个港口。

        例如,您可以通过运行以下命令对其进行测试,假设您的映像名为my_image,您的映像包含telnet 实用程序,并且您要连接的服务位于端口 25:

        docker run --rm -i -t --net=host my_image telnet localhost 25
        

        如果您考虑这样做,请参阅此页面上的安全注意事项:

        https://docs.docker.com/articles/networking/

        上面写着:

        --net=host -- 告诉 Docker 跳过将容器放置在单独的网络堆栈中。本质上,这个选择告诉 Docker 不要容器化容器的网络!虽然容器进程仍将被限制在它们自己的文件系统和进程列表以及资源限制中,但一个快速的 ip addr 命令将向您显示,在网络方面,它们位于主 Docker 主机的“外部”,并且可以完全访问其网络接口.请注意,这不会让容器重新配置主机网络堆栈——这需要 --privileged=true——但它确实让容器进程像任何其他根进程一样打开低编号端口。它还允许容器访问本地网络服务,如 D-bus。这可能导致容器中的进程能够执行意外的事情,例如重新启动计算机。您应该谨慎使用此选项。

        【讨论】:

        • 对于在 Linux 上不使用 docker(例如使用一些虚拟化)的任何人来说,这都行不通,因为主机将是包含的虚拟机,而不是实际的主机操作系统。
        • 尤其是在 MacOS 上,这不可能(没有一些变通方法):docs.docker.com/docker-for-mac/networking/…
        • 在 MacOS 上,--net=host 不能让您的容器进程使用localhost 连接到您的主机。相反,让您的容器连接到仅适用于 MacOS 的特殊主机名 docker.for.mac.host.internal 而不是 localhostdocker run 不需要额外的参数来工作。如果你想让你的容器平台不可知,你可以使用-e 将它作为环境变量传递。这样您就可以连接到在 env var 中命名的主机,并在 MacOS 上传递 docker.for.mac.host.internal,在 Linux 上传递 localhost
        • mac 的最新主机名是host.docker.internal,见doc
        • 同样适用于 Windows docker run --rm -it --net=host postgres bash 然后 psql -h host.docker.internal -U postgres
        【解决方案7】:

        您的 docker 主机向所有容器公开了一个适配器。假设你使用的是最近的 ubuntu,你可以运行

        ip addr
        

        这会给你一个网络适配器列表,其中一个看起来像

        3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP
        link/ether 22:23:6b:28:6b:e0 brd ff:ff:ff:ff:ff:ff
        inet 172.17.42.1/16 scope global docker0
        inet6 fe80::a402:65ff:fe86:bba6/64 scope link
           valid_lft forever preferred_lft forever
        

        您需要告诉 rabbit/mongo 绑定到该 IP (172.17.42.1)。之后,您应该能够从您的容器中打开到 172.17.42.1 的连接。

        【讨论】:

        • 容器如何知道向哪个IP发送请求?我可以硬编码这个值(这里和我的测试台上的 172.17.42.1,但这总是真的吗?),但这似乎违背了与任何主机一起工作的 docker 原则!
        • @Seldo:该界面是否需要配置才能显示?我使用的是 docker 1.7.1,我只有loeth0
        • 如果主机只在 127.0.0.1 上监听,是否有可能做到这一点?
        • “你需要告诉 rabbit/mongo 绑定到那个 IP (172.17.42.1)。之后,你应该能够从你的容器中打开到 172.17.42.1 的连接。”如果你能解释如何做到这一点会很好
        • 正如@Novaterata 提到的,有人可以解释一下这个过程吗?
        猜你喜欢
        • 2017-01-03
        • 1970-01-01
        • 2014-09-01
        • 1970-01-01
        • 1970-01-01
        • 2016-07-17
        • 1970-01-01
        • 1970-01-01
        • 2016-07-22
        相关资源
        最近更新 更多