用 Nginx + Lua 做按用户灰度发布:一次真实落地记录

举报
yd_219600272 发表于 2026/11/07 09:18:05 2026/11/07
【摘要】 记录一次不依赖服务网格、只用 Nginx + Lua 就完成的灰度发布:分流键怎么选、脚本怎么写、会话和缓存怎么保持一致,以及回滚时踩到的坑。

一、为什么把灰度放在网关层

项目早期只有三台应用服务器,每次上线都是整批替换,出事就只能回滚。想上灰度,评估过服务网格和配置中心下发权重,但对一个五人团队来说太重:要引入新的控制面,还要改所有调用链。最后选了一个更土的办法——在 Nginx 上用 Lua 做按用户分流,改造成本不到一天,回滚只需要改一个 upstream 名字。

二、分流键比权重更难选

按 IP 分是最省事的,但同一个办公室出口 IP 相同,测试同事会集体落到灰度组,等于没灰度。按设备号分又会让同一用户在不同端上看到不一样的版本。最后用的是登录用户 ID:从 Cookie 里取 uid,取不到就退回设备指纹,再取不到就按 IP 哈希。分流比例用 uid 对一个固定盐做 crc32 再取模,保证同一个用户永远落在同一侧,不会刷新一次页面就换一个版本。

三、用 Lua 在 access 阶段改写 upstream

关键是把逻辑放在 access 阶段,而不是 rewrite,因为 rewrite 阶段还拿不到完整的请求头。脚本里先算桶号,命中灰度就把变量 upstream_name 设成 app_v2,否则设成 app_v1,再在 location 里用 proxy_pass 指向这个变量。注意 proxy_pass 带变量时 Nginx 不会自动解析域名,必须显式写 resolver,并且变量里要带上完整的 scheme 和路径,否则会出现 502 或者路径被吃掉的情况。

四、会话与缓存的一致性

分流以后最容易出的问题是数据不一致。第一是登录态:如果 session 存在应用本地内存里,用户被分到另一组就会掉线,所以灰度前必须先把 session 外置到 Redis。第二是缓存:两组服务写同一份缓存,key 里必须带上版本前缀,否则老版本写进去的旧结构会被新版本读到,序列化直接报错。第三是消息队列,灰度组的消费者如果先上线,可能消费到老版本还不认识的消息格式,稳妥做法是新增字段而不是改字段。

五、回滚与观测

灰度期间必须能一眼看出两组的表现差异。做法是在 Nginx 的日志里加一个 upstream 字段,落到日志服务后按版本分组统计错误率和 P95 耗时;同时在 Lua 里把命中的桶号写进响应头,出问题时能直接定位是哪个用户被分到了哪一组。回滚分两级:先按比例降回零,让新请求全部走老版本,观察五分钟;如果还有问题,再把 upstream 指回单组并清掉灰度机器的连接。整个过程不需要重新发版,这也是当初选网关方案最大的收益。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。