有可能让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
我必须这样做才能在容器内获得任何互联网连接:我的防火墙阻止了从docker容器到外部的网络连接
编辑:最终我使用管道创建了一个自定义网桥,并让服务侦听网桥IP。我使用这种方法而不是让MongoDB和RabbitMQ在docker桥上监听,因为它提供了更大的灵活性。
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的连接。
您还可以创建一个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"
码头工人/ 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
ssh problem / / problem:
# Elasticsearch Server
Host elasticsearch
HostName jump.host.czerasz.com
User czerasz
ForwardAgent yes
IdentityFile ~/.ssh/id_rsa
这样一来,elasticsearch就有了一个连接到服务器上运行的服务(elasticsearch, MongoDB, PostgreSQL)的隧道,并将9200端口与该服务公开。
一个简单但相对不安全的方法是使用——net=host选项来运行docker。
此选项使容器使用主机的网络堆栈。然后,只需使用“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 -- Tells Docker to skip placing the container inside of a separate network stack. In essence, this choice tells Docker to not containerize the container's networking! While container processes will still be confined to their own filesystem and process list and resource limits, a quick ip addr command will show you that, network-wise, they live “outside” in the main Docker host and have full access to its network interfaces. Note that this does not let the container reconfigure the host network stack — that would require --privileged=true — but it does let container processes open low-numbered ports like any other root process. It also allows the container to access local network services like D-bus. This can lead to processes in the container being able to do unexpected things like restart your computer. You should use this option with caution.
现在在所有平台上更简单的方法是使用host.docker.internal。让我们首先从Docker run命令开始:
docker run --add-host=host.docker.internal:host-gateway [....]
或者在使用Docker Compose时,将以下内容添加到您的服务:
extra_hosts:
- "host.docker.internal:host-gateway"
这样一个完整的Docker Compose文件的例子应该是这样的:
version: "3"
services:
your_service:
image: username/docker_image_name
restart: always
networks:
- your_bridge_network
volumes:
- /home/user/test.json:/app/test.json
ports:
- "8080:80"
extra_hosts:
- "host.docker.internal:host-gateway"
networks:
your_bridge_network:
这只是一个例子。但是,如果这个docker映像将在端口80上启动一个服务,那么它将在端口8080上的主机上可用。
更重要的是对于你的用例;如果Docker容器想要使用来自主机系统的服务,现在可以使用特殊的host. Docker .internal名称。该名称将自动解析为docker0接口的内部Docker IP地址。
不管怎样,让我们说……您还在主机上(端口80)运行web服务。您现在应该能够在Docker容器中访问该服务。试试:nc -vz host.docker.internal 80。
所有没有使用network_mode: "host"。
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的连接。
TLDR;
只适用于本地发展,请采取以下措施:
在笔记本电脑/电脑/PC/Mac上启动服务或SSH隧道。
构建/运行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
我将此与SSH隧道一起使用到Windows 10上的AWS RDS Postgres实例。我只需要在容器中使用localhost:containerPort更改为host.docker.internal:hostPort。