上个月我们团队在推进GitOps实践的过程中遇到了一次惊心动魄的线上故障整个过程从发现问题到排查原因再到恢复服务前后持续了将近两个小时影响了部分用户的使用,虽然最后成功恢复了服务没有造成太大的损失,但是整个过程非常惊心动魄也给我们留下了深刻的教训和宝贵的经验。
今天想把这次故障的完整过程复盘一下包括故障背景现象排查过程根本原因以及后续的改进措施希望能给正在实践,或者准备实践GitOps的团队一些参考和警示避免踩类似的坑。
一、故障背景:我们正在推进GitOps实践
先说说背景我们团队这半年一直在推进DevOps和云原生转型把服务逐步迁移到Kubernetes集群上,同时也在探索更好的部署和运维方式之前,我们用的是传统的CI/CD流水线代码提交后触发CI构建镜像,然后CD工具(我们用的是Jenkins)通过kubectl或者Helm命令把新版本部署到Kubernetes集群这种方式,虽然也能工作,但是有一些问题,比如部署操作不透明谁在什么时候部署了什么不好追溯集群的实际状态和代码仓库里的声明可能不一致(有人手动改了集群配置没同步到代码仓库)运维人员需要有集群的直接访问权限有安全风险等等。
后来我们了解到GitOps的理念觉得很适合我们的场景GitOps的核心思想是把基础设施和应用的声明式配置都存在Git仓库里作为唯一的事实来源(Single Source of Truth)然后通过自动化工具(比如ArgoCDFlux等)把Git仓库里的配置同步到Kubernetes集群保证集群的实际状态和Git仓库里的声明一致GitOps有很多好处,比如部署操作透明可追溯(所有变更都是Git提交有记录有审核)集群状态和Git声明一致不会出现配置漂移(Configuration Drift)运维人员不需要直接访问集群只需要提交GitPR安全性更好回滚方便(出问题直接Git回滚提交就行)等等这些好处正好能解决我们之前,遇到的问题,所以我们决定在团队内推进GitOps实践。
我们选型的GitOps工具是ArgoCD因为它功能比较完善界面友好社区活跃支持多集群管理应用同步健康检查等功能我们先在测试环境搭了ArgoCD把几个非核心应用的部署迁移到GitOps模式跑了一段时间觉得比较稳定也比较好用,然后就开始逐步把生产环境的应用也迁移到GitOps模式出故障的那天正好是我们把一个核心业务应用迁移到GitOps模式后的第二天本来以为一切顺利没想到就出了大问题。
二、故障发生:监控告警突然响起
故障发生在一个周三的下午大概三点多我当时正在写代码突然手机和电脑,同时响起了告警声音是我们的监控系统(Prometheus + Alertmanager)发出来的告警说我们的一个核心业务应用的健康检查失败了实例数从正常的6个降到了0个,而且错误率飙升到100%用户的请求几乎全部失败我当时心里一沉知道出大事了这个应用是我们的核心业务应用承载了大量用户的核心操作出问题影响会很大。
我赶紧打开监控仪表盘一看果然这个应用的所有Pod都处于CrashLoopBackOff状态(不断崩溃重启,但是启动不起来)实例数0用户请求全部503错误,而且这个应用的下游依赖的几个应用也,因为这个应用不可用开始出现错误率上升的情况,如果不赶紧恢复影响面会越来越大我赶紧在团队群里喊了一声"核心应用挂了赶紧来排查"然后开始排查问题。
首先,我先看了一下最近有没有什么变更,因为故障发生得很突然,而且是整个应用所有实例都挂了不像是单个节点,或者单个实例的问题更像是配置变更,或者代码发布导致的问题我先看了Git仓库的提交记录发现大概半小时前有一个同事提交了一个PR修改了这个应用的Kubernetes部署配置(deployment.yaml)把镜像版本从v1.2.3更新到了v1.2.4并且调整了一些环境变量和资源配置这个PR已经被合并到主分支了,而且,因为我们用了GitOpsArgoCD会自动把Git仓库的变更同步到集群,所以这个配置变更应该已经被同步到集群了触发了应用的重新部署我心里大概有数了很可能就是这次配置变更导致的问题。
但是奇怪的是这个v1.2.4版本的镜像我们之前,在测试环境已经测过了功能都正常为什么到生产环境就启动不起来呢,而且是所有实例都CrashLoopBackOff不像是偶发问题更像是配置有问题,或者环境差异导致的我先,不管,那么多先看Pod的日志和事件看看到底为什么启动不起来。
我用kubectl看了一下Pod的事件(kubectl describe pod)发现Pod的启动过程中没有明显的错误事件镜像拉取成功了容器也启动了,但是启动后很快就退出了退出码是1然后被重启又退出陷入CrashLoopBackOff我又看了容器的日志(kubectl logs)发现日志里,只有几行启动信息,然后就报了一个错误"配置文件加载失败:无法连接到配置中心连接超时"然后进程就退出了哦原来是应用启动的时候,连接配置中心失败超时导致启动失败进程退出,所以Pod不断崩溃重启。
但是为什么会连接配置中心失败呢这个应用一直都是连接同一个配置中心之前,都正常,而且配置中心本身也没告警应该是正常的为什么突然就连接不上了呢我又仔细看了一下日志里的配置中心地址发现不对日志里显示的配置中心地址是一个测试环境的地址而不是生产环境的地址哦原来如此应用在尝试连接测试环境的配置中心而生产环境的网络和测试环境是隔离的,当然连接不上超时失败,所以启动失败那为什么配置中心地址会变成测试环境的呢我赶紧去看那个刚合并的PR修改的配置文件。
一看果然那个同事在修改deployment.yaml的时候,把环境变量里的配置中心地址给改错了本来生产环境的配置中心地址应该是config-center.prod.svc.cluster.local但是他可能是从测试环境的配置文件复制过来的没改干净地址变成了config-center.test.svc.cluster.local也就是测试环境的地址,而且这个PR在Code Review的时候,大家都没注意到这个细节(因为环境变量比较多这个地址藏在一堆配置里不显眼)就给合并了,然后ArgoCD自动同步到生产环境触发了应用重新部署所有Pod都用了错误的配置中心地址启动失败全部CrashLoopBackOff导致整个应用不可用故障就这样发生了。
找到原因之后,我松了一口气,但是也很紧张,因为已经过去了快半小时了应用还没恢复得赶紧修复我先想直接在集群里手动改Deployment的环境变量把配置中心地址改对先恢复服务,但是一想不对我们用了GitOpsArgoCD会自动把集群状态同步成Git仓库里的声明,如果我手动改集群里的配置ArgoCD检测到集群状态和Git声明不一致会自动把我的修改覆盖掉又同步回错误的配置这样就改不了反而可能更乱,所以不能手动改集群配置必须通过Git提交来修改这就是GitOps的特点所有变更都必须通过Git不能手动改集群这本来是GitOps的优点(保证一致性可追溯)但是在故障应急的时候,反而成了一个小麻烦,因为必须走Git提交流程不能直接改集群快速恢复。
不过我们当时也顾不上,那么多了赶紧先在Git仓库里提一个紧急修复PR把配置中心地址改对,然后找人快速Review合并,因为是紧急故障修复我们简化了Code Review流程我自己改好提交另一个同事快速看了一下确认没问题就合并了整个过程大概花了5分钟合并之后ArgoCD检测到Git仓库有新提交自动开始同步新的配置到集群触发应用重新部署我盯着ArgoCD的界面和Pod的状态看着新的Pod一个个启动心里很紧张生怕又出什么问题大概过了两三分钟新的Pod全部启动成功了健康检查通过实例数恢复到6个错误率也开始下降用户请求逐渐恢复正常我悬着的心终于放了下来服务恢复了。
但是整个过程从发现故障到恢复服务前后花了将近一个小时(其中排查原因花了半小时多Git修复和同步花了十几分钟等待Pod启动花了几分钟)影响了部分用户大概一个小时的使用,虽然最后恢复了没有造成数据丢失,或者更大的损失,但是整个过程非常惊心动魄也给我们敲响了警钟GitOps虽然好,但是,如果流程和规范没跟上也会出大问题。
三、故障根因分析
服务恢复之后,我们没有就这样过去而是组织了故障复盘会议深入分析了这次故障的根本原因以及为什么我们的各种防护措施都没拦住这个问题最后总结出了以下几个根本原因:
根因1:配置变更错误把测试环境地址提交到了生产环境配置:
这是故障的直接原因那个同事在修改生产环境的deployment.yaml配置文件时不小心把测试环境的配置中心地址提交到了生产环境的配置里导致应用启动时连接错误的配置中心地址失败启动不起来整个应用不可用这个错误本身很低级,但是,因为环境变量比较多这个地址藏在一堆配置里不显眼,所以没被及时发现。
根因2:Code Review没有发现配置错误:
这个配置变更的PR在合并前经过了Code Review但是Review的同事没有发现这个配置中心地址错误的问题就合并了这说明我们的Code Review流程有漏洞对于配置变更这种高风险的变更没有重点关注也没有检查清单(Checklist)来确保关键配置项被检查到Review的时候,大家可能更关注代码逻辑和镜像版本对不对忽略了环境变量里的地址配置这种细节导致错误没被拦住。
根因3:GitOps自动同步没有经过灰度和验证直接全量部署到生产:
这是这次故障能造成这么大影响的重要原因我们用了ArgoCD的自动同步功能Git仓库的变更合并后ArgoCD会自动立即同步到生产环境全量部署新配置没有经过灰度发布,或者金丝雀发布也没有自动化的验证步骤(比如部署后自动检查应用健康状态和错误率有问题自动回滚)所以一个错误的配置提交合并后几分钟内就全量部署到了生产所有实例都用错误配置启动全部挂掉造成了大面积影响,如果有灰度发布机制先部署一个实例,或者小部分流量验证没问题再全量部署这次故障的影响就会小很多甚至能在灰度阶段就发现问题不会影响全量用户。
根因4:没有配置环境隔离和校验机制防止测试环境配置混入生产:
我们的生产环境和测试环境的配置文件是分开存放的在Git仓库的不同目录里,但是没有自动化的校验机制来防止测试环境的配置(比如测试环境的地址域名等)被提交到生产环境的配置目录里完全靠人来检查而人是会犯错的,所以才出现了测试环境地址混入生产环境配置的问题,如果有自动化的校验机制,比如在CI流水线里加一个检查步骤扫描生产环境配置文件里是否包含测试环境的地址,或者其他不应该出现在生产的配置有问题就阻止PR合并这次故障就能被提前拦住不会发生。
根因5:故障应急流程不完善GitOps模式下快速恢复手段不足:
故障发生后我们在恢复过程中遇到了一个问题就是,因为用了GitOps不能直接手动改集群配置快速恢复必须走Git提交流程而Git提交需要改代码提PRReview合并,然后等ArgoCD同步整个过程比直接手动改集群配置要慢一些在故障应急的时候,每一分钟都很宝贵我们之前,没有针对GitOps模式制定完善的故障应急流程和快速恢复手段,比如紧急情况下怎么快速回滚(是Git revert还是用ArgoCD的回滚功能还是临时关闭自动同步手动改集群配置应急)这些都没有提前明确,所以故障发生后有点手忙脚乱浪费了一些时间,如果有完善的应急流程和快速恢复手段故障恢复时间能缩短不少影响也会更小。
四、改进措施
针对以上根因我们制定了一系列改进措施来避免类似故障再次,发生也提升我们GitOps实践的成熟度和安全性主要有以下几个方面:
改进1:建立配置变更检查清单强化Code Review:
我们针对Kubernetes配置变更(DeploymentServiceConfigMap等)建立了专门的Code Review检查清单(Checklist)要求Review配置变更PR时必须逐项检查清单上的内容包括:
- 镜像版本是否正确是否是经过测试验证的版本。
- 环境变量是否正确特别是各种地址(配置中心注册中心数据库缓存消息队列等)是,否是对应环境的正确地址没有混入其他环境的地址。
- 资源配置(CPU内存requests/limits)是否合理有没有明显错误。
- 健康检查配置(livenessProbereadinessProbe)是否正确。
- 副本数是否合理有没有明显错误。
- 配置变更是否经过测试环境验证有没有测试通过的记录。
Review的时候,必须对照清单逐项检查确认没问题才能合并,而且对于核心应用的配置变更要求至少两个人Review通过才能合并一个人Review容易有疏漏两个人交叉检查能大大降低错误漏网的概率,另外我们也对团队进行了培训强调配置变更的风险和Review的重要性让大家重视配置变更的Code Review不要,因为是配置不是代码就掉以轻心。
改进2:引入灰度发布机制配置变更不再直接全量部署:
这是最重要的改进措施之一我们之前GitOps是自动同步全量部署风险很大现在我们引入了灰度发布机制对于生产环境的应用部署和配置变更不再直接全量部署而是先灰度部署验证没问题再全量部署具体来说,我们用ArgoCD配合Argo Rollouts(或者Istio的流量管理功能)实现灰度发布配置变更提交后先部署一个灰度实例(或者把10%的流量切到新版本)然后自动检查灰度实例的健康状态错误率延迟等指标,如果一段时间内(比如10分钟)指标都正常再自动逐步扩大灰度比例直到全量部署,如果灰度阶段指标异常(比如健康检查失败错误率超过阈值)自动回滚到旧版本,并且发送告警通知人工介入这样,即使有错误的配置变更也只会影响灰度的少量实例,或者少量流量不会全量挂掉影响面大大缩小,而且能自动回滚快速恢复不用人工紧急处理。
另外对于特别核心的应用,或者重大的配置变更我们还要求必须先在预发布环境(和生产环境配置一致的环境)验证通过才能提交生产环境的变更进一步降低风险通过这些灰度和验证机制我们能把配置变更的风险降到很低不会再出现一次错误提交就全量挂掉的情况。
改进3:在CI流水线增加配置校验防止错误配置合并:
我们在CI流水线里增加了自动化的配置校验步骤在PR提交后自动运行校验检查配置文件的正确性和,安全性主要包括:
- 环境隔离校验:扫描生产环境配置目录里的文件检查是否包含测试环境的地址域名,或者其他不应该出现在生产的标识(比如"test""staging"等关键词在地址里)有问题就阻止PR合并提示错误。
- 配置语法校验:用
kubeval或者kube-linter等工具校验KubernetesYAML文件的语法是否正确有没有格式错误,或者不推荐的配置(比如没有配置资源limits没有配置健康检查等)有问题提示修复。 - 敏感信息校验:检查配置文件里是否包含明文的密码密钥Token等敏感信息(应该用Secret或者,配置中心管理不应该明文写在配置文件里)有问题阻止合并。
- 镜像版本校验:检查配置里的镜像版本是否存在(去镜像仓库查询)是否是经过测试验证的版本(和,测试环境部署的版本对比)有问题提示。
通过这些自动化的校验能在PR阶段就发现大部分配置错误阻止错误配置合并到主分支从源头避免故障比靠人Code Review可靠很多毕竟人会犯错会疏漏,但是自动化工具不会,只要规则配置对就能稳定地发现问题这次的配置中心地址错误,如果有环境隔离校验就能在PR阶段被发现阻止合并不会造成生产故障。
改进4:完善GitOps故障应急流程建立快速恢复机制:
我们针对GitOps模式的特点制定了完善的故障应急流程和快速恢复机制明确了故障发生后的处理步骤和,责任人主要包括:
- 快速回滚机制:明确了GitOps模式下的快速回滚方法优先用Git revert回滚最近的配置变更提交,然后ArgoCD会自动同步回滚后的配置恢复服务这是最推荐的回滚方式,因为符合GitOps的理念可追溯也不会造成配置不一致,另外也可以用ArgoCD的回滚功能(ArgoCD支持回滚到历史版本)更快一些,但是回滚后要记得同步修改Git仓库的配置,不然下次同步又会覆盖回去。
- 紧急手动干预机制:明确了在特别紧急的情况下(比如Git仓库访问不了,或者ArgoCD本身出问题无法自动同步)可以临时关闭ArgoCD的自动同步功能,然后手动修改集群配置快速恢复服务,但是事后必须把手动的修改同步回Git仓库保持一致性,并且要记录操作过程便于复盘这个机制作为最后的应急手段平时不用,但是必须有以防万一。
- 故障分级和响应流程:明确了故障分级标准(P0P1P2P3)以及不同级别故障的响应时间处理流程通知机制等,比如P0级故障(核心应用全量不可用)必须5分钟内响应立即启动应急流程通知相关负责人等等让故障发生后大家知道该怎么做谁来做不会手忙脚乱浪费时间。
- 定期故障演练:定期(比如每季度)组织故障演练模拟各种故障场景(包括GitOps配置错误导致的故障)检验应急流程和,恢复机制是否有效团队成员是否熟悉应急流程发现问题及时改进通过演练让大家在真正故障发生时能从容应对快速恢复。
改进5:加强团队培训提升GitOps意识和能力:
最后我们也加强了团队的培训和分享提升大家对GitOps的理解和实践能力包括:
- 组织GitOps最佳实践培训讲解GitOps的理念优势风险以及我们团队的规范和,流程让每个人都清楚GitOps模式下该怎么工作要注意什么。
- 分享这次故障的复盘结果让每个人都从这次故障中吸取教训重视配置变更的风险和,流程规范。
- 建立GitOps实践文档和规范包括配置变更流程Code Review规范灰度发布流程故障应急流程等让大家有章可循新同学也能快速上手。
- 鼓励团队成员学习和探索GitOps相关的工具和最佳实践不断优化我们的GitOps实践提升效率和安全性。
五、经验和教训
这次故障,虽然惊心动魄也造成了一定的影响,但是也给我们带来了很多宝贵的经验和教训这里也分享给大家希望正在实践,或者准备实践GitOps的团队能引以为戒少踩坑:
教训1:GitOps不是银弹流程和规范比工具更重要:
很多团队刚接触GitOps的时候,可能会觉得用了GitOps工具(ArgoCDFlux等)就万事大吉了部署就安全了,但是这次故障告诉我们GitOps只是一种理念和工具不是银弹它能带来很多好处,但是,如果配套的流程和规范没跟上(比如Code Review不严格没有灰度发布没有配置校验没有应急流程等)同样会出大问题甚至,因为GitOps自动同步的特点错误配置会更快地部署到生产造成更大的影响,所以工具只是基础更重要的是配套的流程和规范以及团队的意识和能力,只有工具+流程+规范+人都到位了GitOps才能真正发挥它的价值安全高效地工作。
教训2:配置变更和代码变更一样重要甚至风险更大:
很多团队包括我们之前,都比较重视代码变更的Review和测试,但是对配置变更比较轻视觉得配置就是改几个参数没什么大不了,但是实际上,配置变更的风险一点都不比代码变更小甚至更大,因为配置直接决定了应用的运行行为和环境一个小小的配置错误(比如这次的配置中心地址错误)就可能导致整个应用不可用,而且配置变更往往不经过编译和自动化测试(很多团队对配置没有自动化测试)更容易隐藏错误,所以一定要像重视代码变更一样重视配置变更甚至更重视严格的Code Review自动化校验灰度发布验证这些都不能少不要,因为是配置变更就掉以轻心。
教训3:自动化校验和灰度发布是防止故障的两道关键防线:
这次故障,如果有自动化的配置校验就能在PR阶段发现配置中心地址错误阻止合并故障根本不会发生,如果有灰度发布机制,即使错误配置合并了也只会影响灰度的少量实例不会全量挂掉,而且能自动回滚影响很小,所以自动化校验和灰度发布是防止配置变更故障的两道关键防线一道在合并前拦住错误一道在部署时限制影响范围有了这两道防线配置变更的风险就能降到很低不会再出现一次错误提交就全量挂掉的情况,所以,不管用不用GitOps只要有配置变更的场景都建议建立这两道防线自动化校验+灰度发布这是用这次故障的代价换来的宝贵经验。
教训4:故障应急能力是团队的核心能力必须提前建设:
这次故障我们在应急恢复过程中,因为对GitOps模式下的应急流程不熟悉浪费了一些时间这也让我们认识到故障应急能力是团队的核心能力必须提前建设不能等故障发生了才想起来要怎么处理那样就晚了会浪费宝贵的恢复时间扩大故障影响,所以一定要提前制定完善的故障应急流程和快速恢复机制明确不同故障场景的处理步骤和责任人,并且定期做故障演练让团队成员都熟悉应急流程在真正故障发生时能从容应对快速恢复把故障影响降到最低这是团队技术能力和工程成熟度的重要体现一定要重视。
六、写在最后
以上就是这次GitOps实践故障的完整复盘包括故障背景现象排查过程根本原因改进措施以及经验教训整个过程,虽然惊心动魄也造成了一定的影响,但是也给我们团队带来了很大的成长让我们对GitOps的理解更深了也让我们的工程实践更成熟了从这个角度,来说这次故障也是有价值的是我们团队成长过程中交的一次"学费"希望以后我们能不再交类似的"学费"也希望这篇复盘能帮其他正在实践GitOps的团队少交"学费"少踩坑。
GitOps是一种很好的DevOps理念和实践方式能带来很多好处,比如部署透明可追溯配置一致安全性好回滚方便等等,但是它也不是银弹不是用了就万事大吉了它需要配套的流程规范工具和团队能力来支撑才能真正发挥它的价值安全高效地工作我们团队也是在实践过程中不断踩坑不断改进逐步成熟的相信,只要我们持续改进不断完善一定能把GitOps实践得越来越好也能给团队带来更大的价值。
最后用一句话结束这篇文章也是我这次故障最大的心得"工具是把双刃剑用得好能提升效率用不好也会带来风险GitOps也一样关键不在于工具本身而在于使用工具的人和配套的流程规范,只有人流程规范工具都到位了才能真正发挥工具的价值安全高效地工作故障不可怕可怕的是不复盘不改进每次故障都是一次成长的机会让我们变得更强。"
愿我们都能在实践中不断成长不断进步也愿大家都能用好GitOps和,各种工具提升效率保障稳定为业务发展保驾护航加油!
评论(0)
暂无评论,快来抢沙发~
评论功能仅对会员开放,请先登录
登录