如何在 Docker 容器中配置 Nginx 实现内部服务负载均衡

文章导读
在 Docker 环境下,最稳妥的做法是通过自定义网络让 Nginx 容器与后端服务容器互通,并在 Nginx 配置中使用 upstream 模块指向服务名。
📋 目录
  1. 命令速用版
  2. 为什么会这样
  3. 分步处理
  4. 怎么验证是否生效
  5. 常见坑
  6. 参考来源
A A

在 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 容器中配置 Nginx 实现内部服务负载均衡
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 backend1

2. 测试负载均衡
在宿主机多次请求 Nginx 暴露的端口,观察响应是否来自不同后端。如果后端页面有区分标识,可以看到变化。

如何在 Docker 容器中配置 Nginx 实现内部服务负载均衡
curl http://localhost:8080
curl http://localhost:8080

3. 查看 Nginx 日志
检查访问日志,确认 upstream 地址是否有记录。

docker logs lb_nginx

常见坑

1. 不要写 localhost
在 Nginx 配置中,proxy_pass 的目标不能写 localhost127.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/