docker 容器在负载均衡器后面的滚动部署

rolling deployment for docker containers behind load balancer

我在负载均衡器后面滚动部署 docker 个容器时遇到问题。

这是我的 docker compose yml 文件内容。

nginx:
    image: nginx_image
    links:
        - node1:node1
        - node2:node2
        - node3:node3
    ports:
        - "80:80"
node1:
    image: nodeapi_image
    ports:
        - "8001"
node2:
    image: nodeapi_image
    ports:
        - "8001"
node3:
    image: nodeapi_image
    ports:
        - "8001"

这是我的 nginx.conf

worker_processes 4;

events { worker_connections 1024; }

http {

  upstream node-app {
        least_conn;
        server node1:8001 weight=10 max_fails=3 fail_timeout=30s;
        server node2:8001 weight=10 max_fails=3 fail_timeout=30s;
        server node3:8001 weight=10 max_fails=3 fail_timeout=30s;
  }

  server {
        listen 80;
        listen 443 ssl;

        # ssl    on;
        ssl_certificate     /etc/nginx/ssl/imago.io.chain.crt;
        ssl_certificate_key /etc/nginx/ssl/imago.io.key;

        location / {
          proxy_pass http://node-app;
          proxy_http_version 1.1;
          proxy_set_header Upgrade $http_upgrade;
          proxy_set_header Connection 'upgrade';
          proxy_set_header Host $host;
          proxy_cache_bypass $http_upgrade;
        }
  }
}

如果我要部署一个新构建的映像,我必须停止节点容器,将其删除并使用新映像重新创建它。这里的问题是新容器将获得一个新 IP 而 nginx 容器不知道该新 IP,因此如果我重新创建负载均衡器后面的 3 个容器,一旦我重新创建最后一个容器,应用程序将不再提供服务,因为所有 IP在 nginx 机器中 /etc/hosts 和环境变量不再是最新的。

我可以通过 SSH 连接到每个容器,通过从 git 存储库中提取并重新启动进程来更新其代码,但这对我来说似乎是错误的。这样做的正确方法是什么?

有一个更简单的方法来实现,以下面的docker-compose.yml文件为例:

lb:
    image: tutum/haproxy
    links:
        - app
    ports:
        - "80:80"
app:
    image: tutum/hello-world

这个 docker 组合文件描述了两个服务:

  • lb:使用 tutum/haproxy 镜像的负载均衡器
  • 应用程序:侦听端口 80 的示例 Web 应用程序

如果您天真地使用 docker-compose up -d 启动这些服务,您最终将只有 2 个容器(负载均衡器和 Web 应用程序)。

但是如果你 运行 docker-compose scale app=3 然后 运行 再次 docker-compose up -d,你最终会得到 4 个负载平衡容器。

这里的关键人物是 tutum/haproxy docker 图像,它能够发现它链接到的不同容器。


一个类似的解决方案是使用 Jason Wilder's nginx-proxy 图像,它的优点是可以实时发现新节点;这样您就不必重新启动 lb 服务。

lb:
    image: jwilder/nginx-proxy
    volumes:
        - /var/run/docker.sock:/tmp/docker.sock:ro
    ports:
        - "80:80"
app:
    image: tutum/hello-world
    environment:
        VIRTUAL_HOST: www.mysite.com

VIRTUAL_HOST 环境变量必须设置为解析为 docker 主机 IP 地址的域名。


另一种是使用Traefik

lb:
  image: traefik
  command: --docker
  ports:
    - "80:80"
  volumes:
    - /var/run/docker.sock:/var/run/docker.sock

app:
  image: tutum/hello-world
  labels:
    traefik.frontend.rule: Host:www.mysite.com

traefik.frontend.rule 标签必须定义一个 Traefik rule 设置为解析为 docker 主机 IP 地址的域名。

Traefik 还提供不同的 load balancing strategies and circuit breakers.