是否可以让主机打开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
我必须这样做才能获得容器的任何互联网连接:My firewall is blocking network connections from the docker container to outside
编辑:最后我使用pipework创建了一个自定义网桥,并让服务监听网桥IP。我采用这种方法而不是让MongoDB和RabbitMQ在docker桥上监听,因为它提供了更大的灵活性。
答案 0 :(得分:91)
一种简单但相对不安全的方法是使用--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等本地网络服务。这可能导致容器中的进程能够执行意外操作,例如重新启动计算机。您应该谨慎使用此选项。
答案 1 :(得分:46)
您的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的连接。
答案 2 :(得分:8)
您还可以创建一个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。
答案 3 :(得分:7)
如其中一条评论所述,这适用于 Mac(也可能适用于 Windows/Linux):
<块引用>我想从容器连接到主机上的服务
主机有一个不断变化的 IP 地址(如果您没有网络访问权限,则没有)。我们建议您连接到解析为主机使用的内部 IP 地址的特殊 DNS 名称 host.docker.internal
。这是用于开发目的,不适用于 Docker Desktop for Mac 之外的生产环境。
您也可以使用 gateway.docker.internal
到达网关。
引用自https://docs.docker.com/docker-for-mac/networking/
这对我有用,无需使用 --net=host
。
答案 4 :(得分:3)
如果MongoDB和RabbitMQ在主机上运行,那么该端口应该已经公开,因为它不在Docker中。
您不需要-p
选项即可将端口从容器公开给主机。默认情况下,所有端口都会公开。 -p
选项允许您将容器中的端口公开给主机外部。
所以,我的猜测是你根本不需要-p
它应该可以正常工作:)
答案 5 :(得分:3)
我在从docker容器访问LDAP-Server时遇到了类似的问题。 我为容器设置了固定的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
答案 6 :(得分:1)
TLDR;
仅针对本地开发,请执行以下操作:
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 上的 AWS RDS Postgres 实例的 SSH 隧道一起使用。我只需将容器中的 localhost:containerPort
更改为 host.docker.internal:hostPort
。