docker-compose中的nginx为何突然访问不到服务了?
我使用docker-compose的方式启动了一个Nginx、服务A、服务B,nginxconfig内部是填写的服务名,
在重建服务A之后,有时就会出现服务A无法访问的问题,此时重启一下nginx就可以访问到了。
场景复现准备
目录结构
docker-repro/
├── docker-compose.yml
├── nginx/
│ └── nginx.conf
└── service-a/
├── Dockerfile
└── app.py
1. 编写 docker-compose.yml
在根目录 docker-repro/docker-compose.yml
中写入:
version: '3.8'
services:
nginx:
image: nginx:latest
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
ports:
- "8080:80"
depends_on:
- service_a
service_a:
build: ./service-a
container_name: service_a
service_b:
image: busybox
command: ["sh", "-c", "while true; do echo B; sleep 5; done"]
container_name: service_b
service_a
:一个简单的 Python Flask 服务,返回 “Hello A”service_b
:一个不停输出字符 B 的占位容器nginx
:反向代理到service_a
,端口转发到本机 8080
2. 编写 Service A
在 docker-repro/service-a/
下:
app.py
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello():
return 'Hello A\n'
if __name__ == '__main__':
# 监听 5000 端口
app.run(host='0.0.0.0', port=5000)
Dockerfile
FROM dockerpull.cn/library/python:3.9-slim
WORKDIR /app
COPY app.py /app/
RUN pip install flask -i https://mirrors.aliyun.com/pypi/simple/
EXPOSE 5000
CMD ["python", "app.py"]
3. 编写 Nginx 配置
在 docker-repro/nginx/nginx.conf
中:
worker_processes 1;
events {
worker_connections 1024;
}
http {
upstream service_a {
server service_a:5000;
}
server {
listen 80;
location / {
proxy_pass http://service_a;
}
}
}
注意:这里直接在
upstream
里写了service_a:5000
,Nginx 启动时只解析一次。
4. 启动服务并验证启动成功
在项目根目录运行:
docker-compose up -d --build
待启动完成之后执行:
curl -i http://localhost:8080
问题复现
- 先查看下目前
service_a
的IP
docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' service_a
销毁并重新创建 Service A
docker-compose stop service_a
docker-compose rm -f service_a
docker-compose up -d service_a
如果你是这样就开始实验了,那么你大概率会发现 IP 没变,服务正常访问,本次实验就没有找出原因。
因为 Docker 的网络默认会“填补空缺”——只要那个槽位(IP)在你重建前没有被别的容器占用,
它就会再分配给新的 service_a。
想要让它真正拿到一个不同的 IP,你需要“先占坑”。
正确复现的做法是:
docker-compose stop service_a
docker-compose rm -f service_a
# 使用一个临时的容器占坑
docker run -d --name dummy --network docker-repro_default busybox sleep 3600
docker-compose up -d service_a
这样你再次执行查看ip命令就能发现,IP变了,服务访问不到了,问题复现出来了。
总结以下问题的核心:
Nginx 默认只解析服务名一次,DNS 结果缓存不变。
Docker 中服务 A 重建后 IP 会变,但 Nginx 仍然访问旧 IP,导致连接失败。
解决方案
解决方案有很多,在这里使用较为简单的一种方式,仅需要修改nginx配置文件。
worker_processes 1;
events {
worker_connections 1024;
}
http {
resolver 127.0.0.11 valid=5s;
server {
listen 80;
location / {
set $backend "service_a:5000";
proxy_pass http://$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;
}
}
}
配置项 | 作用 |
---|---|
resolver 127.0.0.11 |
使用 Docker 内置 DNS 进行域名解析 |
valid=5s |
DNS 记录最多缓存 5 秒,之后会重新解析 |
set $backend |
动态构造代理地址,触发每次请求前的 DNS 查询 |
proxy_pass http://$backend |
代理到动态地址 |
proxy_set_header |
添加常用的请求头,方便后端服务识别请求来源 |