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
成功解析为宿主机 IPcurl
成功请求并获取响应内容- 容器退出码为 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,还是运行其他宿主机服务,这个配置都能帮助你轻松实现容器与宿主机之间的通信。