【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参考例子

相关推荐
计算机小手1 小时前
一个带Web UI管理的轻量级高性能OpenAI模型代理网关,支持Docker快速部署
经验分享·docker·语言模型·开源软件
坚持学习前端日记2 小时前
常见docker指令
docker·容器·eureka
小芳矶3 小时前
Dify本地docker部署踩坑记录
python·docker·容器
70asunflower3 小时前
阿里云无影云电脑 Docker 使用完全指南
阿里云·docker·云计算
久绊A4 小时前
春节前云平台运维深度巡检-实操经验
运维·安全·容器·kubernetes·云平台
feasibility.5 小时前
在OpenCode使用skills搭建基于LLM的dify工作流
人工智能·低代码·docker·ollama·skills·opencode·智能体/工作流
!chen5 小时前
银河麒麟v11 kubeadm部署k8s v1.35.0高可用集群
云原生·容器·kubernetes
DB!!!6 小时前
自学Kubernestes(k8s)Day1 -- 核心基础概念
云原生·容器·kubernetes
lpruoyu6 小时前
【Docker进阶-02】Docker命令
docker·容器
FLGB7 小时前
Docker网段和服务器内部网段172.17 网段冲突导致网络不通
服务器·网络·docker