【Docker】Docker-Compose内置DNS负载均衡失效问题

Docker Compose实现负载均衡

还是对前面的例子docker-compose.yml稍微修改:

shell 复制代码
version: "3.8"

services:
  flask-demo:
    build:
        context: .
        dockerfile: Dockerfile
    image: flask-demo:latest
    environment:
      - REDIS_HOST=redis-server
      - REDIS_PASS=${REDIS_PASS}
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:5000"]
      interval: 30s
      timeout: 3s
      retries: 3
      start_period: 40s
    depends_on:
      - redis-server
    deploy:
      replicas: 3
    networks:
      - backend
      - frontend

  redis-server:
    image: redis:latest
    command: redis-server --requirepass ${REDIS_PASS}
    networks:
      - backend
  nginx:
    image: nginx:stable-alpine
    ports:
      - 8000:80
    depends_on:
      flask-demo:
        condition: service_healthy
    volumes:
      - ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
      - ./log/nginx:/var/log/nginx
    networks:
      - frontend

networks:
  backend:
  frontend:

主要是修改flask-demo启动3个容器。

nginx.conf文件的内容如下:

shell 复制代码
server {
  listen  80 default_server;
  location / {
    proxy_pass http://flask-demo:5000;
  }
}

启动服务:

shell 复制代码
$ docker-compose up -d
Creating network "app8_backend" with the default driver
Creating network "app8_frontend" with the default driver
Creating app8_redis-server_1 ... done
Creating app8_flask-demo_1   ... done
Creating app8_flask-demo_2   ... done
Creating app8_flask-demo_3   ... done
Creating app8_nginx_1        ... done

$ docker-compose ps
       Name                      Command                  State                      Ports
----------------------------------------------------------------------------------------------------------
app8_flask-demo_1     flask run -h 0.0.0.0             Up (healthy)   5000/tcp
app8_flask-demo_2     flask run -h 0.0.0.0             Up (healthy)   5000/tcp
app8_flask-demo_3     flask run -h 0.0.0.0             Up (healthy)   5000/tcp
app8_nginx_1          /docker-entrypoint.sh ngin ...   Up             0.0.0.0:8000->80/tcp,:::8000->80/tcp
app8_redis-server_1   docker-entrypoint.sh redis ...   Up             6379/tcp

访问服务:

shell 复制代码
$ curl localhost:8000
Hello Container World! I have been seen 4 times and my hostname is 448b5d70d3d8.

$ curl localhost:8000
Hello Container World! I have been seen 5 times and my hostname is 77b2a2314533.

$ curl localhost:8000
Hello Container World! I have been seen 9 times and my hostname is 4eee9c8d54f1.

从运行结果可以发现我们可以根据service的名称访问容器,Docker会使用内置的DNS服务器将service的名称解析成IP,如果service对应的容器有多个,nginx会进行负载均衡。

Docker带有一个内置的DNS服务器。默认情况下,可以通过127.0.0.11:53访问服务器。这个DNS的IP可以通过进入容器内部查看/etc/resolv.conf获得:

shell 复制代码
$ docker-compose exec nginx cat /etc/resolv.conf
nameserver 127.0.0.11
options ndots:0

负载均衡的实现

在上面的例子中我们将flask-demo容器的个数提高到6个:

shell 复制代码
$ docker-compose up -d --scale flask-demo=6
app8_redis-server_1 is up-to-date
Creating app8_flask-demo_4 ... done
Creating app8_flask-demo_5 ... done
Creating app8_flask-demo_6 ... done
app8_nginx_1 is up-to-date

$ docker-compose ps
       Name                      Command                  State                      Ports
----------------------------------------------------------------------------------------------------------
app8_flask-demo_1     flask run -h 0.0.0.0             Up (healthy)   5000/tcp
app8_flask-demo_2     flask run -h 0.0.0.0             Up (healthy)   5000/tcp
app8_flask-demo_3     flask run -h 0.0.0.0             Up (healthy)   5000/tcp
app8_flask-demo_4     flask run -h 0.0.0.0             Up (healthy)   5000/tcp
app8_flask-demo_5     flask run -h 0.0.0.0             Up (healthy)   5000/tcp
app8_flask-demo_6     flask run -h 0.0.0.0             Up (healthy)   5000/tcp
app8_nginx_1          /docker-entrypoint.sh ngin ...   Up             0.0.0.0:8000->80/tcp,:::8000->80/tcp
app8_redis-server_1   docker-entrypoint.sh redis ...   Up             6379/tcp

