云服务器运维与项目部署
13.6 Docker容器网络
Tip
由于容器是隔离的(像一个个独立的小房间),默认情况下它们之间是听不到对方说话的。你需要给它们拉电话线,这就是Docker Network。
1. 为什么要学这个?
你的 Node.js 应用要连 MySQL。
你在代码里写 localhost:3306。
报错 Connection Refused。
因为 localhost 在容器里指的是容器自己,而不是宿主机,也不是另一个容器。
不懂 Docker Network,你的微服务就是一座座孤岛。
2. 核心概念:Docker Compose
虽然可以用 docker network create 手动配网,但太麻烦。
我们通常使用 Docker Compose 来编排多个容器。它会自动创建一个局域网,让容器可以通过服务名互相访问。
2.1 DNS Magic
在 Docker Compose 网络里:
- Service Name (
db) 会自动解析成 MySQL 容器的 IP。 - 在 Node.js 代码里,配置
host: 'db'就能连上。
3. 解决方案 (HOW)
3.1 编写 docker-compose.yml
在项目根目录:
version: '3'
services:
# 服务 1: Web 应用
web:
build: .
ports:
- "3000:3000"
environment:
# 注意这里 host 写的是 db,不是 localhost
- DATABASE_URL=mysql://user:pass@db:3306/mydb
depends_on:
- db
# 服务 2: 数据库
db:
image: mysql:8.0
environment:
- MYSQL_ROOT_PASSWORD=pass
- MYSQL_DATABASE=mydb
volumes:
- ./data:/var/lib/mysql # 数据持久化
3.2 运行
# 一键启动所有服务
docker-compose up -d
# 查看状态
docker-compose ps
3.3 网络拓扑图
graph TD
User["外部用户"] -->|"Step 1: port 3000"| WebContainer["Web 容器"]
subgraph DockerNet ["Docker Network (内部局域网)"]
WebContainer -->|"Step 2: host='db'"| DBContainer["MySQL 容器"]
end
WebContainer --x|"Step 3: localhost 不通"| DBContainer
style WebContainer fill:#e1bee7,stroke:#8e24aa
style DBContainer fill:#c8e6c9,stroke:#2e7d32
4. 避坑指南
| ❌ 不要这样做 | ✅ 应该这样做 | 为什么 |
|---|---|---|
| 使用 IP 地址 | 使用 Service Name | 容器重启后内部 IP (172.x.x.x) 可能会变,但 Service Name (db) 永远不变。 |
| 依赖启动顺序 | 实现重试机制 | depends_on 只管启动顺序,不管数据库是否 Ready。代码里最好加重试逻辑 (wait-for-it)。 |
| Localhost | 理解 Localhost | 99% 的新手坑:在容器里用 localhost 连别的服务。Localhost inside container = Container itself。 |
5. 真实案例
Story
2019年,死板的 IP
某开发者查到 Docker 分配给数据库的 IP 是 172.18.0.2。
于是他在代码里写死了这个 IP。
两个月后,服务器重启,Docker 容器启动顺序变了,Redis 先启动拿到了 172.18.0.2,数据库变成了 172.18.0.3。
本来应该写进数据库的交易记录,全被写到了 Redis 里(或者直接连接失败)。
Vibe 心法:在容器的宇宙里,IP 是流动的沙尘。永远不要在代码里固化具体的容器 IP,而要利用 Docker Compose 的 DNS 服务名发现机制。信任服务名,就是信任系统解耦的未来。
6. 本章小结
- Compose: 是管理多容器应用的最佳实践。
- Service Name: 是容器间的通关密语。
- Localhost: 在容器世界里,它最孤独。