热部署,就是在应用正在运行的时候升级软件,却不需要重新启动应用。
首先在本地模拟一个线上需要升级 Nginx 的环境,假设旧版本为 nginx-1.0.15,需要升级到 nginx-1.16.0。
配置旧版本
# 下载 nginx-1.0.15
wget http://nginx.org/download/nginx-1.0.15.tar.gz
# 解压压缩包
tar -zxf nginx-1.0.15.tar.gz
# 进入解压后的目录
cd nginx-1.0.15
# 配置 nginx
./configure --prefix=/home/nginx
# 编译安装
make && make install
# 运行 nginx
sudo /home/nginx/sbin/nginx
此时访问服务器地址应该可以看到 nginx 的欢迎页面了。
获得新版本二进制
# 下载 nginx-1.16.0
wget http://nginx.org/download/nginx-1.16.0.tar.gz
# 解压压缩包
tar -zxf nginx-1.16.0
# 进入解压后的目录
cd nginx-1.16.0/
# 配置 nginx
./configure --prefix=/home/nginx
# 只编译不需要安装
make
在编译后的 objs 目录中,可以看到二进制文件 nginx。
热部署
经过以上步骤,我们实现了一个正在运行的旧版本 nginx 和编译完成的新版本 nginx 二进制执行文件。
热部署的流程是:
- 备份旧的 nginx 可执行文件
- 新的 nginx 可执行文件直接替换旧的(此时旧的 nginx 进程还在运行)
- 向 nginx master 进程发送热部署信号,新的 nginx 进程启动,旧的 worker 不再就收请求。
- 关闭旧的 worker 进程,完成热部署。
# 备份
cp /home/nginx/sbin/nginx /home/nginx/sbin/nginx.old
# 替换
cp -f objs/nginx /home/nginx/sbin/nginx
# 查看 master pid
ps -ef | grep nginx
root 23712 1 0 21:21 ? 00:00:00 nginx: master process /home/nginx/sbin/nginx
nobody 23715 23712 0 21:21 ? 00:00:00 nginx: worker process
# 发送热部署信号,这里 master pid 替换为自己查询到的
kill -USR2 23712
# 查看当前 nginx 进程情况,27522 就是新的 master 进程
ps -ef | grep nginx
root 23712 1 0 21:21 ? 00:00:00 nginx: master process /home/nginx/sbin/nginx
nobody 23715 23712 0 21:21 ? 00:00:00 nginx: worker process
root 27522 23712 0 21:41 ? 00:00:00 nginx: master process /home/nginx/sbin/nginx
nobody 27524 27522 0 21:41 ? 00:00:00 nginx: worker process
# 关闭旧的 worker
kill -WINCH 23712
# 再次查看进程,可以发现旧的worker进程关闭了
ps -ef | grep nginx
root 23712 1 0 21:21 ? 00:00:00 nginx: master process /home/nginx/sbin/nginx
root 27522 23712 0 21:41 ? 00:00:00 nginx: master process /home/nginx/sbin/nginx
nobody 27524 27522 0 21:41 ? 00:00:00 nginx: worker process
保留旧的 master 进程是为了在新的版本存在问题时,可以快速回退到原版本。如果发现问题要紧急回滚呢?
cp -f nginx.old nginx
# 拉起旧版本的worker进程(-HUP 相当于 -s reload)
kill -HUP old_master_pid
# 让新版本的 worker 不再接受请求
kill -USR2 new_master_pid
# 关闭新版本的 woker 进程
kill -WINCH new_master_pid
如果确认无误要退出老版本的 nginx,可以执行命令:
kill -QUIT old_master_pid