繁体   English   中英

将主机端口转发到 docker 容器

[英]Forward host port to docker container

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

我知道我可以将一个端口从容器转发到主机(通过 -p 选项)并从 Docker 容器内连接到外部世界(即 inte.net),但我不想公开 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

我必须做这个技巧才能在容器内获得任何 inte.net 连接: 我的防火墙正在阻止从 docker 容器到外部的网络连接

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

一个简单但相对不安全的方法是使用--net=host选项来 docker 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。 这可能导致容器中的进程能够执行意外的事情,例如重新启动计算机。 您应该谨慎使用此选项。

您的 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"

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。

正如其中一条评论所述,这适用于 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

我在从 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

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

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

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

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

对于较新版本的 Docker,这对我有用。 像这样创建隧道(注意开头的 0.0.0.0):

-L 0.0.0.0:8080:localhost:8081

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

然后,在容器内只使用“host.docker.internal”,例如:

curl host.docker.internal:8081

为什么不使用稍微不同的解决方案,像这样?

services:
  kubefwd:
    image: txn2/kubefwd
    command: ...
  app:
    image: bash
    command:
     - sleep
     - inf
    init: true
    network_mode: service:kubefwd

REF: txn2/kubefwd:用于本地开发的批量端口转发 Kubernetes 服务。

现在在所有平台下更简单的方法是使用host.docker.internal 让我们首先从 Docker 运行命令开始:

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名称。 该名称将自动解析为内部 Docker IP 地址( docker0接口)。

无论如何,比方说...您还在主机(端口 80)上运行 web 服务。 您现在应该能够您的 Docker 容器中访问该服务。试试看: nc -vz host.docker.internal 80

全部使用network_mode: "host"

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM