在Compose文件中,--add-host=host.docker.internal:host-gateway的等效语句是什么?

44

从Docker版本20.10开始(https://github.com/moby/moby/pull/40007),有一个新的特殊字符串host-gateway可以在Linux系统上的--add-host运行标志内使用,以允许docker容器内部与本地机器直接连接。这非常好。

但在Compose文件中,--add-host=host.docker.internal:host-gateway的等效内容是什么?

例如:

$ docker run \
  --rm \
  --name postgres \
  -p "5433:5432" \
  -e POSTGRES_PASSWORD=**** \
  --add-host=host.docker.internal:host-gateway \
  -d postgres:14.1-bullseye

在这个Docker Compose等效的模板中,相同的--add-host标志应该如何适用:

version: '3.9'

services:
  postgres:
    image: postgres:14.1-bullseye
    environment:
      POSTGRES_PASSWORD: ****
    ports:
      - "5433:5432"

可以肯定的是,在服务级别上不是network_mode: host(参见#文档)。

1个回答

70
实际上,Docker Compose 的等效方法是将相同的字符串附加到 extra_hosts 参数(#Doc)中,如下所示:
version: '3.9'

services:
  postgres:
    image: postgres:14.1-bullseye
    environment:
      POSTGRES_PASSWORD: ****
    ports:
      - "5433:5432"
    extra_hosts:
      - "host.docker.internal:host-gateway"

您可以看到它已成功映射到docker0接口的IP地址,这里是172.17.0.1,从容器内部可以看到,例如:

$ docker-compose up -d
$ docker-compose exec postgres bash

接下来,在容器内部执行:

root@5864db7d7fba:/# apt update && apt -y install netcat
root@5864db7d7fba:/# nc -vz host.docker.internal 80
Connection to host.docker.internal (172.17.0.1) 80 port [tcp/http] succeeded!

(假设主机上没有关闭或限制docker0接口的IP端口80的防火墙)。

更多相关信息请参见:
https://medium.com/@TimvanBaarsen/how-to-connect-to-the-docker-host-from-inside-a-docker-container-112b4c71bc66

但是...请注意...

警告 ⚠️

这通常会匹配主机上docker0接口的172.17.0.1 IP。因此,如果您使用Compose文件(而不是使用docker run)启动容器,则该容器将无限期地依赖于构建Compose服务期间创建的网络。并且此网络将使用形如172.xxx.0.1的随机网关地址,这肯定与默认的docker网关172.17.0.1不同,例如可能是172.22.0.1

如果您仅明确授权从主机本地服务的端口172.17.0.1进行连接,这可能会给您带来一些麻烦。 实际上,无法从容器内部ping该服务的端口,正是因为具有不同分配的网关地址(172.22.0.1)。

因此,由于无法预先知道Compose网络将具有哪个网关地址,我强烈建议您在Compose文件中明智地构建自定义network定义,例如:

version: '3.9'

networks:
  network1:
    name: my-network
    attachable: true
    ipam:
      driver: default
      config:
        - subnet: 172.18.0.0/16
          ip_range: 172.18.5.0/24
          gateway: 172.18.0.1

services:
  postgres:
    image: postgres:14.1-bullseye
    environment:
      POSTGRES_PASSWORD: ****
    ports:
      - "5433:5432"
    networks:
      - network1

如果需要的话,我建议使用一些IP范围计算器工具,比如http://jodies.de/ipcalc?host=172.18.5.0&mask1=24&mask2=,尤其是在使用CIDR表示法定义范围时。

最后,启动您的容器,并验证新指定的网关地址172.18.0.1是否已正确使用:

$ docker inspect tmp_postgres_1  -f '{{range .NetworkSettings.Networks}}{{.Gateway}}{{end}}'
172.18.0.1

连接到它,安装netcat并进行验证:

root@9fe8de220d44:/# nc -vz 172.18.0.1 80
Connection to 172.18.0.1 80 port [tcp/http] succeeded!

您可能还需要相应地调整防火墙规则和/或本地服务(例如数据库)允许的IP地址。

另一种解决方案

是使用docker network连接到现有的默认bridge网络。为此,在启动容器后,运行以下命令:

$ docker network connect bridge tmp_postgres_1

现在,检查应该会给你两个IP地址:一个是你设置的(如果有),或者是Docker在容器创建期间自动设置的IP地址,另一个是bridge IP地址:
$ docker inspect tmp_postgres_1 -f '{{range .NetworkSettings.Networks}}{{.Gateway}}{{end}}' 
172.17.0.1 172.18.0.1

或者

您可以跳过手动创建网络,直接在Compose服务定义中使用以下network_mode:标志,告知加入bridge网络:

version: '3.9'

services:
  postgres:
    image: postgres:14.1-bullseye
    environment:
      POSTGRES_PASSWORD: ****
    ports:
      - "5433:5432"
    # removed networks: and add this:
    network_mode: bridge
    extra_hosts:
      - "host.docker.internal:host-gateway"

现在,无论您是使用docker network connect...方法还是在Compose文件中使用network_mode:标志,您通常都可以成功加入默认的bridge网络,并与网关172.17.0.1连接。这将允许您使用该网关IP连接到主机,无论是通过输入其数字值还是(如果设置了)变量host.docker.internal

root@9fe8de220d44:/# nc -vz 172.18.0.1 80
Connection to 172.18.0.1 80 port [tcp/http] succeeded!
root@9fe8de220d44:/# nc -vz 172.17.0.1 80
Connection to 172.18.0.1 80 port [tcp/http] succeeded!
root@9fe8de220d44:/# nc -vz host.docker.internal 80
Connection to host.docker.internal (172.17.0.1) 80 port [tcp/http] succeeded!

⚠️但是,加入“桥接”网络后,您的容器也可以与该网络上的所有其他容器进行通信(如果它们已经发布了端口),反之亦然。因此,如果您需要将其与这些其他容器清楚地分开,请最好不要这样做,并坚持使用其自己的自定义网络!

如果出现问题怎么办?

如果您在尝试后弄乱了docker网络,则可能会遇到以下错误消息:

Creating tmp_postgres_1 ... error

ERROR: for tmp_postgres_1  Cannot start service postgres: failed to create endpoint tmp_postgres_1 on network bridge: network 895de42e2a0bdaab5423a6356a079fae55aae41ae268ee887ed214bd6fd88486 does not exist

ERROR: for postgress  Cannot start service postgres: failed to create endpoint tmp_postgres_1 on network bridge: network 895de42e2a0bdaab5423a6356a079fae55aae41ae268ee887ed214bd6fd88486 does not exist
ERROR: Encountered errors while bringing up the project.

尽管 895de42e2a0bdaab5423a6356a079fae55aae41ae268ee887ed214bd6fd88486 桥接网络确实存在,但你必须通过重新启动计算机或在最幸运的情况下使用以下命令清除所有内容:

docker service
$ sudo service docker restart

执行 docker networkd prune -f 可能不足以解决问题。


更多文档详见:
https://docs.docker.com/compose/networking/
https://docs.docker.com/compose/compose-file/compose-file-v3/#networks
https://github.com/compose-spec/compose-spec/blob/master/spec.md#networks-top-level-element

这里提供更多的文档链接,以便了解有关Docker Compose网络和文件格式的更多信息。请点击以上链接查看详情。

在具备以下规格的主机上进行了测试:
Ubuntu: 18.04.6 LTS
Kernel: 5.4.0-94-generic
Docker: 20.10.12,构建版本 e91ed57
Docker Compose: 1.27.4,构建版本 40524192


非常好的答案,但是“networks_mode”选项中可能有错别字吗?在文档中应该是“network_mode”:https://docs.docker.com/compose/compose-file/compose-file-v3/#network_mode - Ramiro R.C.
确实,要么是networks,要么是network_mode,你说得对。谢谢你的警觉性。我已经修复了。 - swiss_knight
所有关于Docker部分的内容都已经讲完了。你可能还需要在PostgreSQL配置部分做一些工作,以便允许从网关IP连接,方法是在postgresql.conf中将其添加到listen_addresses中。 - smido
1
请确保 live-restore: false,否则在 Linux 下似乎无法使用 host.docker.internal - Melroy van den Berg

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