在多容器应用中,服务之间不应该依赖临时 IP。容器重建后 IP 可能变化,手工维护地址既脆弱又难协作。Docker 的内置 DNS 与自定义 bridge 网络,让容器可以通过服务名或容器名通信,例如 API 访问 redis:6379,而不是写死某个容器 IP。理解这套机制,可以显著减少本地开发、集成测试和 Compose 环境中的连接问题。
本文关注 Docker 单机环境下的服务名解析。如果你需要从更广的体系理解容器网络和服务发现,可继续阅读容器网络与Docker容器探索。

Docker 容器 DNS 的基本工作方式
在自定义 bridge 网络中,Docker 会为容器提供内置 DNS 服务。容器的 /etc/resolv.conf 通常指向 Docker 管理的本地解析地址,容器发起名称解析时,Docker 根据网络内的容器名、网络别名或 Compose 服务名返回对应 IP。
docker network create app-net
docker run -d --name redis --network app-net redis:7
docker run --rm --network app-net alpine getent hosts redis
这个能力只在同一 Docker 网络范围内生效。也就是说,api 和 redis 必须连接到同一个自定义 bridge 网络,api 才能解析 redis。网络既是通信边界,也是 DNS 可见性边界。
为什么不要依赖容器 IP
容器 IP 是运行时分配的,容器删除重建、网络重连或 Compose 项目重启后都可能变化。把 IP 写进配置文件,会让环境变得不可复制。服务名则由 Docker 根据当前网络状态动态解析,更适合本地多服务开发。
# 不推荐
REDIS_URL=redis://172.18.0.3:6379
# 推荐
REDIS_URL=redis://redis:6379
这里的 redis 可以是容器名,也可以是 Compose 文件中的服务名。应用只关心逻辑服务入口,具体 IP 由 Docker 网络负责维护。

Compose 网络中的服务名解析
Docker Compose 会为项目创建默认网络,并把同一 Compose 文件中的服务连接进去。服务名会自动注册为 DNS 名称。例如:
services:
api:
image: my-api:latest
environment:
REDIS_URL: redis://redis:6379
depends_on:
- redis
redis:
image: redis:7
在这个配置中,api 容器可以通过 redis 访问 Redis 服务。需要注意,depends_on 只表达启动顺序,不代表 redis 已经可用。若应用依赖数据库、缓存或消息队列,应使用健康检查、重试机制或等待脚本,而不是只依赖 DNS 解析成功。
services:
redis:
image: redis:7
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 3s
retries: 10
网络别名:让一个服务拥有多个名称
有些场景中,旧系统配置要求访问固定域名,或同一个容器需要在不同测试用例中使用不同名称。可以通过 network aliases 设置别名:
services:
mock-payment:
image: payment-mock:latest
networks:
app-net:
aliases:
- payment.internal
networks:
app-net:
driver: bridge
别名应谨慎使用。它能提高兼容性,也可能让环境命名变复杂。团队应约定服务名、别名和外部域名的边界,避免一个服务出现过多入口名称,导致排障时无法判断实际连接目标。
DNS 能解析不代表服务可用
服务名解析只解决“名字到 IP”的问题,不保证目标端口开放、应用已完成启动或协议正确。常见现象是 getent hosts redis 能返回 IP,但应用仍连接失败。这时应继续检查端口、监听地址和服务健康状态。
| 检查点 | 命令示例 | 说明 |
|---|---|---|
| — | — | — |
| 名称是否可解析 | getent hosts redis |
验证 Docker DNS 是否返回 IP |
| 端口是否可达 | nc -vz redis 6379 |
验证网络与目标端口 |
| 应用是否就绪 | docker logs redis |
检查服务启动过程 |
| 是否同一网络 | docker network inspect app-net |
确认容器连接关系 |

常见问题:默认 bridge、跨网络和主机名混淆
第一,默认 bridge 网络的服务名解析能力不如自定义 bridge 清晰,建议多容器应用统一使用自定义网络。第二,容器在不同网络中默认不可互相解析,除非同时连接到同一网络。第三,容器的 hostname、container name、Compose service name 和 network alias 并不完全等价,配置时应优先使用稳定的服务名。
docker network inspect app-net --format '{{json .Containers}}'
docker exec api cat /etc/resolv.conf
docker exec api getent hosts redis
如果需要让容器访问宿主机服务,还要区分“服务名解析”和“宿主机地址”。在 Docker Desktop 中常见 host.docker.internal,在 Linux 原生环境则可能需要额外配置网关地址或 host-gateway。不要把宿主机服务也伪装成普通容器服务名,否则环境迁移时容易出错。
与 Kubernetes 服务发现的差异
Docker Compose 服务名适合单机项目内通信,Kubernetes Service 则面向集群内服务发现、负载均衡和多副本后端。二者都避免应用写死 IP,但 Kubernetes 还涉及命名空间、ClusterIP、CoreDNS、EndpointSlice 和服务治理。理解 Docker 容器 DNS 是进入 Kubernetes 服务发现的前置知识,但不能把 Compose 的服务名机制直接等同于集群级服务注册。可延伸阅读Kubernetes实践与Kubernetes容器专题。
小结:服务名通信的核心是网络边界清晰
Docker 容器 DNS 的价值,是让多容器应用通过稳定名称通信,而不是依赖易变 IP。实践中应优先使用自定义 bridge 或 Compose 默认网络,用服务名表达依赖关系,用健康检查处理启动时序,用排障命令区分解析、连通和应用就绪。这样既能提升本地开发体验,也能为后续理解 Kubernetes 服务发现打好基础。
转载请注明出处:https://www.cloudnative-tech.com/p/7341/