【Docker】docker-compose中的nginx为何突然访问不到服务了?

docker-compose中的nginx为何突然访问不到服务了?

我使用docker-compose的方式启动了一个Nginx、服务A、服务B,nginxconfig内部是填写的服务名,

在重建服务A之后,有时就会出现服务A无法访问的问题,此时重启一下nginx就可以访问到了。

场景复现准备

目录结构
text 复制代码
docker-repro/
├── docker-compose.yml
├── nginx/
│   └── nginx.conf
└── service-a/
    ├── Dockerfile
    └── app.py

1. 编写 docker-compose.yml

在根目录 docker-repro/docker-compose.yml 中写入:

yaml 复制代码
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
python 复制代码
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
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 中:

nginx 复制代码
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. 启动服务并验证启动成功

在项目根目录运行:

bash 复制代码
docker-compose up -d --build

待启动完成之后执行:

bash 复制代码
curl -i http://localhost:8080

问题复现

  • 先查看下目前service_a的IP
shell 复制代码
docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' service_a
销毁并重新创建 Service A
shell 复制代码
docker-compose stop service_a
docker-compose rm -f service_a
docker-compose up -d service_a

如果你是这样就开始实验了,那么你大概率会发现 IP 没变,服务正常访问,本次实验就没有找出原因。

因为 Docker 的网络默认会"填补空缺"------只要那个槽位(IP)在你重建前没有被别的容器占用,

它就会再分配给新的 service_a。

想要让它真正拿到一个不同的 IP,你需要"先占坑"。

正确复现的做法是:

shell 复制代码
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配置文件。

shell 复制代码
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 添加常用的请求头,方便后端服务识别请求来源
相关推荐
紫璨月4 分钟前
nginx反向代理的bug
运维·nginx·bug
没有名字的小羊5 分钟前
2.安装Docker
运维·docker·容器
xiezhr12 分钟前
50 个常用 Docker 命令
运维·docker·容器
就叫飞六吧9 天前
基于keepalived、vip实现高可用nginx (centos)
python·nginx·centos
退役小学生呀9 天前
三、kubectl使用详解
云原生·容器·kubernetes·k8s
小生云木9 天前
Linux离线编译安装nginx
linux·运维·nginx
API开发9 天前
苹果芯片macOS安装版Homebrew(亲测) ,一键安装node、python、vscode等,比绿色软件还干净、无污染
vscode·python·docker·nodejs·openssl·brew·homebrew
程序员小潘9 天前
Kubernetes多容器Pod实战
云原生·容器·kubernetes
进击的码码码码N10 天前
Docker 镜像加速
运维·docker·容器
Q_w774210 天前
基于 Docker 的服务部署探索(Day 2)
运维·docker·容器