将主机端口转发到Docker容器


166

主机可以打开Docker容器访问端口吗?具体而言,我在主机上运行了MongoDB和RabbitMQ,我想在Docker容器中运行一个进程以侦听队列并(可选)写入数据库。

我知道我可以将端口从容器转发到主机(通过-p选项),并可以从Docker容器中连接到外部环境(即Internet),但我不想公开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

我必须执行此技巧才能使容器获得任何互联网连接:我的防火墙阻止了从Docker容器到外部的网络连接

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

Answers:


53

您的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的连接。


35
容器如何知道向哪个IP发送请求?我可以在此处和在我的测试台上对值进行硬编码(172.17.42.1,但这始终是真的吗?),但这似乎违反了与任何主机一起工作的docker原则!
JP。

2
@Seldo:该界面是否需要配置才能显示?我正在使用docker 1.7.1,并且只有loand eth0
mknecht

8
如果主机仅在127.0.0.1上侦听,是否可以通过某种方式执行此操作?
HansHarhoff '16

5
“您将需要告诉Rabbit / mongo绑定到该IP(172.17.42.1)。在那之后,您应该能够从容器中打开与172.17.42.1的连接。” 如果您解释了如何做,那就太好了
Novaterata

1
如@Novaterata所述,有人可以解释一下该过程吗?
keskinsaf

121

一种简单但相对不安全的方法是使用的--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。这可能导致容器中的进程能够执行意外操作,例如重新启动计算机。


12
对于不在Linux上使用docker的任何人(例如使用某些虚拟化),这将不起作用,因为主机将是包含VM,而不是实际的主机OS。
塞巴斯蒂安·格拉夫

13
特别是在MacOS上,这是不可能的(没有一些解决方法):docs.docker.com/docker-for-mac/networking/…–
pje

15
在MacOS上,--net=host不适用于允许您的容器进程使用来连接到主机localhost。而是让您的容器仅连接到特殊的MacOS主机名,docker.for.mac.host.internal而不是localhost。不需要额外的参数即可docker run使其工作。-e如果要保持容器平台不可知,可以使用env var来传递它。这样,您可以连接到env var中命名的主机,并docker.for.mac.host.internal在MacOS和localhostLinux上传递。
联邦制药

18
Mac的最新主机名是host.docker.internal,请参阅doc
xysun

docker run --rm -it --net=host postgres bash然后,对于Windows同样如此psql -h host.docker.internal -U postgres
Leo Cavalcante

12

您还可以创建一个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。


8
您基本上是将私钥放入Docker映像中。绝不应该将秘密纳入Docker映像中。
Teoh Han Hui

2
这是迄今为止唯一可用的理智解决方案。
helvete

5

我从码头工人容器访问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


3

如果MongoDB和RabbitMQ在主机上运行,​​则该端口应该已经公开,因为它不在Docker中。

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

因此,我的猜测是您完全不需要-p,它应该可以正常工作:)


1
我知道这一点,但似乎缺少一些信息:请参阅最近的编辑,因为我无法访问主机上的端口。
JoelKuiper

2
您需要设置rabbitmq和mongodb以便在网桥上侦听,而不仅仅是在主网络接口上。
creack

13
@creack如何让Rabbitmq和mongodb在桥上监听?
Ryan Walls 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.