资讯动态

Chaos Mesh 实操指南:Kubernetes 混沌工程与故障注入编排

发布时间:2026/10/22 11:13:52 来源:尧图企业网站定制
我其实不太喜欢把“混沌工程”挂在嘴边这个词这两年被讲得太宽泛反而让很多人以为它等于随机搞垮环境。直到我们负责的某个核心集群连续三周出现间歇性 5xx每次都查不到稳定复现路径我才真正意识到需要一套可编排、可观测、可恢复的故障注入机制而 Chaos Mesh 是目前把这三件事讲得最清楚的那个。这篇不是官方文档的复述是我从部署到跑完第一批实验走了一遍之后留下的实操笔记和思考。先说结论Chaos Mesh 绝对不只是“往 Kubernetes 里丢几个搞破坏的 Pod”的工具。它把混沌实验变成了声明式资源你写一个 YAML控制平面负责注入实验到点自动恢复Dashboard 能看现场Workflow 还能把多类故障编排成一次完整演练。这套模式对于真正想把“故障演练”当成日常工程动作的团队来说比我之前见过的很多自研脚本都要扎实。1. 混沌工程到底能回答什么问题一次事故逼出的实践念头1.1 我们当时遇到的现象以及为什么常规监控没用当时现象其实很典型线上订单相关服务每隔几小时出现一波 5xx单看时序曲线就像有人在毛刺上撒盐长短不一但很快恢复。我们查了半天数据库慢查询、线程池堵塞、Redis 热 key都找不到一条能稳定复现的路径。最难受的是当我们把环境放到压测脚本里跑一切正常一回到生产流量问题又来了。后来复盘才把方向锁定在一个很朴素的猜测上某个依赖连接的中间件在长时间空转后连接被对端静默回收而服务并没有正确处理这种情况。这个场景用普通压测很难触发因为它需要时间积累再叠加一次小规模节点漂移或网络抖动。类似这种问题就是混沌工程要回答的在受控范围内主动制造“故障条件”验证系统在异常到来时是否具备自愈能力、降级能力以及最终是否能恢复到一个稳定态。而不是等故障随机出现后再对着日志猜。1.2 Chaos Mesh 在混沌工程里的定位注入故障不是目的很多人第一次接触 Chaos Mesh注意力全放在“它能杀 Pod、它能延迟网络、它能压CPU”但真正交付价值的是后面那半句注入之后发生了什么。Chaos Mesh 给我的整体感觉是把实验做成了 Kubernetes 原生对象。控制面由 chaos-controller-manager 负责监听实验资源每个计算节点上跑一个 chaos-daemon 来执行具体注入动作Dashboard 提供可视化交互入口。整个链路像是一套“故障编排系统”你定义目标它负责执行、执行期间的清理和自动恢复都由控制器兜底。这跟手动登录到节点往容器里执行几条 tc 命令完全不是一回事。手动方式偶发搞一次可以但没法形成制度你没法把实验写到 Git 里 review没法自动定时跑也没法在出问题时一键定位到“是哪个实验造成的”。Chaos Mesh 把混沌操作规约成了 API 和 CRD那它就能融入持续交付和运维自动化里。1.3 不同注入手段的取舍自研脚本 vs 成熟平台我最早也考虑过自己写一套。思路很简单做一个 DaemonSet按队列去执行 kill、网络延迟、磁盘占满之类的动作。但写了两天就发现真正麻烦的不是注入动作本身而是状态管理。自研脚本要考虑的问题会迅速膨胀实验进行到一半Pod 被重新调度怎么办DaemonSet 重启后之前注入的规则还在不在如何保证多个实验并发执行时不互相覆盖谁来负责恢复现场审计谁来做。这些问题每一个都不难但合在一起就是一套控制面平台。所以“要不要上成熟平台”的答案取决于团队是想要一个一次性故障脚本还是想要一套长期可维护的混沌工程基础设施。如果你认可后者Chaos Mesh 这类基于 CRD 的平台优势就非常明显。它把“注入”和“恢复”都定义成了声明式状态剩下的事情交给控制器循环处理出问题也方便排查。2. 部署前的边界确认与 Helm 安装全程2.1 部署之前我先确认的三件事第一Kubernetes 集群版本。Chaos Mesh 依赖 CRD 和 Admission Webhook建议使用较新的稳定版本。我这里用的是 Kubernetes 1.25 集群整体流程很顺利。如果你的集群低于 1.20务必先查一下对应版本的兼容性以免 webhook 或 CRD 行为有差异。第二节点上的特权容器能力。chaos-daemon 要在工作节点上操纵网络栈、挂载文件系统、调整 cgroup所以必须运行特权容器。有些受管集群默认会拦截特权容器或者通过 Pod Security Admission 限制得很死。我建议在部署之前先用一个最小化特权容器验证至少一个节点能跑起来别等整个 chart 装完才发现 daemon 一直 Pending。第三命名空间隔离。混沌平台本身需要足够权限但它不该和你业务应用混在一个命名空间里。单独建一个chaos-mesh命名空间再通过 RBAC 和实验 selector 做边界控制比把一切揉在一起要安全得多。2.2 Helm 安装与关键 values 说明我用 Helm 安装整体命令并不复杂。先把 chart 仓库加上然后指定版本安装helm repo add chaos-mesh https://charts.chaos-mesh.org helm repo update helm search repo chaos-mesh kubectl create ns chaos-mesh helm install chaos-mesh chaos-mesh/chaos-mesh \ --namespace chaos-mesh \ --version 2.6.3当然生产环境不建议直接给默认参数。我给了一份裁剪过的 values三处比较关键controllerManager: # 生产建议至少 2 个副本依赖 leader election 保证只有一个 controller 在干活 replicaCount: 2 resources: requests: cpu: 200m memory: 256Mi limits: cpu: 1 memory: 1Gi chaosDaemon: imagePullPolicy: IfNotPresent # 如果你对节点安全有要求可以通过 nodeSelector 先圈定几台测试节点 nodeSelector: chaos-pool: true dashboard: create: true service: type: ClusterIP ingress: enabled: true hosts: - chaos.internal.example这里想展开讲两句。controllerManager 的资源请求不能给太小它要处理大量实验对象的 watch 和事件我在一个 200 节点规模的集群上曾经因为 requests 给满导致 controller 被抢占出现实验任务分配延迟。chaosDaemon 的nodeSelector是我自己加的一个护栏。第一轮上马时我不希望它覆盖整个集群而是先圈出两台业务量比较小的节点跑通了再逐步放开。等确认所有链路没问题再去掉这个约束也不迟。2.3 安装验证与 Dashboard 的访问安装完成后先看 Pod 状态kubectl get pods -n chaos-mesh正常情况下会看到三类 Podcontroller-manager、chaos-daemon、dashboard。如果 controller-manager 一直 CrashLoopBackOff先看日志如果是 chaos-daemon 一直 Pending大概率是节点调度或权限问题。再确认 CRD 是否注册成功kubectl get crd | grep chaos-mesh我会看到networkchaos.chaos-mesh.org、podchaos.chaos-mesh.org、stresschaos.chaos-mesh.org等一长串。只要这些 CRD 在实验对象就能被资源控制器正常理解。Dashboard 的访问方式可以根据团队习惯来定我在内网环境直接用 Ingress 暴露。第一次进去看一眼空列表会觉得没什么但当你开始批量创建实验它会变成一个很有价值的“现场图”哪些实验还在运行、注入对象是谁、剩余时长多少一眼扫过去就清楚。3. 从 150ms 网络延迟开始第一个 NetworkChaos 实验3.1 实验设计范围、时长、观察指标我建议每个团队第一次跑正式的混沌实验都从“网络延迟”而不是“杀 Pod”开始。原因很简单网络延迟是渐进式影响你有时间观察连接池、超时、重试等行为而杀 Pod 一上来就是硬中断信息量太大反而难定位。当时选定的目标是一个提供订单查询能力的服务有三个副本流量相对平稳。我先看了一眼监控系统里的 P99 延迟大约是 120ms然后设计一个注入 150ms 延迟、附加 30ms 抖动的实验理论上会让整体 P99 爬到 300ms 附近足够暴露问题又不至于把服务完全打挂。我特意把实验范围限制在一个命名空间和一组 label 里而不是全集群随机挑选。范围越小越容易归因。3.2 核心实验 YAML 解读下面这份 YAML 是我当时实际用的模板后来也成了团队内部的规范模板apiVersion: chaos-mesh.org/v1alpha1 kind: NetworkChaos metadata: name: order-api-delay-150ms spec: action: delay mode: all selector: namespaces: - prod-order labelSelectors: app: order-api delay: latency: 150ms jitter: 30ms correlation: 50 duration: 10m字段看起来不多但每个都值得讲清楚。mode: all意味着把这个命名空间下命中 label 的所有 Pod 都作为实验目标。如果只想固定打某一个副本就改成mode: one。对于有状态服务、单副本服务我强烈不建议用all因为那会造成全量不可用已经不是“演练”而是“事故”了。selector.namespaces一定要显式写不写就可能把实验对象扩大到整个集群。labelSelectors支持多个标签多个标签之间是 AND 关系。我曾见过有同事把 label 写成apporder但真实服务的 label 是apporder-api结果实验没打中目标大家还以为是平台不干活。delay.jitter是用来模拟真实网络波动的不能省。没有抖动的话流量延迟是一个固定值太“干净”和真实故障不同。correlation: 50控制延迟变化的时间相关性简单理解就是让后续采样点在一定程度上延续前一个延迟值避免完全随机跳动。3.3 实验过程中的观察与结束方式实验注入后我同时在监控系统里盯三组数据服务的 P99 延迟、后端连接池的活跃连接数、业务网关的重试次数。第一次跑的时候“重试次数”最先起了变化。我们发现虽然是同一个下游调用但不同客户端实例的重试策略并不一致有的重试 1 次有的重试 5 次导致下游服务在延迟上升时反而承压更大。这就是一个很典型的“混沌实验暴露设计不一致”的例子。如果不做延迟注入这组数据在正常流量下永远不会被注意。实验会自动结束因为 YAML 里写了duration: 10m。到点之后控制器会把注入规则清理掉网络延迟恢复原状。这里我特别想提醒一件事不要把duration省掉也不要图方便写成特别大的数。混沌实验最怕“失忆式”做法——实验建完就忘了几小时后整个链路还在被延迟吊着。如果希望手动控制结束时机宁可把时长设为 10 分钟然后专心观察也别让它无限期跑下去。4. 把实验推向关键链路Pod 被杀、资源压力与编排4.1 PodChaos验证多副本服务的自愈能力网络延迟验证的是“链路降级能力”Pod 被杀验证的则是“平台自愈能力”。我用 PodChaos 做过一轮针对多副本服务的下钻实验目标是确认 Kubernetes 的滚动调度和探针机制真的能扛住实例级故障。一份典型的杀 Pod 实验长这样apiVersion: chaos-mesh.org/v1alpha1 kind: PodChaos metadata: name: kill-order-api-one spec: action: kill mode: one selector: namespaces: - prod-order labelSelectors: app: order-api duration: 60saction: kill会直接结束容器进程。mode: one表示每次只杀一个 Pod如果实验时长内 Pod 控制器又拉起新副本Chaos Mesh 会持续执行“每轮只针对一个目标”的动作所以 60 秒里实际上会看到多个 Pod 被依次杀死。跑这个实验前我最关心的是就绪探针和优雅退出是否做得足够规范。如果服务不管 SIGTERM直接把自己卡死那即使 Kubelet 发出了终止信号容器也可能要等到强制 kill 才退出流量自然会被打断。实测中我们发现某个服务的优雅退出处理只有 5 秒而连接池里的存量请求在高峰时可能超过 10 秒于是每次 Pod 被杀时总有一小部分请求被强制断开。这个问题光看代码是看不出来的但杀一轮 Pod 就暴露得非常直白。后来我们把优雅退出时长调整到 30 秒再配合 preStop 里的 sleep情况明显改善。4.2 StressChaos把资源挤到临界点再观察资源类故障是混沌实验里容易被低估的一块。很多人觉得 CPU 高一点无关紧要但当 CPU 和内存同时被挤压很多服务的线程调度、GC 停顿、连接心跳都会连锁出问题。我用 StressChaos 做了一次内存压力实验apiVersion: chaos-mesh.org/v1alpha1 kind: StressChaos metadata: name: mem-stress-order-api spec: mode: one selector: namespaces: - prod-order labelSelectors: app: order-api stressors: memory: workers: 2 size: 512MB duration: 5m这个实验会让目标 Pod 额外占用 512MB 内存。如果 Pod 的内存 limit 比较紧它会开始触碰 OOM即使没有到限制GC 的频率也会显著上升从而观察到响应延迟的曲线变化。这里有个很关键的实践心得不要一上来就压 CPU 到 100%而是先用 50% 左右跑一次观察服务在这些资源占用下表现出的拐点。所谓拐点就是延迟开始非线性上涨的那一瞬间。生产环境不太可能突然给你一台“干净”的满负载机器更多是处于临界状态的毛刺所以压力实验的价值不是验证“扛不扛得住峰值”而是找出“哪个水位会导致行为变异”。4.3 Workflow 编排把多个实验串成一次演练单类实验跑得差不多之后我开始把目光投向 Chaos Mesh 的 Workflow 能力。它能把多个混沌动作编排成一个有先后顺序的流程非常适合做定期故障演练比如每周跑一个“网络延迟 杀 Pod 资源压力”的组合。一个简化版的 Workflow 长这样apiVersion: chaos-mesh.org/v1alpha1 kind: Workflow metadata: name: weekly-order-regression spec: entry: main templates: - name: main type: serial tasks: - network-delay - pod-kill - mem-stress - name: network-delay type: chaos deadline: 10m chaos: kind: NetworkChaos chaosSpec: action: delay mode: one selector: namespaces: - prod-order labelSelectors: app: order-api delay: latency: 150ms jitter: 30ms duration: 5m - name: pod-kill type: chaos deadline: 10m chaos: kind: PodChaos chaosSpec: action: kill mode: one selector: namespaces: - prod-order labelSelectors: app: order-api duration: 3m - name: mem-stress type: chaos deadline: 10m chaos: kind: StressChaos chaosSpec: mode: one selector: namespaces: - prod-order labelSelectors: app: order-api stressors: memory: workers: 1 size: 256MB duration: 3m每个 template 通过deadline控制整体最大执行时间。type: serial会让三个故障依次发生而不是同时压上来。这个编排的价值在于单一故障和组合故障引发的系统行为往往完全不同。先是网络变慢紧接着 Pod 被杀这和我们线上偶发的“节点亚健康”场景非常接近。在这里如果你不去研究故障编排混沌实验就永远是碎片化的只能回答“这个服务在某个单一故障下表现如何”而回答不了“这些故障叠加在一起时系统是否还能守住 SLA”。5. 实践下来最该记住的坑selector、网络注入和恢复机制5.1 selector 误伤排查一次覆盖范围错误混沌实验最可怕的事不是系统挂了而是它挂错了系统。我们曾经想对一个金丝雀版本的单副本服务做杀 Pod 实验当时 YAML 里 label 写的是version: canary。但因为历史上另一个团队的服务也沿用了这个 label实验一跑被杀的却是对方那组刚刚接流量的 Pod。那次之后我把 selector 的“预检”变成了团队强制动作。任何实验创建之前必须先执行一遍等价的 Pod 查询命令确认范围kubectl get pods -n prod-order -l apporder-api,versioncanary输出里必须能看到且只看到你预期的那组 Pod然后再创建 Chaos 资源。这个习惯救了我们很多次因为 YAML 里多一个空格、少一个命名空间人眼根本看不出来但查询结果会直接告诉你真相。5.2 网络注入失效的一种常见原因网络注入不像杀 Pod 那么“简单粗暴”它依赖 CHaos-daemon 在节点上注入数据包处理规则。正常情况下Chaos Mesh 会通过网络管理机构对象在目标 Pod 的虚拟网卡上设置延迟、丢包规则从而让数据包按预期被干扰。但我在某些环境里遇到过实验创建成功、Pod 选中成功却完全看不到延迟效果的情况。后来用调试工具查了 daemon 的日志发现它根本无法在目标容器网络命名空间里完成规则注入原因是当前节点的容器网络插件有一套独立的数据路径导致规则注入的位置和实际流量路径不一致。这类问题的通用排查路径是先用一个极低比例的实验确认注入是否生效比如 10ms 延迟、1% 丢包然后观察监控曲线有没有对应变化。如果没有别急着调大参数先看 daemon 日志、确认节点网络栈是否被正常操作。这是很多“实验无效”案例背后的真正原因。5.3 自动恢复与手动恢复的边界Chaos Mesh 的自动恢复机制依赖实验资源的duration时间一到控制器会主动清理注入动作。这听起来很省心但我建议大家还是要在建立实验时把 CR 名称、命名空间、结束时间一起记录下来。为什么因为现实中总会出现“到点没恢复”的情况。比如注入期间目标 Pod 正好发生重启控制器的清理动作可能因为对象状态变化而没有完全执行。又比如你用 Workflow 编排了多个实验中途某个流程失败剩余的清理逻辑不一定能覆盖所有残留规则。手动恢复的方式并不神秘找到对应的 CR直接删除即可。但你需要先确认故障资源和实验资源之间的对应关系。我的做法是在实验室命名空间里给每个实验打上统一的 annotation标记业务归属和创建人这样一旦需要紧急清理用一条kubectl delete podchaos -n prod-order -l owner...就能精准收尾。5.4 混沌实验前必须对齐的护栏经过几轮实验我给自己定了几条铁律也强烈建议沿用范围先收敛再放开第一轮实验永远选最小单副本或最低流量服务运行稳定后再向核心链路扩展。必须有明确的观测窗口实验前先确认监控系统能看到目标服务的延迟、错误率、饱和度和流量四条曲线否则实验做了也白做。自动恢复时长必须显式设置凡是能设置duration的实验一律不省略建议默认 5 到 10 分钟禁止使用无限时长。每个实验都要能回答一个问题你说不清“这个实验想验证什么”就不要创建它。混沌平台只是工具实验设计才是真正的门槛。保留现场实验结束后不要立刻删除所有 CR至少保留一段时间方便把实验时间点和监控曲线对齐否则复盘就会变成另一场猜谜。在实际操作中我也会把“实验申请-审批-执行-复盘”做成一条轻量流程让每次实验都有一条可以被追溯的记录。这并不复杂成本主要在于一开始把模板和护栏建立起来。最后再分享一个小技巧当你面对一个反复出现的间歇性故障与其靠猜不如直接在 Chaos Mesh 里构造一个“怀疑场景”跑一个短时实验试试水。很多时候混沌实验的意义不是马上修好问题而是帮你在十分钟内把“不可能”变成“可复现”这比任何压测工具都来得直接。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