使用NGINX与Docker原生DNS实现API负载均衡的轮询失效问题
解决NGINX + Docker DNS负载均衡仅转发到单服务器的问题
我来帮你搞定这个头疼的问题——这种情况我碰过好多次,核心原因其实是NGINX默认的DNS缓存机制在搞鬼!咱们一步步拆解解决:
为什么会只转发到单服务器?
Docker的原生DNS(比如你配置的servers.api别名)确实会返回所有在线API容器的IP,但NGINX默认是启动时一次性解析域名,之后就把结果缓存起来再也不更新。哪怕后面有新的API容器上线,或者现有容器重启换了IP,NGINX都不会主动去重新查询,自然只会死死盯着第一次解析到的那台服务器。
解决方案:让NGINX动态解析Docker DNS
1. 修改NGINX配置,强制动态解析
把你的nginx.conf改成下面这样,重点是添加解析器配置和用变量存储后端地址:
worker_processes 2; events { worker_connections 1024; } http { sendfile on; # 指定Docker内置DNS服务器,30秒刷新一次解析结果 resolver 127.0.0.11 valid=30s; resolver_timeout 5s; # 可选:添加日志记录后端IP,方便验证负载均衡 log_format proxy_log '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$upstream_addr"'; access_log /var/log/nginx/access.log proxy_log; server { listen 8080; location / { # 用变量存后端域名,让NGINX每次请求都重新解析 set $api_backend servers.api; proxy_pass http://$api_backend; # 常规代理配置,保证请求头正确传递 proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } } }
这里的关键细节:
127.0.0.11是Docker固定内置的DNS服务器地址,所有容器都能直接访问valid=30s告诉NGINX每30秒重新查询一次域名,确保能获取到最新的API容器IP列表- 用
set $api_backend变量存储后端域名,NGINX遇到变量形式的proxy_pass时,就不会在启动时缓存解析结果,而是每次请求都重新解析,这样就能拿到所有IP实现轮询
2. 优化Docker Compose配置(更规范的做法)
现在Docker Compose已经不推荐用links了,换成自定义网络更稳定,服务之间直接用服务名就能互通:
version: '3.8' networks: api-internal: driver: bridge services: proxy: restart: always build: ./src/nginx/. ports: - "8080:8080" networks: - api-internal # 去掉旧的links配置,同一网络下直接用api服务名访问 api: # 你的API服务镜像/构建配置 networks: - api-internal # 直接指定要启动的API实例数量 deploy: replicas: 3
这样启动后,proxy服务可以直接通过api这个服务名访问所有3个API实例,Docker DNS会自动返回所有运行中的容器IP。
3. 验证负载均衡是否生效
启动所有服务后,多执行几次curl http://localhost:8080,然后:
- 查看API容器的日志,应该能看到请求被分发到不同的实例上
- 查看NGINX的
access.log,里面的$upstream_addr字段会显示每次转发到的后端IP,确认轮询是否正常工作
内容的提问来源于stack exchange,提问作者Raj
相关产品推荐
相关产品推荐

