#
目标
滚动更新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;
|