$ docker container ps
CONTAINER ID   IMAGE                 COMMAND                  CREATED          STATUS                    PORTS
 NAMES
6e1494379165   flask-demo:latest     "flask run -h 0.0.0.0"   53 seconds ago   Up 51 seconds (healthy)   5000/tcp
 app8_flask-demo_6
62733bdccdb8   flask-demo:latest     "flask run -h 0.0.0.0"   53 seconds ago   Up 51 seconds (healthy)   5000/tcp
 app8_flask-demo_5
77e74622fa4e   flask-demo:latest     "flask run -h 0.0.0.0"   53 seconds ago   Up 51 seconds (healthy)   5000/tcp
 app8_flask-demo_4
b67132189d90   nginx:stable-alpine   "/docker-entrypoint...."   2 minutes ago    Up 2 minutes              0.0.0.0:8000->80/tcp, :::8000->80/tcp   app8_nginx_1
448b5d70d3d8   flask-demo:latest     "flask run -h 0.0.0.0"   2 minutes ago    Up 2 minutes (healthy)    5000/tcp
 app8_flask-demo_1
4eee9c8d54f1   flask-demo:latest     "flask run -h 0.0.0.0"   2 minutes ago    Up 2 minutes (healthy)    5000/tcp
 app8_flask-demo_2
77b2a2314533   flask-demo:latest     "flask run -h 0.0.0.0"   2 minutes ago    Up 2 minutes (healthy)    5000/tcp
 app8_flask-demo_3
215beaad114a   redis:latest          "docker-entrypoint.s..."   2 minutes ago    Up 2 minutes              6379/tcp
 app8_redis-server_1

再次访问我们的服务:

shell 复制代码
$ curl localhost:8000
Hello Container World! I have been seen 28 times and my hostname is 448b5d70d3d8.

$ curl localhost:8000
Hello Container World! I have been seen 29 times and my hostname is 77b2a2314533.

$ curl localhost:8000
Hello Container World! I have been seen 30 times and my hostname is 4eee9c8d54f1.

$ curl localhost:8000
Hello Container World! I have been seen 31 times and my hostname is 448b5d70d3d8.

$ curl localhost:8000
Hello Container World! I have been seen 32 times and my hostname is 77b2a2314533.

$ curl localhost:8000
Hello Container World! I have been seen 33 times and my hostname is 4eee9c8d54f1.

连续访问6次之后发现请求并没有转发到我们新启动的3个容器中,这是为什么呢?

负载均衡失效原因分析

初步怀疑是DNS解析失效,我们进入nginx容器查看DNS在解析flask-demo时解析出哪些IP:

