· 阅读约 3 分钟
一次切换迁移到 Kubernetes
ClickFortify 如何从单台 VM 迁移到 Kubernetes:在 stage 主机名后运行新栈,在那里完成验证,再通过一次 DNS 变更将生产环境指向新集群。
机器翻译。 阅读原文
ClickFortify 最初运行在一台跑着 docker-compose 的 VM 上。如今它运行在 GCP 上的 Kubernetes 中,并通过 ArgoCD 部署。这篇文章讲的是两者之间的迁移,以及我为什么最终选择一次切换,而不是渐进式迁移。
产品不能暂停
ClickFortify 在真实流量上采集广告点击,为其评分,并把排除规则写回 Google Ads 和 Meta。点击全天候不断涌入。一个需要维护窗口的迁移计划,或者让追踪服务一半在旧栈、一半在新栈的方案,都会让真实的广告主损失真实的预算。
所以问题不是“我们如何把所有东西搬过去?”,而是“我们如何让切换成为一个单一、小巧且可逆的步骤?”。
先把部署路径做对
在迁移任何流量之前,集群需要一条我信得过的部署路径。GitOps 方案使用三个仓库:应用代码、ArgoCD 部署的清单,以及平台基础设施。最初的计划是把清单放在应用的 monorepo 里。我把它们拆了出来,原因有三:ArgoCD 每次刷新都会重新克隆仓库,而 monorepo 很大;平台有自己的生命周期和审查人;拆分后,应用的 CI 完全无法访问平台仓库。
这一点对切换很重要。如果新环境的部署方式与今后一直使用的方式完全相同,那么第一天验证它,也就同时验证了发布流程。
一组并行的 stage 主机名
我最终确定的计划分为三步:
- 在集群上部署完整的新栈,置于一组并行的 stage 主机名之后,例如
stage.example.com。 - 在那里进行验证。
- 一次性把生产主机名指向集群。
在测试新环境期间,生产环境没有任何变化。旧 VM 继续处理所有真实请求,而 stage 主机名给了我一个端到端检验新栈的地方。
无需数据迁移,以及随之而来的隐患
两边使用的是同一套数据库。这正是一次切换得以实现的原因:没有需要执行的数据复制,没有需要保持一致的同步,也不存在两套栈对数据看法不一致的时刻。DNS 切换时,新栈早已在读写与旧栈相同的数据行。
隐患在于,stage 环境从来都不是真正的沙箱。它写入的任何内容都会进入生产数据。测试使用的是专门预留的空间,并且从未把任何真实客户的脚本指向 stage 追踪服务。
为什么 worker 保持为零
worker 是运行定时任务的服务。在共享数据库上,两个 worker 意味着每个定时任务都会运行两次:邮件发两遍,清理任务删两遍。因此在整个 stage 阶段,新环境中的 worker 一直保持零副本。
# Illustrative: keep the new side's worker off until the flip
kubectl scale deployment worker --replicas=0切换,以及确认切换稳定
切换本身就是一次 DNS 变更:把生产主机名指向集群。在此之前,我检查了集群上运行的镜像摘要(digest)是否与我预期发布的版本一致。
切换之后,最关键的检查在追踪服务上。它位于 CDN 和反向代理之后,而在这种结构下获取访客的真实 IP,恰恰是拓扑变化时最容易出问题的地方。切换后的实时追踪流量显示的是真实访客 IP,没有任何 CDN 边缘节点地址。确认这一点之后,stage 主机名就被移除了。
切换刚完成时,回滚就是反向执行同样的操作:把主机名重新指向旧 VM。我只是停掉了那台 VM,而没有删除它。
给要做同样迁移的人的建议
- 如果两边可以共享数据库,就这么做。这能省去数据迁移,让切换只需一次 DNS 变更。
- 列出所有定时任务,确保在 stage 阶段每个任务只在一个地方运行,即使这意味着某个服务无法预演。
- 在切换前清空或导出排队中和延迟执行的任务。它们存放在数据库之外。
- 在切换前就确定验证检查项,并且要基于真实流量,而不是健康检查端点。