Featured image of post Docker-Nginx不停机滚动更新WEB程序

Docker-Nginx不停机滚动更新WEB程序

# 目标

滚动更新web程序, 不停机给用户带来更好的体验.

# 背景

不停机的原理主要是利用docker和nginx来实现, 下面将使用docker compose讲解如何实现.

# 实操

docker-compose.yaml

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
services:
  server:  # 假设我们的后端服务端口为8888
    image: your-image:latest
    restart: always
  web:   # nginx代理前端文件, 后端服务直接访问同网络下的8888即可
    image: nginx:alpine
    restart: always
    ports: # 暴露8086端口在外部使用
      - '8086:80'
    volumes:
      - ./nginx:/etc/nginx/conf.d

nginx/config.conf

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
server {
    listen       80;
    server_name _;

    location / {
        root /usr/share/nginx/html;
        try_files $uri $uri/ /index.html;
    }

    resolver 127.0.0.11 valid=10s;
    set $backend "server";
    proxy_connect_timeout 5s;

    location /api {
        proxy_set_header Host $http_host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        rewrite ^/api/(.*)$ /$1 break;  #重写
        proxy_pass http://$backend:8888; # 设置代理服务器的协议和地址
    }
}

以上配置假定已经运行:

1
2
3
4
$ docker compose ps --format 'table {{.ID}}\t{{.Names}}\t{{.State}}'
CONTAINER ID   Container Name    STATE
3391f7b7f896   system-server-1   running
641b2733f3aa   system-web-1      running

届时我们要更新server服务, 只需要启动多一个server服务, 然后将旧的server服务网络断开, 再将其容器删除即可.

# 启动多一个server容器

1
2
3
4
5
$ docker compose scale server=2
[+] Running 3/3
 ✔ Container system-web-1     Running
 ✔ Container system-server-1  Running
 ✔ Container system-server-2  Started

可以看到启动了一个新容器system-server-2, 接下来等待10秒钟左右, 再将旧容器system-server-1的网络断开.

# 断开旧容器的网络(让请求不再到达)

断开网络前, 先通过docker network ls查看当前compose的网络, 通常以compose文件的目录为开头.

1
2
3
4
5
6
7
$ docker network ls
NETWORK ID     NAME                 DRIVER    SCOPE
b4df81bc7a80   basic-server_basic   bridge    local
3fd2de515d81   bridge               bridge    local
febbd8496116   host                 host      local
2771992beac0   none                 null      local
$ docker network disconnect basic-server_basic system-server-1

断开网络之后, 所有到达web的后端请求都会自动去访问新的server, 但是继续等待10秒钟左右, 让旧服务的请求处理完成, 再将旧服务停止.

# 停止旧容器的服务

1
docker stop system-server-1

旧容器服务停止之后, 如果没出现什么意外的情况下, 就可以使用下面命令把旧服务删除掉了.

1
docker rm system-server-1

也可以不删除直接等待下次更新之前在删除, 那么下次更新之前运行下面命令就可以快速删除旧容器, 其中name后面跟着的是服务名.

1
2
3
docker ps --filter status=exited --filter name=server \
          --format '{{.ID}}\t{{.CreatedAt}}' \
          | sort -k2 | awk '{{print $1}}' | HEAD -n 1 | xargs docker rm

# 细节

重点在nginx配置文件中, 这里不要使用upstream, 使用upstream一定有问题, 首先启动的不是nginx, 而是server服务, 会报错导致容器退出, 其他细节参考这里

1
2
3
    resolver 127.0.0.11 valid=10s;
    set $backend "server";
    proxy_connect_timeout 5s;