1. 概述

在使用 Docker 开发容器化应用时,经常需要容器访问运行在宿主机上的服务。在 macOS 和 Windows 上,Docker 提供了一个特殊的 DNS 名称 host.docker.internal 来指向宿主机的 IP,但在 Linux 上,这一功能长期以来需要手动配置。幸运的是,Docker 20.10 版本引入了 host-gateway,为 Linux 用户提供了类似的便利。

本文将介绍 --add-host=host.docker.internal:host-gateway 的作用,以及如何在 Docker Compose 中实现相同的功能。


2. 容器访问宿主机的方式

假设我们有一个运行在容器中的后端服务,而数据库运行在宿主机上,如何让容器访问这个数据库?

  • 在 macOS 和 Windows 上,Docker 内置了 host.docker.internal 这个 DNS 名称,会自动解析为宿主机的内网 IP。
  • 在 Linux 上,默认不支持 host.docker.internal,需要手动配置宿主机的 IP 或使用 --add-host 参数指定。这显然不够灵活,也缺乏可移植性。

从 Docker 20.10 开始,引入了 host-gateway 这个关键字,它可以在容器内部动态解析为宿主机的网关 IP。结合 --add-host 使用,可以实现如下命令:

--add-host=host.docker.internal:host-gateway

这行命令的作用是:在容器的 /etc/hosts 文件中添加一条记录,把 host.docker.internal 映射到宿主机的网关 IP。


3. docker run 示例

我们可以通过一个简单的 docker run 命令来演示如何使用该功能。比如启动一个 PostgreSQL 容器,并让它能够访问宿主机上的服务:

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

参数说明:

  • --rm:容器停止后自动删除
  • --name:指定容器名称
  • -p "5433:5432":将容器内的 5432 端口映射到宿主机的 5433
  • -e POSTGRES_PASSWORD=yourpassword:设置 PostgreSQL 的密码
  • --add-host=host.docker.internal:host-gateway:添加宿主机的解析
  • -d:后台运行容器

这样,容器就可以通过 host.docker.internal 访问宿主机上的服务了。


4. Docker Compose 中的等价写法

在 Docker Compose 中,我们可以通过 extra_hosts 字段实现相同的效果。下面是等价的 docker-compose.yml 配置:

version: '3.9'

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

字段说明:

  • image:使用哪个镜像
  • environment:环境变量配置
  • ports:端口映射
  • extra_hosts:等价于 --add-host,用于添加自定义 hosts 映射

当 Docker Compose 解析到 extra_hosts 中的 host.docker.internal:host-gateway 时,它会自动查找默认 bridge 网络的网关 IP,并将其写入容器的 /etc/hosts 文件中,比如:

172.17.0.1 host.docker.internal

这样,容器内的应用就可以通过 host.docker.internal 访问宿主机。


5. 适用场景与替代方案

适用场景 ✅

  • 容器中的应用需要访问宿主机上的服务(如本地数据库、API 服务)
  • 宿主机服务绑定在 0.0.0.0 或特定 IP 上
  • 需要在 Linux 上实现跨平台兼容的访问方式

替代方案 ❌

  • 使用 network_mode: host:虽然可以直接共享网络栈,但牺牲了容器网络隔离性,且所有端口都会暴露在宿主机上。
  • 使用自定义 bridge 网络 + 容器名访问:适用于容器间通信,但无法访问宿主机上非容器服务。

6. 如何测试该配置

我们可以用一个简单的测试流程验证容器是否能通过 host.docker.internal 成功访问宿主机。

6.1 启动宿主机上的 HTTP 服务

在宿主机上运行一个简单的 HTTP 服务(比如使用 Python):

$ python3 -m http.server 8000
Serving HTTP on 0.0.0.0 port 8000 ...

6.2 编写 Docker Compose 文件

创建如下 docker-compose.yml 文件:

version: '3.9'
services:
  curl:
    image: curlimages/curl
    command: ["curl", "http://host.docker.internal:8000"]
    extra_hosts:
      - "host.docker.internal:host-gateway"

6.3 执行测试

运行 docker-compose up 命令:

$ docker-compose up
Creating network "example_default" with the default driver
Creating example_curl_1 ... done
Attaching to example_curl_1
curl_1  |   % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
curl_1  |                                  Dload  Upload   Total   Spent    Left  Speed
100   358  100   358    0     0  85830      0 --:--:-- --:--:-- --:--:--  116k
curl_1  | <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
...
example_curl_1 exited with code 0

输出显示成功访问了宿主机上的 HTTP 服务,并返回了目录列表。其中:

  • host.docker.internal 成功解析为宿主机 IP
  • curl 成功请求并获取响应内容
  • 容器退出码为 0,表示无错误完成

7. 常见问题与排查

问题 1:host-gateway 不被识别

如果你看到类似错误:

invalid IP address in add-host: host-gateway

说明你的 Docker 版本低于 20.10。请升级到 20.10 或更高版本。

问题 2:无法访问宿主机服务

  • ✅ 确认服务监听地址是否为 0.0.0.0 而不是 127.0.0.1
  • ✅ 检查宿主机防火墙是否允许来自 Docker 的连接
  • ✅ 检查容器是否正确配置了 extra_hosts

8. 总结

通过 --add-host=host.docker.internal:host-gateway 我们可以在 Linux 上实现类似 macOS 和 Windows 的 host.docker.internal 功能,使得容器能够访问宿主机上的服务。

在 Docker Compose 中,使用 extra_hosts 字段即可实现相同效果,配置简单、可移植性强,是开发调试阶段非常实用的技巧。

无论你是连接本地数据库、调试本地 API,还是运行其他宿主机服务,这个配置都能帮助你轻松实现容器与宿主机之间的通信。


原始标题:The Equivalent of –add-host=host.docker.internal:host-gateway in Docker Compose