Docker: 容器间通信模式
最后更新:2026-08-26
容器不只是在自己的网络里运行——它们需要互相发现、互相通信。理解容器间通信模式是构建微服务架构的基础。
1. 你将学到
- 容器间通信的三种方式
- 容器别名与 DNS 高级用法
- 网络故障排查方法
- 端口暴露策略
- 安全组隔离设计
2. 一个微服务开发者的真实故事
(1) 痛点:三个微服务网络不通
Charlie 部署了三个微服务容器:API Gateway、用户服务和订单服务。API Gateway 能访问用户服务,但请求总是超时。排查了 2 小时后发现:API Gateway 暴露了端口但防火墙没开,用户服务用了错误的容器名,订单服务在不同的网络里。
(2) 系统化网络排查的解法
Bob 教他一套网络排查流程:容器名 → DNS → 网络 → 端口。
BASH
# Step 1: Verify DNS resolution
docker exec api nslookup user-service
# Step 2: Test network connectivity
docker exec api ping -c 2 user-service
# Step 3: Test port connectivity
docker exec api nc -zv user-service 8080
(3) 收益:网络排查从 2 小时→10 分钟
有了系统化的排查流程,Charlie 的网络问题排查时间从 2 小时降到 10 分钟。
3. 容器间通信三种方式
(1) 方式对比
graph TB
subgraph Method1["① --link (Legacy, Deprecated)"]
L1["Container A"] -->|"env vars + /etc/hosts"| L2["Container B"]
end
subgraph Method2["② Custom Network + DNS (Recommended)"]
D1["Container A"] -->|"DNS auto-resolve"| D2["Container B"]
end
subgraph Method3["③ Exposed Port + Host IP"]
E1["Container A"] -->|"host:port"| E2["Container B<br/>(via host bridge)"]
end
| 方式 | 原理 | DNS | 状态 | 推荐度 |
|---|---|---|---|---|
--link |
环境变量 + hosts 注入 | ❌ | ❌ 废弃 | ⭐ |
| 自定义网络 DNS | Docker 内置 DNS | ✅ | ✅ 推荐 | ⭐⭐⭐ |
| 主机端口转发 | 容器→主机→容器 | ❌ | 可用 | ⭐⭐ |
4. --link(已废弃)
▶ 示例:了解 --link 的工作方式(难度⭐)
BASH
# Legacy link: B can reach A by hostname
docker run -d --name db postgres:15-alpine
docker run -d --name web --link db:database nginx:alpine
# web can use "database" as hostname (via /etc/hosts)
docker exec web cat /etc/hosts
⚠️ 注意:
--link 已废弃,仅用于理解历史。它只支持单向连接(web→db,不能 db→web),且容器重启后 IP 变化时 hosts 文件不更新。始终用自定义网络替代。**
5. 自定义网络 DNS 详解
(1) DNS 解析流程
graph LR
A["Container A<br/>ping db"] --> B["Docker DNS<br/>127.0.0.11"]
B -->|db → 172.18.0.2| C["Container B<br/>172.18.0.2"]
▶ 示例:--network-alias 设置别名(难度⭐⭐)
BASH
# Start container with network alias
docker run -d --name my-postgres \
--network app-net \
--network-alias=database \
--network-alias=db \
postgres:15-alpine
# Both names resolve to the same container
docker exec api ping -c 1 database # resolves to my-postgres
docker exec api ping -c 1 db # same IP
(1) 容器名 vs 网络别名
| 方式 | 定义 | 作用域 | 优先级 |
|---|---|---|---|
--name |
容器名 | 所有网络 | 高 |
--network-alias |
网络别名 | 仅指定网络 | 中 |
| Docker Compose service 名 | 服务名 | Compose 网络 | 高 |
💡 提示:
--network-alias 允许同一容器在不同网络中有不同名字。例如数据库在 backend-net 中叫 primary-db,在 admin-net 中叫 database。
6. 端口暴露策略
(1) --expose vs -p vs -P
| 参数 | 作用 | 外部可访问 | 适用场景 |
|---|---|---|---|
--expose 3306 |
声明端口(文档) | ❌ 不映射 | 容器间通信 |
-p 3306:3306 |
映射到主机 | ✅ 外部可访问 | 对外服务 |
-P |
随机映射 EXPOSE 端口 | ✅ 随机端口 | 开发调试 |
▶ 示例:docker port 查看映射(难度⭐)
BASH
# Check port mappings for a container
docker port web
# Output example
80/tcp -> 0.0.0.0:8080
7. 网络故障排查
(1) 排查决策树
graph TB
START["Container A<br/>cannot reach Container B"] --> DNS{"DNS 解析?"}
DNS -->|失败| FIX1["同一自定义网络?<br/>用 --name 或 --network-alias"]
DNS -->|成功| PING{"Ping 通?"}
PING -->|失败| FIX2["同一网络?<br/>docker network connect"]
PING -->|成功| PORT{"端口通?<br/>nc -zv B port"]
PORT -->|失败| FIX3["端口暴露?<br/>B 是否监听正确端口?"]
PORT -->|成功| APP{"应用协议?<br/>curl/wget 测试"]
APP -->|失败| FIX4["检查应用配置<br/>环境变量/URL"]
APP -->|成功| OK["✅ 通信正常"]
▶ 示例:容器内网络排查命令(难度⭐⭐)
BASH
# Step 1: Check DNS resolution
docker exec api nslookup db
docker exec api getent hosts db
# Step 2: Test basic connectivity
docker exec api ping -c 2 db
# Step 3: Test specific port
docker exec api nc -zv db 5432
# or with wget
docker exec api wget -qO- http://api:8080/health
# Step 4: Check container's network interfaces
docker exec api ip addr
# Step 5: Check routing table
docker exec api ip route
▶ 示例:docker network connect 动态加入网络(难度⭐⭐)
BASH
# Connect a running container to another network
docker network connect backend-net web
# Disconnect from a network
docker network disconnect frontend-net web
# Verify container's networks
docker inspect web --format='{{json .NetworkSettings.Networks}}' | python3 -m json.tool
8. 安全组隔离设计
(1) 三层隔离模型
| 网络层 | 容器 | 可访问 | 对外端口 |
|---|---|---|---|
| Frontend Net | Nginx/CDN | Backend Net only | 80, 443 |
| Backend Net | API/Worker | DB Net only | 无(通过 Nginx 代理) |
| DB Net | PostgreSQL/Redis | 无外部访问 | 无 |
🔒 安全: 数据库只在 DB Net 中,Frontend 容器无法直接访问数据库——必须经过 Backend API。这是"最小权限网络"原则。
9. 完整示例:微服务网络配置与排查
BASH
# ============================================
# Complete walkthrough: Microservice networking
# Covers: networks, aliases, isolation, debugging
# ============================================
# 1. Create isolated networks
docker network create frontend-net
docker network create backend-net
docker network create db-net
# 2. Start database (db-net only)
docker run -d --name postgres \
--network db-net \
--network-alias=database \
--network-alias=primary-db \
-e POSTGRES_PASSWORD=secret \
-e POSTGRES_DB=myapp \
postgres:15-alpine
# 3. Start API server (backend-net + db-net)
docker run -d --name api \
--network backend-net \
--network-alias=api-server \
-e DATABASE_URL="postgresql://postgres:secret@primary-db:5432/myapp" \
myapp-api:1.0
docker network connect db-net api
# 4. Start Nginx (frontend-net + backend-net)
docker run -d --name nginx \
--network frontend-net \
--network-alias=gateway \
-p 8080:80 \
nginx:1.25-alpine
docker network connect backend-net nginx
# 5. Debug: verify connectivity
echo "=== DNS Resolution ==="
docker exec nginx nslookup api-server
docker exec api nslookup primary-db
echo "=== Port Connectivity ==="
docker exec nginx nc -zv api-server 5000
docker exec api nc -zv primary-db 5432
echo "=== Isolation Check ==="
docker exec nginx sh -c "timeout 2 nc -zv primary-db 5432 2>&1 || echo 'BLOCKED: nginx cannot reach db directly (expected)'"
# 6. Clean up
docker stop postgres api nginx
docker rm postgres api nginx
docker network rm frontend-net backend-net db-net
❓ 常见问题
Q --link 已经废弃了吗?
A 是的。Docker 官方明确废弃了 --link,推荐用自定义网络 + DNS。--link 的问题:单向连接、重启后 IP 不更新、不支持 Compose。自定义网络解决了所有这些问题。
Q 容器名在不同网络间唯一吗?
A 容器名在 Docker 主机全局唯一。但 DNS 解析是网络级别的——只有同一网络中的容器才能通过容器名互相解析。跨网络用
docker network connect 加入后才能解析。Q 怎么让容器只能被特定容器访问?
A 用网络隔离。把被保护的服务(如数据库)放在专用网络中,只有需要访问的容器才加入该网络。例如数据库在 db-net,只有 API 加入 db-net,Nginx 无法直接访问数据库。
Q 容器 DNS 超时怎么办?
A DNS 超时通常因为:① 容器不在同一网络(最常见);② 容器名拼错;③ DNS 缓存问题(重启容器解决)。排查:
docker exec <container> nslookup <target> 看解析结果。Q 怎么看到容器的实际 IP?
A
docker inspect <container> --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'。或进入容器执行 ip addr / hostname -I。但记住:IP 可能变,生产代码用容器名而不是 IP。📖 小节
- 容器间通信推荐自定义网络 + DNS,废弃 --link
- 自定义 bridge 网络:容器名自动注册 DNS,同网络容器按名通信
--network-alias给容器起别名,同一容器在不同网络可有不同名字- 端口暴露:
--expose仅文档声明,-p映射到主机,-P随机映射 - 网络排查四步:DNS 解析 → Ping → 端口测试 → 应用协议测试
- 三层隔离:Frontend → Backend → Database,数据库不暴露到前端网络
📝 作业
- 基础题(难度⭐):创建两个自定义网络,各启动一个容器,验证同网络容器名 DNS 解析正常。
- 进阶题(难度⭐⭐):验证跨网络容器无法 ping 通,然后用
docker network connect连通,再断开。 - 挑战题(难度⭐⭐⭐):用
docker exec+nc/wget排查一个容器无法访问另一个容器服务的根因,写出排查步骤和结论。