Compose 文件中 --add-host=host.docker.internal:host-gateway 的等价物是什么

What is the equivalent of --add-host=host.docker.internal:host-gateway in a Compose file

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

但是 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

如何将相同的 --add-host 标记放入此 Docker Compose 等效模板中:

version: '3.9'

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

肯定不是:network_mode: host 在服务级别(参见 #Doc)。

实际的 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!

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

可在此处找到更多相关信息:
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 形式的随机网关地址,这肯定不同于 172.17.0.1 默认 docker 网关,例如 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= to help yourself in that task, especially when defining ranges using the CIDR notation。

最后,启动您的容器。并验证新指定的网关地址 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!

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

另一个解决方案

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

$ docker network connect bridge tmp_postgres_1

现在,检查应该给你两个 IP;您设置的那个(如果有的话)或在容器创建期间由 docker 自动神奇地设置的那个,以及 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: 标志,您通常都可以通过网关 172.17.0.1 成功加入默认 bridge 网络,这将允许您使用该网关 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!

⚠️ 但是通过加入 bridge 网络,您还可以让您的容器与该网络上的所有其他容器通信(如果它们已发布端口),反之亦然。因此,如果您需要明确地将其与这些其他容器区分开来,您最好不要这样做并坚持使用自己的自定义网络!

如果出现问题怎么办?

如果您在尝试后弄乱了 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 服务来清除所有这些网络:

$ 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


在具有以下规格的主机上测试:
Ubuntu:18.04.6 LTS
内核:5.4.0-94-generic
Docker: 20.10.12, 构建 e91ed57
Docker 撰写:1.27.4,构建 40524192