shell 复制代码
$ docker-compose exec nginx sh
/ # apk update
fetch https://dl-cdn.alpinelinux.org/alpine/v3.14/main/x86_64/APKINDEX.tar.gz
fetch https://dl-cdn.alpinelinux.org/alpine/v3.14/community/x86_64/APKINDEX.tar.gz
v3.14.10-47-g7553b19fe26 [https://dl-cdn.alpinelinux.org/alpine/v3.14/main]
v3.14.10-42-gd8ce7b89082 [https://dl-cdn.alpinelinux.org/alpine/v3.14/community]
OK: 14983 distinct packages available

/ # apk add bind-tools
(1/11) Installing fstrm (0.6.1-r0)
(2/11) Installing krb5-conf (1.0-r2)
(3/11) Installing libcom_err (1.46.2-r1)
(4/11) Installing keyutils-libs (1.6.3-r0)
(5/11) Installing libverto (0.3.2-r0)
(6/11) Installing krb5-libs (1.18.5-r0)
(7/11) Installing json-c (0.15-r1)
(8/11) Installing protobuf-c (1.3.3-r6)
(9/11) Installing libuv (1.41.0-r0)
(10/11) Installing bind-libs (9.16.39-r0)
(11/11) Installing bind-tools (9.16.39-r0)
Executing busybox-1.33.1-r6.trigger
OK: 31 MiB in 53 packages
/ # dig flask-demo

; <<>> DiG 9.16.39 <<>> flask-demo
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 55582
;; flags: qr rd ra; QUERY: 1, ANSWER: 6, AUTHORITY: 0, ADDITIONAL: 0

;; QUESTION SECTION:
;flask-demo.                    IN      A

;; ANSWER SECTION:
flask-demo.             600     IN      A       192.168.96.7
flask-demo.             600     IN      A       192.168.96.2
flask-demo.             600     IN      A       192.168.96.3
flask-demo.             600     IN      A       192.168.96.4
flask-demo.             600     IN      A       192.168.96.8
flask-demo.             600     IN      A       192.168.96.6

;; Query time: 0 msec
;; SERVER: 127.0.0.11#53(127.0.0.11)
;; WHEN: Tue Oct 10 08:45:53 UTC 2023
;; MSG SIZE  rcvd: 184

发现DNS解析出了6个IP,然后我们在nginx容器中访问flask-demo

shell 复制代码
/ # curl flask-demo:5000
Hello Container World! I have been seen 281 times and my hostname is 77e74622fa4e.

/ # curl flask-demo:5000
Hello Container World! I have been seen 282 times and my hostname is 4eee9c8d54f1.

/ # curl flask-demo:5000
Hello Container World! I have been seen 283 times and my hostname is 62733bdccdb8.

/ # curl flask-demo:5000
Hello Container World! I have been seen 284 times and my hostname is 448b5d70d3d8.

/ # curl flask-demo:5000
Hello Container World! I have been seen 285 times and my hostname is 6e1494379165.

/ # curl flask-demo:5000
Hello Container World! I have been seen 289 times and my hostname is 77b2a2314533.

在nginx容器内访问就能访问到6个节点,那就说明nginx容器中DNS的解析没有问题,问题出在Nginx服务上。

Nginx在启动的时候会将代理服务器域名解析的ip地址缓存起来,后续不会再更新这个缓存,除非重启。

我们可以给nginx的缓存设置一个失效事件来解决这个问题,nginx.conf配置修改如下:

shell 复制代码
server {
  listen  80 default_server;
  location / {
    resolver 127.0.0.11 valid=1s; #设置dns服务器,缓存时间改为1s
    set $backend http://web:5000
    proxy_pass $backend;
  }
}

然后再让nginx重新加载配置,再访问服务,发现已经生效了:

shell 复制代码
$ docker-compose exec nginx nginx -s reload

$ curl localhost:8000
Hello Container World! I have been seen 414 times and my hostname is 77b2a2314533.

$ curl localhost:8000
Hello Container World! I have been seen 415 times and my hostname is 448b5d70d3d8.

$ curl localhost:8000
Hello Container World! I have been seen 417 times and my hostname is 4eee9c8d54f1.

$ curl localhost:8000
Hello Container World! I have been seen 420 times and my hostname is 77e74622fa4e.

$ curl localhost:8000
Hello Container World! I have been seen 421 times and my hostname is 6e1494379165.

$ curl localhost:8000
Hello Container World! I have been seen 422 times and my hostname is 62733bdccdb8.

docker compose参考例子

相关推荐
z日火1 小时前
Windows Server 2019 安装 Docker 完整指南
windows·docker·容器
邹卓为1 小时前
docker 安装 jenkins
java·docker·jenkins
弧襪2 小时前
Docker Swarm 集群
docker·容器·eureka
就新年快乐吧3 小时前
【HD-RK3576-PI】Docker搭建与使用
linux·docker
爬山算法3 小时前
Dubbo(48)如何排查Dubbo的负载均衡问题?
运维·负载均衡·dubbo
alden_ygq9 小时前
k8s node inode被耗尽如何处理?
云原生·容器·kubernetes
爱知菜10 小时前
Windows安装Docker Desktop(WSL2模式)和Docker Pull网络问题解决
运维·docker·容器
月下雨(Moonlit Rain)11 小时前
Docker
运维·docker·容器
技术小甜甜12 小时前
[Dify] 使用 Docker 本地部署 Dify 并集成 Ollama 模型的详细指南
运维·docker·容器·dify
小白也有IT梦13 小时前
解决 Ubuntu 上 Docker 安装与网络问题:从禁用 IPv6 到配置代理
ubuntu·docker·网络配置