小Cの已经记不起来的博客

Nginx 反向代理 蓝绿部署 配置详解

起因很简单:站点每次更新,我都是直接把旧进程一杀,换包重启,那十几秒里所有请求全是 502,有人正在用就直接被踢出去了。之前流量小都忍了,直到有一次更新正好赶上有人在操作,群里当场有人问“网站挂了?”,怪尴尬的。于是琢磨着搞蓝绿部署,说白了就是同时跑两套一样的服务,一套对外服务(蓝),一套闲着(绿),更新只更新闲的那套,测好了把流量切过去,全程不断线。

原理没什么神秘的,核心就是让 Nginx 帮你切流量,切的方式是改 upstream 配置然后 reload,已有连接不受影响,新请求走新的 upstream,就这么简单。

环境准备

假设机器上跑着两个一样的服务:

怎么跑两套随你,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 了,不过那是另一个故事了。

评论

还没有评论。

发表评论

提交后评论将经过自动审核,审核通过后公开展示。

未在播放