在 Docker 环境下,最稳妥的做法是通过自定义网络让 Nginx 容器与后端服务容器互通,并在 Nginx 配置中使用 upstream 模块指向服务名。
先说结论:这种方案适合微服务或前后端分离架构,利用 Docker 内置 DNS 解析服务名,避免硬编码 IP。
- 适合:多容器协作、服务动态扩缩容的场景
- 先准备:规划好 Docker 网络和服务命名
- 验收:通过 curl 测试负载均衡效果及故障转移
命令速用版
如果你使用 Docker Compose,可以直接用以下配置模板快速搭建环境。注意 networks 部分确保所有服务在同一网络下。
version: '3'
services:
web1:
image: httpd
container_name: backend1
networks:
- app_net
web2:
image: httpd
container_name: backend2
networks:
- app_net
nginx:
image: nginx:latest
container_name: lb_nginx
ports:
- "8080:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
depends_on:
- web1
- web2
networks:
- app_net
networks:
app_net:
driver: bridge为什么会这样
Docker 容器之间默认是隔离的,但加入同一个自定义网络后,它们可以通过容器名或服务名互相访问。Docker 内置的 DNS 服务器会自动将服务名解析为容器 IP。Nginx 的 upstream 模块允许定义一组后端服务器,配合 proxy_pass 指令将请求分发出去。这种组合避免了手动维护 IP 列表,当后端容器重启 IP 变化时,配合正确的 resolver 配置,Docker DNS 会自动更新解析。
分步处理
第一步:创建自定义网络
如果不用 Compose,先手动创建网络,确保后续容器能互通。
docker network create app_net第二步:启动后端服务
启动两个简单的后端容器,加入刚才创建的网络。为了区分,可以修改默认首页内容。
docker run -d `--name` backend1 `--network` app_net httpd
docker run -d `--name` backend2 `--network` app_net httpd第三步:编写 Nginx 配置
创建 nginx.conf 文件,核心是 upstream 块指向容器名。注意:必须添加 resolver 以支持动态 DNS 解析,并配置健康检查参数。
events { worker_connections 1024; }
http {
# 指向 Docker 内置 DNS 服务器,设置缓存有效期
resolver 127.0.0.11 valid=30s;
upstream backend_servers {
# 添加 max_fails 和 fail_timeout 实现简单健康检查
server backend1:80 max_fails=3 fail_timeout=30s;
server backend2:80 max_fails=3 fail_timeout=30s;
}
server {
listen 80;
location / {
proxy_pass http://backend_servers;
proxy_set_header Host $host;
}
}
}第四步:启动 Nginx 容器
挂载配置文件并加入同一网络。
docker run -d `--name` lb_nginx `--network` app_net -p 8080:80 -v $(pwd)/nginx.conf:/etc/nginx/nginx.conf nginx:latest怎么验证是否生效
1. 检查连通性
进入 Nginx 容器,尝试 ping 后端服务名,确认 DNS 解析正常。
docker exec lb_nginx ping -c 3 backend12. 测试负载均衡
在宿主机多次请求 Nginx 暴露的端口,观察响应是否来自不同后端。如果后端页面有区分标识,可以看到变化。
curl http://localhost:8080
curl http://localhost:80803. 查看 Nginx 日志
检查访问日志,确认 upstream 地址是否有记录。
docker logs lb_nginx常见坑
1. 不要写 localhost
在 Nginx 配置中,proxy_pass 的目标不能写 localhost 或 127.0.0.1,因为那是 Nginx 容器自己。必须写 Docker 服务名或容器名。
2. DNS 缓存问题
Nginx 默认会缓存 DNS 解析结果。后端容器重启 IP 变更后,旧 IP 可能仍被使用导致请求失败。必须在配置中添加 resolver 指令指向 Docker 内置 DNS(127.0.0.11),并设置 valid 时间,例如 resolver 127.0.0.11 valid=30s;。这样 Nginx 会定期重新解析域名。
3. 网络模式混淆
如果使用了 host 模式,容器共享宿主机网络,不需要自定义网络,但会失去端口隔离优势。默认 bridge 模式最安全。
参考来源
- Nginx Documentation, "Module ngx_http_upstream_module", https://nginx.org/en/docs/http/ngx_http_upstream_module.html
- Docker Documentation, "Networking in Docker", https://docs.docker.com/network/