生产环境搞混沌实验,别拿线上用户当小白鼠
作者:Echo_Wish
做运维这些年,我发现一个挺有意思的现象:有些团队平时把高可用挂在嘴边,架构图上画着多副本、负载均衡、自动故障转移,监控面板也挺漂亮。可真到了某个 Pod 挂掉、网络突然抖动的时候,整个系统还是可能跟着趴窝。
为什么?
因为架构设计里写着“支持故障”,不代表系统真的经历过故障;平时看起来正常,也不代表异常发生时能够正常运行。
所以,混沌工程这件事,我一直觉得很有必要。
但这里有个前提:我们是为了验证系统能不能扛住故障,不是为了证明运维有多敢折腾。
尤其是在生产环境里,实验做得好,可以提前发现问题;实验做得不好,自己制造的故障就可能变成真正的生产事故。
今天就聊聊,怎么借助 Chaos Mesh 和 Litmus,在生产环境中安全地开展混沌实验,以及我认为必须守住的几条底线。
一、先搞清楚:混沌实验到底在验证什么?
先举个比较常见的例子。
一个订单服务部署了三个 Pod,前面挂着负载均衡。架构设计告诉我们,即使一个 Pod 挂掉,另外两个 Pod 也能继续处理请求。
听起来没什么问题。
但真实环境里,我们还有一堆需要验证的事情:
一个 Pod 被删除之后,流量能不能及时切走?服务发现需要多长时间才能感知变化?剩余两个 Pod 扛不扛得住突然增加的请求?连接池会不会被打满?下游数据库会不会跟着过载?
这些问题,光看 YAML 配置是得不到可靠答案的。
混沌实验的核心,不是制造故障,而是验证我们对系统的判断到底对不对。
比如,我们可以提出这样一个假设:
“如果订单服务的一个实例出现短暂网络延迟,剩余实例仍然能够正常处理请求,整体错误率不会超过预定阈值,核心业务不会中断。”
接下来,我们才有理由设计实验。
这里有三个关键点。
第一,故障必须明确。到底是增加网络延迟、删除一个 Pod,还是限制 CPU?一次实验最好只验证一个主要变量,否则多个故障叠加之后,出了问题很难判断真正原因。
第二,成功条件必须明确。不能实验结束后发现服务没挂,就宣布实验成功。我们还得看错误率、响应时间、请求成功率、队列积压,以及实际业务是否正常。
第三,实验必须有退出条件。一旦超出预设的风险边界,就应该停止注入故障,而不是想着“再观察几十秒,说不定就恢复了”。
举个例子,针对一个订单 API,可以在实验开始前约定:
- 请求成功率不能低于业务 SLO 要求。
- P95 响应时间不能持续超过预定上限。
- 错误率达到预设阈值时立即停止实验。
- 订单创建、支付状态等关键业务不能发生异常。
阈值具体设多少,需要根据业务 SLO、平时的基线和可接受的风险来定,而不是所有系统统一套一个数字。
这样一来,混沌实验就有了明确的目标,也有了可以判断成败的依据。
二、第一条底线:先控制影响范围,再谈故障注入
不少人第一次接触 Chaos Mesh,觉得最简单的实验就是删一个 Pod。
这确实容易理解,但这里有个很容易忽略的问题:你删掉的到底是哪一个 Pod?
如果选择范围配置得过宽,原本只是想测试订单服务的一个副本,结果把整个服务的 Pod 都纳入了实验范围,事情就完全变了。
Chaos Mesh 支持通过 Namespace、Label Selector 等选择器限制实验目标。在实验配置里,目标范围不是一个可有可无的字段,而是控制故障影响面的第一道防线。
假设我们要测试一个专门划分出来的订单服务金丝雀实例,第一步应该是确认目标:
kubectl get pods \
-n prod-canary \
-l app=checkout-canary \
--show-labels
这里的 prod-canary 和 checkout-canary 是示例名称,需要替换成真实环境中的命名空间和标签。
先看命令返回了哪些 Pod,再确认这些实例是否真的属于本次实验允许影响的范围。
别小看这一步。很多线上误操作,根本不是技术手段有问题,而是执行之前没有核对对象。
接下来,我们用 Chaos Mesh 模拟一次短暂的网络延迟。
apiVersion: chaos-mesh.org/v1alpha1
kind: NetworkChaos
metadata:
name: checkout-canary-delay
namespace: chaos-mesh
spec:
action: delay
mode: one
selector:
namespaces:
- prod-canary
labelSelectors:
app: checkout-canary
delay:
latency: "100ms"
duration: "30s"
这份配置表达的意思很直接:针对指定命名空间内符合标签条件的 Pod,选择其中一个目标,注入持续 30 秒的网络延迟,然后结束实验。
执行前,还需要确认当前 Chaos Mesh 版本支持相应字段,并确保标签选择结果符合预期。实际网络影响方向和范围,也要结合业务调用链确认。
我个人比较反感一种做法:先把故障注入进去,出问题再考虑怎么控制范围。
正确的顺序应该反过来:先限定对象,再设定故障强度,最后才是执行。
对于生产环境,建议优先选择专门的金丝雀实例、可隔离的流量路径和明确授权的命名空间。数据库、消息中间件、共享网络等关键基础设施,不应该因为测试一个普通应用,就顺手纳入实验范围。
尤其是有状态服务,删除实例和模拟轻微网络延迟的风险完全不是一个量级。不要拿破坏真实业务数据来验证所谓的系统韧性。
三、第二条底线:实验一定要有自动结束机制
做生产实验,最怕的不是故障注入成功,而是故障注入成功以后,没有按预期结束。
例如,你原本只想增加 30 秒网络延迟,结果忘记清理实验对象,影响一直持续,直到业务监控报警才发现问题。
这种错误听起来低级,却是生产自动化中必须认真防范的风险。
Chaos Mesh 官方文档明确说明,配置了 duration 的实验可以在指定时间后自动结束;暂停或者删除实验对象,也可以触发故障恢复。若没有设置 duration,故障可能会持续到实验被暂停或删除为止。
所以,生产实验里至少要有两层保障。
第一层是自动结束。
duration: "30s"
第二层是人工停止手段。
例如,针对前面创建的实验,可以使用:
kubectl annotate networkchaos checkout-canary-delay \
-n chaos-mesh \
experiment.chaos-mesh.org/pause=true \
--overwrite
这个命令用于暂停相应的 Chaos Mesh 实验,并触发故障恢复。执行前要确认对象名称、命名空间和当前工具版本的行为。
更重要的是,停止实验的权限和操作方式,必须在实验开始之前就准备好。
别等到线上已经开始报错,才发现自己没有对应集群的操作权限,也不知道哪个对象负责注入故障。
我建议每次生产实验至少确认这些事情:
- 自动结束时间已经配置,并经过预检查。
- 当班人员知道实验目标、实验时段和停止命令。
- 能够观察故障是否真正撤销,而不只是命令返回成功。
- 实验结束后,业务指标能够恢复到合理水平。
这里还有一个容易被误解的地方:故障注入停止了,不等于系统立即恢复正常。
例如,网络延迟消失以后,积压的消息可能还需要时间消费,失败请求可能触发重试,连接池也可能需要重新建立连接。
所以,结束实验之后仍然要观察恢复情况。真正的恢复标准,是业务回到预期状态,而不是故障对象消失了。
四、第三条底线:不要只看 Pod,要盯住业务 SLO
混沌实验中,我最不喜欢看到这样的结论:
“Pod 没有挂,实验通过。”
这句话几乎没有什么参考价值。
Pod 没有挂,可能是因为系统足够健壮,也可能是因为注入的故障太轻;接口还可以访问,也不意味着订单提交、库存扣减等关键业务没有问题。
所以实验的观察指标,必须与一开始提出的假设对应起来。
如果实验目标是验证网络延迟对订单服务的影响,就应该同时观察请求成功率、P95 响应时间、错误率和下游依赖的情况。如果目标是验证消费者故障恢复,就应该关注消息积压量、消费速度和重复处理情况。
更进一步,最好能够把这些检查自动化。
这也是 Litmus 比较值得利用的一项能力:它支持使用 HTTP、命令、Kubernetes 资源状态和 Prometheus 查询等 Probe,验证实验前后的系统状态,以及故障注入期间的指标表现。
例如,我们可以使用 Prometheus 指标检查订单服务的 5xx 错误率。
下面这段是 Litmus ChaosEngine 中的 Probe 配置片段,展示了如何在故障注入期间持续检查错误率,并在检查失败时请求停止故障执行。
probe:
- name: checkout-error-rate
type: promProbe
promProbe/inputs:
endpoint: "http://prometheus.monitoring:9090"
query: |
100 *
sum(rate(http_requests_total{
service="checkout",
status=~"5.."
}[2m]))
/
clamp_min(
sum(rate(http_requests_total{
service="checkout"
}[2m])),
0.001
)
comparator:
criteria: "<="
value: "1"
mode: "Continuous"
runProperties:
probeTimeout: 5s
interval: 5s
retry: 1
probePollingInterval: 5s
stopOnFailure: true
这里的 1 表示错误率不超过 1%。PromQL 中的指标名称和标签只是示例,必须根据自己系统的实际埋点进行调整。
另外,这个查询使用了分母下限来避免除零问题,但在没有请求流量时也可能得到看似正常的结果。因此,真实生产配置还应增加请求量或探测请求检查,防止把“没有流量”误判成“服务健康”。
值得特别说明的是,Litmus 的 stopOnFailure 可以配置在 Probe 的运行属性里,用于在检查失败后停止相应故障执行;Probe 结果也会参与实验结果判定。
不过,我们不能把它当成绝对可靠的保险。
如果 Prometheus 自己不可用、指标采集延迟,或者查询语句本身存在错误,自动判定机制也可能无法按预期工作。对高风险实验来说,指标检查必须配合独立的监控告警、人工停止流程和故障恢复验证。
这也是为什么我一直强调:实验工具负责执行故障,监控负责发现异常,值班人员负责决策和兜底。三者不能互相替代。
五、Chaos Mesh 和 Litmus,到底该怎么选?
这两个工具都能帮助我们开展混沌实验,但使用思路和能力侧重点有所不同。
Chaos Mesh:适合从具体故障入手
当我们希望快速验证一个 Pod 被删除、网络增加延迟、CPU 受到限制后,系统会发生什么,Chaos Mesh 的 Kubernetes 自定义资源和 YAML 配置就很方便。它还支持通过 Workflow 将多个实验步骤按顺序、并行或条件分支进行编排。
Litmus:适合把验证条件和实验流程结合起来
如果我们希望实验不仅注入故障,还持续检查 HTTP 可用性、Prometheus 指标、Kubernetes 资源状态,并形成可追踪的实验结果,那么 Litmus 的 ChaosEngine、Probe 和实验编排能力就有用武之地。
我的建议很简单,不用一开始就纠结哪个工具更高级。
如果团队刚刚准备做混沌工程,可以先选择一种工具,在测试环境里建立完整流程:提出假设、选择目标、注入故障、检查指标、停止实验、验证恢复。
流程跑通之后,再逐步引入生产环境。
如果你们已经有成熟的 Prometheus、业务 SLO 和自动化运维体系,那么就应该进一步考虑怎样把实验结果和这些已有能力结合起来,而不是只追求注入了多少种故障。
工具能帮助我们执行实验,但它不能替我们决定哪些风险值得承担。
六、真正的 guardrails,不是一句“出了问题立即回滚”
很多团队做技术方案的时候,都会写上一句:
“若出现异常,立即停止实验并恢复业务。”
乍一听很合理,可仔细想想,这句话其实没有多少可执行性。
什么叫异常?错误率上涨 0.1% 算不算?告警持续多久才算?谁来判断?谁有权限停止?恢复需要多长时间?如果停止故障以后业务还是没有恢复,接下来怎么办?
如果这些问题都没有答案,那所谓的 guardrails,大概率只是文档里的几行字。
我更倾向于把它理解成一组可以真正执行的保护措施。
第一,明确实验准入条件。 有未处理的生产事故、正在进行重大变更,或者业务正处于不可承受风险的高峰期时,原则上不启动普通的混沌实验。实验开始前,还应确认关键依赖和告警系统正常。
第二,限制影响半径。 从单个实例开始,优先使用金丝雀流量和明确的资源选择器。未经单独评审,不随意扩大到整个服务、整个节点或共享基础设施。
第三,设置自动停止条件。 将错误率、延迟、业务失败率等指标转化为明确阈值,并确保触发后存在有效的停止路径。不要只配置故障持续时间,还要有根据实际业务风险提前停止的机制。
第四,准备人工兜底。 指定实验负责人、当班观察人员和停止操作方式。对关键实验,可以先在演练环境中验证停止命令以及恢复流程。
第五,限制实验并发。 第一次生产实验尽量只改变一个主要变量。不要同时给订单服务增加网络延迟、给数据库施加压力,又把消息队列消费者停掉。那样即使出现事故,也很难从结果中得到清晰结论。
第六,实验结束必须复盘。 不只记录实验成功还是失败,还要记录假设、目标资源、实际影响、指标变化、停止原因和恢复耗时。如果发现了问题,就形成具体改进项,并在修复以后重新验证。
如果把这些要求落到团队流程里,我会在每次实验前检查下面这份清单。
生产混沌实验准入检查
0/6 项
实验假设、成功标准和停止阈值已经明确
目标命名空间、标签和实际 Pod 数量已经核对
自动结束时间已配置,人工停止方式已验证
业务 SLO、关键依赖和独立告警均可观测
实验负责人、观察人员和沟通渠道已经确认
实验结束后的业务恢复检查和复盘责任已经安排
需要强调的是,这份清单用于帮助团队检查准备情况,并不代表勾选完毕就能保证生产安全。对于业务影响范围大、恢复机制复杂的实验,仍然需要经过正式风险评审。
七、别急着自动化,先证明你的停止机制真的有用
最后说一个我认为特别重要的观点。
很多团队一开始做混沌工程,就想着把各种故障做成定时任务,每周自动执行,甚至尝试一次注入多种故障。
我觉得这个顺序有点反了。
自动化的价值,是把已经验证过的流程稳定地重复执行,而不是把尚未验证的风险变成一个定时炸弹。
Chaos Mesh 提供了实验调度和 Workflow 编排能力,可以帮助团队把经过验证的操作串联起来。
Chaos Mesh
+1
但能配置自动化,不代表现在就应该打开自动化。
我的建议是分阶段推进。
第一阶段,在非生产环境验证故障注入和恢复逻辑,检查实验范围是不是准确,故障是否能够按预期撤销。
第二阶段,在生产环境选择可隔离的金丝雀实例,进行短时间、低强度、单变量的实验,同时由人员实时观察。
第三阶段,复盘实验结果,修复发现的问题。只有当停止机制、告警和恢复流程都被实际验证过,才考虑扩大实验范围。
第四阶段,再把稳定的实验纳入自动化流程,逐步建立实验模板、准入条件、执行记录和周期性复验机制。
还有一点经常被忽略:实验执行成功,不代表系统韧性合格;实验发现问题,也不等于实验失败。
假如一次网络延迟实验发现,订单服务在某个时间点会出现大量重试,继而拖垮数据库连接池,那么这次实验的价值就很大。它帮助我们在真正的网络故障发生之前,发现了系统设计中的薄弱环节。
相反,如果每次实验都只关注工具是否成功注入故障、实验对象是否按时恢复,却不跟进实际发现的问题,那么即使每周运行一百次混沌实验,也未必能提升多少系统韧性。
我始终认为,混沌工程真正考验的不是运维人员制造故障的能力,而是团队对风险的理解、对系统行为的预判,以及在异常发生时把事情控制住的能力。
生产环境不是不能做实验,但绝不能为了验证高可用,反过来牺牲高可用。
先让实验可控,再让实验有价值,最后才让实验自动化。
这才是我理解的生产混沌实验应该走的路。