Appearance
容器互联
容器互联是指多个容器之间建立网络通信。本节介绍容器间通信的各种方式和最佳实践。
通信方式概述
| 方式 | 说明 | 推荐程度 |
|---|---|---|
| 用户定义网络 | 通过容器名 DNS 解析 | ⭐⭐⭐ 推荐 |
| --link(已废弃) | 旧版连接方式 | ❌ 不推荐 |
| 端口映射 | 通过宿主机 IP 访问 | 仅需从外部访问时 |
| host 网络 | 共享宿主机网络 | 特殊高性能场景 |
方式一:用户定义网络(推荐)
同一用户定义网络中的容器,内置 DNS 自动解析容器名:
bash
# 创建网络
docker network create app-net
# 启动服务
docker run -d --name redis --network app-net redis:alpine
docker run -d --name api --network app-net myapi:latest
# api 容器通过名称访问 redis
# 在 api 应用中配置:redis://redis:6379Node.js 连接 Redis(通过容器名):
javascript
const redis = require('redis');
const client = redis.createClient({
url: 'redis://redis:6379' // "redis" 是容器名
});Python 连接 MySQL(通过容器名):
python
import pymysql
conn = pymysql.connect(
host='mysql', # "mysql" 是容器名
user='root',
password='secret',
database='mydb'
)方式二:--link(了解即可,已废弃)
旧版容器连接方式,现已不推荐使用:
bash
# 启动 MySQL
docker run -d --name mysql -e MYSQL_ROOT_PASSWORD=secret mysql:8.0
# 使用 --link 连接(会自动注入环境变量和 /etc/hosts)
docker run -d --name app --link mysql:db myapp
# 容器内可以通过 "db" 访问 MySQL(--link 的别名)为什么废弃:
--link单向连接,不支持用户定义网络的自动 DNS,且 Docker 官方已将其标记为遗留功能。
方式三:同一主机端口映射
通过宿主机 IP 和端口进行通信(性能稍低,但适用于跨网络场景):
bash
# 容器 A 暴露端口到宿主机
docker run -d --name service-a -p 8080:8080 service-a
# 容器 B 通过宿主机 IP 访问
# 注意:不能用 localhost,要用宿主机实际 IP 或 host.docker.internal
docker run -d --name service-b \
-e SERVICE_A_URL=http://192.168.1.100:8080 \
service-b在 Docker Desktop(macOS/Windows)中,可以使用 host.docker.internal 访问宿主机:
bash
docker run -d --name app \
-e DB_HOST=host.docker.internal \
myapp实战:前后端 + 数据库三层架构
bash
# 创建网络
docker network create webapp
# 1. 数据库层
docker run -d \
--name mysql \
--network webapp \
-e MYSQL_ROOT_PASSWORD=secret \
-e MYSQL_DATABASE=myapp \
-v mysql-data:/var/lib/mysql \
mysql:8.0
# 2. 等待 MySQL 启动(实际可以在应用中用重试逻辑)
sleep 30
# 3. 后端 API 层
docker run -d \
--name api \
--network webapp \
-e DB_HOST=mysql \
-e DB_PORT=3306 \
-e DB_NAME=myapp \
-e DB_USER=root \
-e DB_PASS=secret \
myapi:latest
# 4. 前端 + 反向代理
docker run -d \
--name nginx \
--network webapp \
-p 80:80 \
-v ./nginx.conf:/etc/nginx/conf.d/default.conf:ro \
nginx:alpineNginx 配置:
nginx
upstream api {
server api:3000;
}
server {
listen 80;
# 前端静态文件
location / {
root /usr/share/nginx/html;
try_files $uri $uri/ /index.html;
}
# 转发 API 请求(通过容器名 "api" 访问)
location /api {
proxy_pass http://api;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}实战:微服务间通信
bash
# 创建微服务网络
docker network create microservices
# 用户服务
docker run -d --name user-service \
--network microservices \
user-service:latest
# 订单服务(依赖用户服务)
docker run -d --name order-service \
--network microservices \
-e USER_SERVICE_URL=http://user-service:8080 \
order-service:latest
# API 网关(对外暴露)
docker run -d --name api-gateway \
--network microservices \
-p 80:80 \
-e USER_SERVICE=http://user-service:8080 \
-e ORDER_SERVICE=http://order-service:8081 \
api-gateway:latest验证容器间通信
bash
# 进入容器,测试连通性
docker exec -it api sh
# 测试 DNS 解析
nslookup mysql
# Server: 127.0.0.11(Docker 内置 DNS)
# Address: 127.0.0.11:53
# Name: mysql
# Address: 172.20.0.2
# 测试连通性
ping mysql
curl http://api:3000/health
nc -zv mysql 3306 # 测试 TCP 端口连通容器等待依赖就绪
容器互联时,经常遇到依赖服务(如数据库)还没完全启动的问题:
方式一:应用内重试
javascript
// Node.js 数据库连接重试
const connectWithRetry = () => {
mongoose.connect(MONGO_URI).then(() => {
console.log('MongoDB connected');
}).catch(err => {
console.log('MongoDB connection failed, retrying in 5s...');
setTimeout(connectWithRetry, 5000);
});
};
connectWithRetry();方式二:wait-for-it 脚本
dockerfile
# 下载 wait-for-it.sh
ADD https://raw.githubusercontent.com/vishnubob/wait-for-it/master/wait-for-it.sh /wait-for-it.sh
RUN chmod +x /wait-for-it.sh
CMD ["/wait-for-it.sh", "mysql:3306", "--", "node", "app.js"]方式三:Docker Compose 的 depends_on + healthcheck
参考 Docker Compose 基础 章节。
总结
容器互联的最佳实践:
- 使用用户定义网络,不要使用默认 bridge 网络
- 通过容器名访问,不要依赖 IP 地址(IP 可能变化)
- 应用层实现重试,处理服务启动顺序问题
- 合理分层:数据层、服务层、接入层使用不同网络
- 最小权限:不需要对外暴露的服务不映射端口