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


    在这里插入图片描述

    Docker Compose实现负载均衡

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

    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:
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46

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

    nginx.conf文件的内容如下:

    server {
      listen  80 default_server;
      location / {
        proxy_pass http://flask-demo:5000;
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    启动服务:

    $ 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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    访问服务:

    $ 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.
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

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

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

    $ docker-compose exec nginx cat /etc/resolv.conf
    nameserver 127.0.0.11
    options ndots:0
    
    • 1
    • 2
    • 3

    负载均衡的实现

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

    $ 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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    再次访问我们的服务:

    $ 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.
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

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

    负载均衡失效原因分析

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

    $ 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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

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

    / # 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.
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

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

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

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

    server {
      listen  80 default_server;
      location / {
        resolver 127.0.0.11 valid=1s; #设置dns服务器,缓存时间改为1s
        set $backend http://web:5000
        proxy_pass $backend;
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

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

    $ 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.
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    docker compose参考例子

    • 投票app练习:https://github.com/dockersamples/example-voting-app
    • docker-compose example:https://github.com/docker/awesome-compose
  • 相关阅读:
    Lumiprobe非荧光叠丨氮化物研究丨3-叠丨氮丙醇
    【无标题】
    SpringCloud和Kubernetes的区别
    React 全栈体系(十九)
    GAN生成哆啦A梦,亲测疯狂训练50000epoch的效果,俺的菜菜电脑吃不消
    C++中的引用
    深入浅出学习透析 Nginx 服务器的基本原理和配置指南「运维操作实战篇」
    java虚拟机详解篇四(类的生命周期)
    【TypeScript】深入学习TypeScript模块
    Percolation: Slipping through the Cracks
  • 原文地址:https://blog.csdn.net/u022812849/article/details/134006508