有可能让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桥上监听,因为它提供了更大的灵活性。


当前回答

现在在所有平台上更简单的方法是使用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"。

其他回答

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

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

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

您还可以创建一个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端口与该服务公开。

现在在所有平台上更简单的方法是使用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,这对我来说是可行的。像这样创建隧道(注意开始时的0.0.0.0):

-L 0.0.0.0:8080:localhost:8081

这将允许任何访问您的计算机的人连接到端口8080,从而访问所连接服务器上的端口8081。

然后,在容器内部使用"host.docker.internal",例如:

curl host.docker.internal:8081

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

我想从容器连接到主机上的服务 主机有一个不断变化的IP地址(如果没有网络访问,则没有)。我们建议您连接到特殊的DNS名称host.docker.internal,它可以解析为主机使用的内部IP地址。这是为了开发目的,不能在Mac Docker Desktop以外的生产环境中工作。 您也可以使用gateway.docker.internal到达网关。

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

我不需要使用——net=host。