Nginx 反向代理 蓝绿部署 配置详解
起因很简单:站点每次更新,我都是直接把旧进程一杀,换包重启,那十几秒里所有请求全是 502,有人正在用就直接被踢出去了。之前流量小都忍了,直到有一次更新正好赶上有人在操作,群里当场有人问“网站挂了?”,怪尴尬的。于是琢磨着搞蓝绿部署,说白了就是同时跑两套一样的服务,一套对外服务(蓝),一套闲着(绿),更新只更新闲的那套,测好了把流量切过去,全程不断线。
原理没什么神秘的,核心就是让 Nginx 帮你切流量,切的方式是改 upstream 配置然后 reload,已有连接不受影响,新请求走新的 upstream,就这么简单。
环境准备
假设机器上跑着两个一样的服务:
- 蓝色版本:
127.0.0.1:8081,当前线上 - 绿色版本:
127.0.0.1:8082,新版本
怎么跑两套随你,docker 也好,systemd 起两个实例也好,只要端口错开就行。我是用 docker-compose 写了两个 service,镜像相同端口不同,更新的时候只 rebuild 新的那份。
Nginx 配置
Nginx 这边的思路是:upstream 不要直接写死在 server 块里,单独抽成一个文件,用 include 引进去。这样切换的时候只动这一个文件,干净。
# /etc/nginx/conf.d/app.conf
upstream app {
include /etc/nginx/conf.d/upstream_app.conf;
}
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://app;
proxy_set_header Host $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;
}
}
upstream 文件内容就一行:
# /etc/nginx/conf.d/upstream_app.conf
server 127.0.0.1:8081; # <--- 当前指向蓝
等绿色部署好、自己 curl 一圈没发现问题了,把这行改成 server 127.0.0.1:8082;,然后执行:
nginx -t && nginx -s reload
nginx -t 先检查语法再 reload,养成习惯,不然配置改错了 reload 直接失败,线上当场去世。reload 是平滑的,正在处理的请求会接着用旧配置处理完,新请求才走新配置,不会出现请求被硬切断的情况。
写个切换脚本
手动改文件也能用,但次数多了就烦,我写了个简单的:
#!/bin/bash
# ./switch.sh <blue|green>
PORT=$([ "$1" = "blue" ] && echo 8081 || echo 8082)
CONF=/etc/nginx/conf.d/upstream_app.conf
if grep -q "127.0.0.1:$PORT;" $CONF; then
echo "当前已经指向 $PORT 了,不用切"
exit 0
fi
echo "server 127.0.0.1:$PORT;" > $CONF
nginx -t && nginx -s reload
用法就是 ./switch.sh green,切完顺手验证一下:
curl -s http://127.0.0.1/health
服务如果有版本号接口,看一眼确认切过去了,没有的话就翻翻新实例的日志,有流量进来说明切成功了。有个小瑕疵:脚本是先写文件再 nginx -t,万一写坏了语法检查不过,文件已经改了,不过反正 upstream 就一行,改回来就是了。
几个踩过的坑
旧实例别急着停。reload 之后旧连接还会存活一段时间,我一开始切完就 docker stop,结果几个长请求直接被掐了。现在都是切完等个几十秒,翻翻旧实例日志确认没流量了再停。
websocket 要单独加配置,不然切过去发现 ws 死活连不上:
location /ws {
proxy_pass http://app;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 300s;
}
排水的问题。一开始我想找 Nginx 开源版有没有自带的连接排水功能,翻了半天文档发现只有 Plus 版才有,可能是我瞎没找到吧... 反正结论就是开源版只能靠时间换,流量大的站旧版本多留一会儿。
回滚
蓝绿部署最大的好处就是回滚快。切过去之后发现新版本有问题?./switch.sh blue,reload,完事,前后不超过十秒。这也是我觉得这玩意儿比滚动部署省心的地方——就两个实例,心智负担几乎为零。当然代价是资源得双份,好在我的服务单实例内存几百兆,一台 4G 的小机器完全跑得下两份。
总结
整套东西说穿了就三步:跑两个实例、upstream 抽成单独文件、改文件加 reload。没有什么高科技,但确实把“更新即宕机”这个问题解决了。等后面实例多了、机器多了,就该上 Consul 动态 upstream 或者干脆 k8s 了,不过那是另一个故事了。
评论
还没有评论。
发表评论
提交后评论将经过自动审核,审核通过后公开展示。