我该如何将一个docker容器的端口转发到另一个容器?

45

我经常看到的解决方案是将一个docker容器的端口暴露给主机。

在我的情况下,我想将一个本地端口从一个容器转发到另一个容器。

假设我在A容器上运行了一个服务,它有一个硬编码配置来访问localhost 3306上的db。但是我想在B容器上运行db服务器。

从A-localhost:3306到B-IP:3306的端口转发的最佳方法是什么?

7个回答

40

在您的容器中安装socat,并在启动时运行

socat TCP-LISTEN:3306,fork TCP:B-IP:3306 &

这将在本地监听3306端口,并将任何流量双向传递到B-IP:3306。socat可在名为socat的软件包中获得。因此,您可以运行以下任一命令来安装它:

$ yum install -y socat
$ apt install -y socat
$ apk add socat

编辑-1

您甚至可以通过不触及原始容器来完成此操作。

Dockerfile

FROM alpine
RUN apk update && apk add socat

按照以下方式构建文件

docker build -t socat .

现在从相同的容器中运行一个容器。
docker run --name mysql-bridge-a-to-b --net=container:<containerAid> socat socat TCP-LISTEN:3306,fork TCP:BIP:3306

这将在A的网络上运行此容器。因此,当它在A的网络上侦听时,localhost:3306将在A中可用,即使A容器未被触摸。


3
在 Docker Hub 上有一个 Alpine socat 映像:https://hub.docker.com/r/alpine/socat/ - Joe Heffer
尽管我的问题与此无直接关系,但这帮助我解决了它!感谢和干杯 :) - Ajay Tom George
“--net=container:someContainer”。这个有文档记录吗? - Marinos An
这对于单个容器很有效,但是如果要为多个容器执行此操作,例如将A:3306 -> B:3306C:3306 -> B:3306进行映射,我应该启动2个socat容器吗? - Kelvin Hu
使用alpine/socat: docker run --network=container:nameOrIdOfcontainerA alpine/socat TCP-LISTEN:3306,fork TCP:nameOrIpOfContainerB:3306 以及关于--network的文档。 - dlauzon
显示剩余2条评论

24

您可以使用网络模式等于主机的容器来运行。

docker run --network=host ...

在这种情况下,从容器的角度来看,localhost或127.0.0.1将指向主机。因此,如果您的数据库运行在另一个监听3306端口的B容器中,则容器A中的localhost:3306地址将连接到容器B中的数据库。


1
绝对是个传奇!+1 - Ash
这真的解决了我的问题。dockerA在自己的网络中运行(可能会暴露给主机)。dockerB也是一样。我的直觉是,在docker run -p上公开它将透明地使其具有主机网络。 - adonese
4
这仅在Linux上受支持:https://docs.docker.com/network/host/ - TmTron
为什么不使用桥接/覆盖网络呢? - OneCricketeer

23
如果您希望容器B的端口在容器A上表现为本地主机端口,则可以使用network选项将容器B设置为container模式启动,以在容器A的网络命名空间中启动容器B。
示例:
docker run --net=container:A postgres

地点:

  • A 是你想要映射到的容器的名称或标识符。

这将在与 A 同一网络命名空间的容器中启动 postgres,因此 postgres 容器中打开的任何端口都会在与 A 相同的接口上被打开,并且应该可以在容器 A 内部的 localhost 上访问。


6
这对我来说似乎比 docker run --network=host 更为合适。 - Marinos An
请问Docker Compose的版本是多少? - undefined

0

对于在Windows上进行开发,我们可以简单地使用host.docker.internal:请参见docker-for-windows networking

我想从容器连接到主机上的服务
我们建议您连接到特殊的DNS名称host.docker.internal,该名称解析为主机使用的内部IP地址。这仅用于开发目的,在Docker Desktop for Windows之外的生产环境中不起作用。

所以在您的情况下:

  • 容器A发布端口3306
    此端口现在可在主机上使用
  • 容器B可以简单地连接到host.docker.internal:3306

0

只有在它们共享同一文件时才为真。否则它们将作为两个独立的容器,彼此之间没有访问权限,需要进行端口映射。 - David S
@DavidS 你似乎在暗示使用Compose,但这并非必需。你可以使用 docker run --network 命令,两个镜像可以互相使用对方的名称。 - OneCricketeer

-1

这个解决方案对于所述的用例来说有些过度,但如果您在研究更大范围时遇到了这种情况。

Hashicorp Consul安装一个本地代理,将localhost端口代理到目录中注册的服务的ip地址和端口。 https://www.consul.io/intro

除了在我建立的系统中使用他们的产品外,我与Hashicorp没有任何关系。


过度杀伤力。桥接网络是内置的https://docs.docker.com/network/network-tutorial-standalone/ - OneCricketeer

-2
你可以使用Docker-Compose来完成这个任务。
docker-compose.yml示例:
version: '3'
services:
  web:
    build: .web
    environment:
      DB_HOST: mysql:3306  # the service name below
    ports:
     - "5000:5000"
  mysql:
    build: .mysql
    ports:
     - "3306:3306"  # optional: used for accessing from the host, not from the other container

这里有两个Docker实例:web和mysql,所以每个Docker实例可以通过您定义的服务名称相互访问。
希望对您有所帮助。

1
我不知道为什么这个没有评论就被踩了。对我来说,这是最好的选择,因为我不必费心去找出另一个容器的端口或主机名。MySQL的URI将是<服务名称:端口>,例如:"mysql:3306"。 - Stevko
4
由于数据库访问被硬编码为“localhost:3306”,因此无法将其更改为连接到“mysql:3306”。 - Julian Pieles

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接