DigitalOcean Kubernetes混沌工程实战:用ChaosMesh精准验证NVMe与网络亚健康
1. 为什么在 DigitalOcean 上跑 Kubernetes 不能只靠“祈祷”——从一次凌晨三点的告警说起
凌晨三点十七分,手机震了三下。不是微信消息,是 PagerDuty 的红色告警:
core-api-service: Latency > 2s (99th percentile)
,紧接着是
payment-gateway: Connection refused
。我抓起电脑,连上跳板机,
kubectl get pods -n production
一敲——所有 Pod 状态都是
Running
,
kubectl get nodes
显示三台 Droplet 全部
Ready
。监控图表上 CPU、内存、网络带宽都平滑得像湖面。可用户订单正在失败,支付链路彻底卡死。
查了四十分钟,最终发现是其中一台 worker 节点的磁盘 I/O 队列深度在凌晨两点四十五分开始持续飙升到 200+,而 Prometheus 没配 I/O wait 的告警阈值,Kubernetes 自身也根本不感知底层块设备的“假死”状态——它只看 kubelet 心跳和 Pod 进程是否存在。那个节点上的容器进程全活着,但所有读写请求都在内核队列里排队,直到超时。我们花了六小时回溯日志、复现路径、打补丁,最后才确认:是 DigitalOcean 的 NVMe SSD 在高负载下触发了某次固件静默降速(firmware throttling),而这个行为完全不报错、不中断、不触发任何标准健康检查。
这件事之后,我删掉了所有“集群稳定运行中”的 Slack 状态。真正的稳定性,不是所有组件都显示绿色,而是当某个组件以一种你从未预料的方式悄悄变慢、变卡、变不可靠时,你的系统依然能扛住、降级、自愈。这就是混沌工程存在的根本理由——它不测试“能不能跑”,而测试“崩成什么样时还能活”。ChaosMesh 不是玩具,它是我在 DigitalOcean Kubernetes 集群上部署的第一道“压力探针”,也是唯一能提前半年发现上述 NVMe 固件问题的工具。它不依赖厂商文档里的“理论 SLA”,只认真实硬件在真实流量下的反应。如果你还在用
kubectl rollout restart
当“重启大法”来验证高可用,那你离下一次凌晨三点的告警,只差一次未被模拟的磁盘抖动。
2. ChaosMesh 在 DigitalOcean 环境中的不可替代性:不是“能用”,而是“必须用”
很多人第一次接触 ChaosMesh,会把它当成一个高级版的
kill -9
工具——不就是随机杀 Pod 吗?DigitalOcean 的 Kubernetes 服务(DOKS)本身已经提供了自动节点恢复、Pod 水平伸缩、托管 etcd 备份,为什么还要额外引入一个“制造故障”的系统?这个问题的答案,藏在 DigitalOcean 的基础设施抽象层级里。
DOKS 是一个托管 Kubernetes 控制平面的服务,但它
不托管你的工作节点(worker nodes)的底层硬件行为
。它保证
kube-apiserver
可用,但不保证你选的
s-2vcpu-4gb
Droplet 上的 NVMe SSD 在 80% I/O 压力下会不会固件降速;它保证
kubelet
能上报心跳,但不保证该节点的网络驱动在内核更新后会不会出现微秒级的 TX 队列锁死;它提供“自动修复”功能,但其触发条件仅限于节点
NotReady
或
kubelet
进程崩溃——而上面提到的 I/O 队列深度飙升,节点状态全程
Ready
,
kubelet
进程 PID 甚至没变过。这种“亚健康”状态,正是生产环境故障的温床,也是传统监控和 Kubernetes 健康检查的盲区。
ChaosMesh 的价值,恰恰在于它能精准刺入这个盲区。它不是在应用层模拟错误(比如 HTTP 503),而是在 基础设施与 Kubernetes 的交界处注入可控扰动 。例如:
-
NetworkChaos可以对特定 Pod 的 egress 流量注入netem规则,模拟 DigitalOcean 数据中心内部网络的微突发丢包(1% 丢包 + 50ms 延迟抖动),这比单纯curl -I测试更能暴露服务间调用的脆弱性; -
IOChaos直接在目标 Pod 的容器根文件系统层挂载fio模拟高 I/O 压力,触发真实的磁盘队列堆积,从而提前验证你的监控是否配置了node_disk_io_time_weighted_seconds_total的 P99 阈值; -
StressChaos在指定 Pod 内启动stress-ng,让 CPU 使用率虚假拉满(但不真正计算),专门用来检验你的 HPA 是否被错误指标误导——DigitalOcean 的top和htop在容器内看到的 CPU 利用率,和cgroup统计的cpuacct.usage_percpu并不总是一致。
更重要的是,ChaosMesh 的实验定义是
Kubernetes Native 的
。一个
NetworkChaos
对象,其
spec.selector
可以精确匹配到由
app.kubernetes.io/instance: payment-gateway
标签标识的 Pod,而这个标签,正是你用
helm install payment-gateway ...
部署时自动生成的。这意味着你的混沌实验可以和 CI/CD 流水线深度集成:每次 Helm Chart 版本发布前,自动运行一组基础网络扰动实验;每次 Node Pool 升级后,自动对新节点上的所有关键服务执行 IO 压力测试。这种“基础设施即代码”式的可靠性验证,是任何外部脚本或手动
kubectl exec
都无法提供的确定性。
提示:在 DigitalOcean 上,务必关闭 DOKS 控制台里的 “Automatically repair unhealthy nodes” 功能,否则 ChaosMesh 注入的
PodChaos(如pod-failure)可能被平台误判为节点故障,触发不必要的节点重建,干扰实验结果。真正的可靠性验证,需要你亲手控制“坏”的节奏,而不是交给平台自动擦屁股。
3. 从零部署 ChaosMesh 到 DigitalOcean Kubernetes:绕开三个最坑的“默认值”
ChaosMesh 官方文档推荐使用 Helm 安装,这没错。但在 DigitalOcean 环境下,直接
helm install chaos-mesh chaos-mesh/chaos-mesh --namespace=chaos-testing --create-namespace
会立刻掉进三个深坑。我踩过两次,第二次重装时把每个参数都记在了笔记本上。下面是你必须手动覆盖的三个核心配置项,以及它们背后的物理意义。
3.1 镜像仓库必须显式指定为
ghcr.io/chaos-mesh/chaos-mesh
DigitalOcean 的默认镜像拉取策略是
IfNotPresent
,而 ChaosMesh 的 Helm Chart 默认使用的镜像是
quay.io/chaos-mesh/chaos-mesh:v2.6.1
。问题在于,Quay.io 在亚太地区的 CDN 节点响应极不稳定,尤其是在新加坡(sgp1)或班加罗尔(blr1)区域创建的 DOKS 集群。
kubectl get pods -n chaos-testing
会看到
chaos-controller-manager
Pod 卡在
ImagePullBackOff
,
Events
里全是
Failed to pull image "quay.io/...": rpc error: code = Unknown desc = failed to pull and unpack image "quay.io/...": failed to resolve reference "quay.io/...": failed to do request: Head "https://quay.io/v2/...": dial tcp 34.227.176.111:443: i/o timeout
。
解决方案极其简单,但官方文档没强调:强制使用 GitHub Container Registry(GHCR),它的全球分发更可靠。执行以下命令:
helm repo add chaos-mesh https://charts.chaos-mesh.org
helm repo update
helm install chaos-mesh chaos-mesh/chaos-mesh \
--namespace=chaos-testing \
--create-namespace \
--set chaosDaemon.runtime=containerd \
--set chaosDaemon.socketPath=/run/containerd/containerd.sock \
--set dashboard.create=true \
--set dashboard.ingress.enabled=true \
--set dashboard.ingress.hosts[0].host=chaos-dashboard.yourdomain.com \
--set chaosControllerManager.image.registry=ghcr.io \
--set chaosControllerManager.image.repository=chaos-mesh/chaos-mesh \
--set chaosControllerManager.image.tag=v2.6.1 \
--set chaosDaemon.image.registry=ghcr.io \
--set chaosDaemon.image.repository=chaos-mesh/chaos-mesh \
--set chaosDaemon.image.tag=v2.6.1 \
--set dnsServer.image.registry=ghcr.io \
--set dnsServer.image.repository=chaos-mesh/chaos-mesh \
--set dnsServer.image.tag=v2.6.1
注意
--set chaosDaemon.runtime=containerd
和
--set chaosDaemon.socketPath=/run/containerd/containerd.sock
这两行。DigitalOcean 的 DOKS 底层使用 containerd 作为 CRI,而非 Docker Engine。如果你漏掉这两项,
chaos-daemon
DaemonSet 会在每个节点上尝试连接
/var/run/docker.sock
,而这个 socket 根本不存在,导致所有混沌实验(尤其是
NetworkChaos
和
IOChaos
)全部静默失败——Pod 状态一切正常,但网络规则压根没注入。
3.2 Dashboard Ingress 必须配置 TLS 并指向正确的 LoadBalancer IP
ChaosMesh Dashboard 默认通过 Ingress 暴露,而 DOKS 的 Ingress Controller(NGINX)需要你手动创建一个
Service
类型为
LoadBalancer
的入口。但直接
kubectl apply -f dashboard-ingress.yaml
会遇到一个经典问题:Ingress 的
spec.rules[0].host
指向了一个域名,而 DigitalOcean 的 LoadBalancer Service 创建后,分配的公网 IP 是动态的,DNS 解析需要时间。更糟的是,如果你的域名 DNS 是 Cloudflare 代理模式,那么 ChaosMesh Dashboard 的 WebSocket 连接(用于实时实验状态推送)会被 Cloudflare 中断,页面永远显示“Connecting...”。
我的实操方案是: 放弃域名,直接用 LoadBalancer 的 IP + NodePort 访问 。首先,获取 LoadBalancer 的 IP:
kubectl get service -n chaos-testing chaos-dashboard -o jsonpath='{.status.loadBalancer.ingress[0].ip}'
# 输出类似:159.203.123.45
然后,编辑 Dashboard Service,将
type: LoadBalancer
改为
type: NodePort
,并记录其
nodePort
(通常是 30080):
kubectl edit service -n chaos-testing chaos-dashboard
# 修改 spec.type 为 NodePort,并确保 spec.ports[0].nodePort 设为 30080
最后,在浏览器中直接访问
http://<YOUR_DROPLET_PUBLIC_IP>:30080
。这里
<YOUR_DROPLET_PUBLIC_IP>
是你任意一台 worker 节点的公网 IP(不是 LoadBalancer IP)。因为
NodePort
会将端口映射到所有节点,所以只要防火墙放行
30080
,你就能绕过所有 DNS 和 Ingress 的复杂性,直连 Dashboard。这个方案在测试和预发环境足够健壮,且避免了 TLS 证书管理的麻烦。
3.3 RBAC 权限必须扩展以支持
NetworkChaos
的 eBPF 模式
这是最隐蔽的坑。ChaosMesh 的
NetworkChaos
实验有两种模式:
tc
(Traffic Control)和
eBPF
。
tc
模式需要在宿主机上执行
tc qdisc
命令,这要求
chaos-daemon
容器拥有
CAP_NET_ADMIN
权限;而
eBPF
模式则需要加载 eBPF 程序,这要求
chaos-daemon
拥有
CAP_SYS_ADMIN
权限,并且内核版本 >= 5.8(DigitalOcean 的 Ubuntu 22.04 默认内核是 5.15,满足条件)。
Helm Chart 默认只授予
CAP_NET_ADMIN
,所以当你创建一个
NetworkChaos
对象时,如果
spec.mode
是
eBPF
(这是推荐模式,性能更好、影响更小),
chaos-controller-manager
日志里会出现
failed to inject network chaos: no such file or directory
的错误,而实验状态却显示
Running
——它根本没生效。
解决方法是,在 Helm 安装时,显式添加
--set chaosDaemon.securityContext.capabilities.add[0]=SYS_ADMIN
:
helm upgrade chaos-mesh chaos-mesh/chaos-mesh \
--namespace=chaos-testing \
--set chaosDaemon.securityContext.capabilities.add[0]=SYS_ADMIN \
--reuse-values
--reuse-values
确保其他参数不变。这个
SYS_ADMIN
权限是
eBPF
模式加载程序所必需的,没有它,
NetworkChaos
就是聋子的耳朵——摆设。你可以用一个简单的实验验证是否生效:
apiVersion: chaos-mesh.org/v1alpha1
kind: NetworkChaos
metadata:
name: test-ebpf
namespace: chaos-testing
spec:
action: delay
mode: one
value: ""
selector:
namespaces:
- default
delay:
latency: "100ms"
duration: "30s"
scheduler:
cron: "@every 1m"
创建后,进入目标 Pod 执行
tc qdisc show dev eth0
,如果看到
qdisc tbf 8001: root refcnt 2 rate 1000Mbit burst 1250000b lat 100.0ms
,说明
tc
模式生效;如果看到
qdisc clsact ffff: root
,说明
eBPF
模式已接管。后者才是 DigitalOcean 环境下推荐的低开销模式。
4. 面向 DigitalOcean 特性的四大核心实验设计:不止于“杀 Pod”
很多团队把混沌工程等同于“随机杀服务”,这就像把外科手术等同于“拿刀乱捅”。在 DigitalOcean 的具体环境中,我们必须设计能击中其基础设施特性的实验。以下是我在生产集群中长期运行的四个核心实验,每一个都对应一个 DigitalOcean 用户高频遇到的真实故障场景。
4.1 实验一:
IOChaos
模拟 NVMe SSD 固件降速——专治“磁盘慢得离谱但监控全绿”
故障背景
:DigitalOcean 的 NVMe SSD 性能卓越,但其固件在持续高 I/O 压力(尤其是大量小文件随机写)下,会触发热节流(thermal throttling)或写缓存刷新延迟(write cache flush delay)。此时
iostat -x 1
显示
await
(平均 I/O 等待时间)飙升至 200ms+,
%util
接近 100%,但
kubectl top nodes
显示磁盘使用率(
diskio.io_service_bytes_recursive
)可能只有 30%,
Prometheus node_exporter
的
node_filesystem_usage
也远未爆满。监控告警全无,但数据库查询慢得像幻灯片。
实验设计 :
apiVersion: chaos-mesh.org/v1alpha1
kind: IOChaos
metadata:
name: nvme-throttling-sim
namespace: chaos-testing
spec:
action: latency
mode: all
selector:
labelSelectors:
"app.kubernetes.io/component": "database"
volumePath: "/var/lib/postgresql/data"
latency:
time: "150ms"
correlation: "100"
percent: 100
duration: "5m"
scheduler:
cron: "@every 24h"
关键参数解析 :
-
volumePath: "/var/lib/postgresql/data":必须精确指向数据库 Pod 挂载的持久卷(PersistentVolume)的实际路径。在 DigitalOcean 的 Block Storage 上,这通常是/dev/disk/by-id/scsi-0DO_Volume_your-pv-name挂载到容器内的路径。 -
latency.time: "150ms":不是模拟“磁盘坏了”,而是模拟“磁盘变慢了”。150ms 是 NVMe 降速后的典型await值,足以让 PostgreSQL 的shared_buffers缓存失效,迫使大量查询走磁盘。 -
correlation: "100":表示每次 I/O 请求都施加延迟,不随机跳过。这是为了最大化复现“持续慢”的效果,而非偶发抖动。 -
scheduler.cron: "@every 24h":每天凌晨 2 点执行,避开业务高峰,且频率足够低,避免对存储造成永久性磨损。
验证方式
:实验运行中,登录数据库 Pod,执行
pg_isready -h localhost -U postgres
,观察返回时间是否从 <5ms 变为 >100ms;同时在 Grafana 查看
node_disk_io_time_weighted_seconds_total{device=~"sd.*|nvme.*"}
的 P99 值是否同步飙升。如果两者同步变化,说明实验精准命中了目标。
4.2 实验二:
NetworkChaos
模拟数据中心内部网络微突发——专治“跨 AZ 调用超时”
故障背景
:DigitalOcean 的多可用区(AZ)部署(如 sgp1、sgp2、sgp3)之间通过私有骨干网互联。这条网络并非“绝对可靠”。我们曾观测到,在 sgp1 的 API 网关调用 sgp2 的用户服务时,P99 延迟从 80ms 突然跳到 1200ms,持续 3 分钟,随后自动恢复。抓包发现是中间网络设备出现了微秒级的队列溢出(micro-burst),导致 TCP 重传。
ping
和
mtr
完全测不出,因为 ICMP 包太小,而业务流量是 HTTPS 大包。
实验设计 :
apiVersion: chaos-mesh.org/v1alpha1
kind: NetworkChaos
metadata:
name: az-microburst
namespace: chaos-testing
spec:
action: loss
mode: all
selector:
labelSelectors:
"app.kubernetes.io/name": "user-service"
direction: to
loss:
loss: "1.5%"
correlation: "95"
jitter: "20ms"
duration: "2m"
scheduler:
cron: "@every 6h"
关键参数解析 :
-
direction: to:只对流入user-servicePod 的流量注入丢包。因为故障是“调用方收不到响应”,根源在服务端接收路径。 -
loss: "1.5%":1.5% 的丢包率是微突发的典型特征。低于 0.5% 很难触发 TCP 重传,高于 3% 则会引发连接重置,偏离“微突发”场景。 -
correlation: "95":高相关性意味着丢包不是均匀分布,而是集中在短时间窗口内(如 100ms 内连续丢 5 个包),完美模拟微突发。 -
jitter: "20ms":在丢包基础上增加 20ms 的延迟抖动,模拟网络设备队列深度波动。
验证方式
:在 API 网关 Pod 内,用
hey -z 5m -q 100 -c 50 https://user-service.default.svc.cluster.local/api/users/123
持续压测。实验开启前,
hey
报告的
Latency distribution
中 99th percentile 应 <100ms;实验开启后,应能看到 99th percentile 瞬间跳至 1000ms+,且
Error distribution
中出现
error: read tcp ...: i/o timeout
。这证明你的服务调用链路对微突发是脆弱的,需要增加客户端重试逻辑或调整 TCP keepalive 参数。
4.3 实验三:
StressChaos
模拟 CPU 资源争抢——专治“节点 CPU 100% 但 Pod 没被驱逐”
故障背景
:DigitalOcean 的 Droplet 是共享宿主机资源的。当你在一个
s-4vcpu-8gb
节点上部署了 10 个 CPU limit 为
500m
的 Pod 时,理论上最多用掉 5 个 vCPU。但如果其中一个 Pod 因 bug 进入无限循环(
while true; do :; done
),它的 CPU usage 会瞬间冲到
4000m
(4 个 vCPU),导致同节点上其他 Pod 的调度严重延迟,
kubectl top pods
显示其 CPU usage 为
0m
(因为 cgroup 统计被抢占),但实际业务请求大量超时。Kubernetes 的
kubelet
驱逐机制(
--eviction-hard=memory.available<500Mi,nodefs.available<10%,nodefs.inodesFree<5%
)默认不包含 CPU,所以这个“CPU 虫子”可以肆虐数小时。
实验设计 :
apiVersion: chaos-mesh.org/v1alpha1
kind: StressChaos
metadata:
name: cpu-bug-sim
namespace: chaos-testing
spec:
mode: one
selector:
labelSelectors:
"app.kubernetes.io/name": "legacy-worker"
stressors:
cpu:
workers: 4
load: 100
duration: "3m"
scheduler:
cron: "@every 12h"
关键参数解析 :
-
workers: 4:启动 4 个stress-ng --cpu 1进程,精准占用 4 个 vCPU,模拟一个 Pod 吃满整个节点。 -
load: 100:100% 的 CPU 占用率,不是 50% 或 80%,要达到“彻底饿死邻居”的效果。 -
mode: one:只在匹配到的一个 Pod 上执行。这样可以隔离影响,避免整个节点瘫痪。
验证方式
:实验运行中,
kubectl top nodes
会显示该节点的 CPU usage 为
4000m/4000m
;同时,
kubectl describe node <node-name>
的
Allocated resources
下,
cpu
一栏会显示
4000m
(100%)。此时,观察同节点上其他 Pod 的
kubectl logs
,应能看到大量
context deadline exceeded
错误。这证明你的应用缺乏对同节点资源争抢的防御能力,需要在 Deployment 中设置合理的
resources.requests
和
resources.limits
,并启用
HorizontalPodAutoscaler
基于 CPU usage 的扩缩容。
4.4 实验四:
PodChaos
模拟
kubelet
心跳丢失——专治“节点 Ready 但服务不可用”
故障背景
:这是 DigitalOcean 上最诡异的故障之一。某次内核更新后,
kubelet
进程仍在运行,
systemctl status kubelet
显示 active,
kubectl get nodes
显示
Ready
,但
kubelet
向
apiserver
发送的心跳(
NodeStatus
更新)因 TLS 证书过期或网络策略变更而静默失败。结果是:
kubectl get pods
能列出所有 Pod,但
kubectl exec
、
kubectl logs
全部超时,
kubectl port-forward
无法建立隧道。集群“看起来”完好,实则完全丧失运维能力。
实验设计 :
apiVersion: chaos-mesh.org/v1alpha1
kind: PodChaos
metadata:
name: kubelet-heartbeat-loss
namespace: chaos-testing
spec:
action: pod-failure
mode: one
selector:
labelSelectors:
"component": "kube-apiserver"
duration: "90s"
scheduler:
cron: "@every 48h"
关键参数解析 :
-
selector.labelSelectors.component: "kube-apiserver":直接针对kube-apiserverPod。这是 DOKS 托管的控制平面组件,我们无法直接操作其节点,但可以通过 ChaosMesh 对其 Pod 注入故障。 -
action: pod-failure:让kube-apiserverPod 进入CrashLoopBackOff状态,模拟其与kubelet的通信中断。注意,这不是杀死kubelet,而是杀死apiserver,因为kubelet的心跳是发给apiserver的,apiserver不可用,kubelet的状态就无法被集群感知。 -
duration: "90s":90 秒是 DOKS 自动恢复kube-apiserver的典型时间。短于 60s 可能来不及触发恢复,长于 120s 则可能影响集群稳定性。
验证方式
:实验开启后,立即执行
kubectl get componentstatuses
,应看到
controller-manager
和
scheduler
状态变为
Unknown
;执行
kubectl get nodes
,节点状态可能短暂变为
NotReady
,然后在 90 秒内自动恢复为
Ready
。这验证了 DOKS 的控制平面自愈能力。更重要的是,你要观察你的应用是否能在
apiserver
中断期间,通过本地缓存(如
k8s.io/client-go
的
Informer
)继续提供只读服务,或者是否有降级开关(fallback)。
5. 实验结果分析与可靠性度量:如何把“混沌”变成可量化的 SLO
部署 ChaosMesh 不是为了看花里胡哨的仪表盘,而是为了回答一个终极问题: 当 X 故障发生时,Y 业务指标的 P99 延迟是否会超过 Z 毫秒? 如果答案是“会”,那你的 SLO(Service Level Objective)就名存实亡。因此,每一次混沌实验,都必须与你的核心业务指标绑定。在 DigitalOcean 环境下,我建立了三层度量体系。
5.1 第一层:基础设施层 —— 验证 DOKS 的“承诺”是否兑现
这一层的目标是验证 DigitalOcean 的托管服务 SLA。我们不关心业务,只关心平台本身。关键指标包括:
| 指标名称 | 查询 PromQL | 预期阈值 | 实验关联 |
|---|---|---|---|
sum(rate(kube_node_status_condition{condition="Ready",status="true"}[5m])) by (node)
|
sum(rate(kube_node_status_condition{condition="Ready",status="true"}[5m])) by (node)
| > 0.999 (99.9%) |
PodChaos
模拟
kube-apiserver
故障后,节点
Ready
状态恢复时间
|
avg_over_time(node_network_receive_bytes_total{device=~"eth.*"}[1h])
|
avg_over_time(node_network_receive_bytes_total{device=~"eth.*"}[1h])
| 波动 < ±15% |
NetworkChaos
注入丢包后,网络吞吐是否稳定
|
max(node_disk_io_time_weighted_seconds_total{device=~"nvme.*"})
|
max(node_disk_io_time_weighted_seconds_total{device=~"nvme.*"})
| < 50ms (P95) |
IOChaos
注入延迟后,磁盘 I/O 时间是否可控
|
这些指标全部来自
node_exporter
和
kube-state-metrics
,它们是 DOKS 开箱即用的监控组件。实验报告中,必须截图对比“实验前 5 分钟”和“实验中 5 分钟”的指标曲线。如果
node_disk_io_time_weighted_seconds_total
在
IOChaos
开启后,P95 值从 25ms 跳到 200ms,那就证明你的数据库 Pod 所在的 NVMe 卷,其 I/O 性能边界就是 200ms,所有依赖该卷的服务,其 P99 延迟 SLO 就不能定为 100ms。
5.2 第二层:平台层 —— 验证 Kubernetes 原生能力的鲁棒性
这一层关注 Kubernetes 自身的编排和调度能力。我们假设基础设施是“可信”的,测试 Kubernetes 如何应对组件故障。关键指标包括:
-
Pod 启动时间
:
histogram_quantile(0.95, sum(rate(container_cpu_usage_seconds_total{container!="",namespace=~"default|production"}[5m])) by (le, namespace, container))。在PodChaos杀死一个关键 Deployment 的所有 Pod 后,新 Pod 的container_cpu_usage_seconds_total从 0 上升到稳定值的时间,应 < 30 秒。如果超过 60 秒,说明你的initContainer或livenessProbe配置有问题。 -
HPA 扩容延迟
:
kube_hpa_status_current_replicas{hpa="your-hpa-name"}。在StressChaos注入 CPU 压力后,HPA 从 2 副本扩容到 4 副本的时间,应 < 2 分钟。如果延迟,检查metrics-server的抓取间隔和horizontal-pod-autoscaler-sync-period参数。 -
Service Endpoints 同步
:
count(kube_endpoint_address_available{endpoint="your-service"} == 1)。在NetworkChaos导致部分 Pod 网络不可达时,Endpoints对象中的available地址数应实时减少,且kube-proxyiptables 规则应同步更新,剔除不可达地址。用iptables -t nat -L KUBE-SVC-* | grep DROP可验证。
5.3 第三层:业务层 —— 验证你的代码能否在混沌中存活
这是最重要的一层,也是最容易被忽视的。它不看 Kubernetes,只看你自己的代码。我强制要求所有新上线的服务,必须通过以下三个业务指标的混沌测试:
-
支付成功率(Payment Success Rate)
:在
NetworkChaos模拟跨 AZ 丢包时,支付网关的http_request_duration_seconds_count{code=~"2..",handler="pay"}与http_request_duration_seconds_count{code=~"5..",handler="pay"}的比值,P95 应 > 99.5%。如果跌到 95%,说明你的支付 SDK 缺少重试或熔断。 -
搜索首屏时间(Search FCP)
:在
StressChaos注入 CPU 压力时,前端监控上报的performance.getEntriesByType("navigation")[0].domContentLoadedEventEnd,P95 应 < 2000ms。如果超时,说明你的 SSR 渲染逻辑存在 CPU 密集型阻塞。 -
库存扣减一致性(Inventory Consistency)
:在
PodChaos随机杀死库存服务 Pod 时,执行 1000 次并发扣减请求(POST /inventory/deduct),最终库存余额应等于初始值减去 1000。如果出现负数或不一致,说明你的分布式锁或数据库事务隔离级别配置错误。
每一次混沌实验的最终报告,必须包含这三层指标的对比表格。例如,一次
IOChaos
实验的结论可能是:“NVMe 降速至 150ms 时,支付成功率从 99.98% 降至 99.2%,不满足 SLO;根本原因是支付 SDK 的数据库连接池最大连接数(maxOpen=10)过小,导致连接等待超时。建议提升至 maxOpen=50,并增加连接超时重试。”
注意:所有实验必须在非生产环境(staging)先行运行至少一周,收集基线数据。切勿在生产环境首次运行未经验证的混沌实验。我的原则是:先用
--dry-run模式生成实验 YAML,再用kubectl create -f手动创建,全程人工审核每一条selector和duration。
6. 我的 DigitalOcean 混沌工程实践清单:一份可直接打印贴在显示器边上的备忘录
经过两年在 DigitalOcean Kubernetes 上的混沌工程实战,我总结了一份极简但致命的实践清单。它不是理论,而是我每天打开终端时,脑子里自动浮现的 checklist。你可以把它打印出来,贴在显示器边框上,或者存为
~/chaos-checklist.md
。
【部署前必做】
-
✅ 确认 DOKS 集群的 Kubernetes 版本 ≥ v1.24(ChaosMesh v2.6+ 要求),执行
doctl kubernetes cluster get your-cluster-name --format Version。 -
✅ 确认所有 worker 节点的操作系统是 Ubuntu 22.04 LTS(内核 5.15),执行
kubectl get nodes -o wide查看OS-IMAGE。 - ✅ 关闭 DOKS 控制台的 “Automatically repair unhealthy nodes” 开关,避免干扰实验。
-
✅ 为
chaos-testing命名空间单独创建 ResourceQuota,限制其 CPU/Memory 总用量,防止混沌实验本身耗尽集群资源。
【实验设计必做】
-
✅ 每个
NetworkChaos实验,selector必须精确到labelSelectors,禁用mode: all(除非你真想搞垮整个集群)。 -
✅ 每个
IOChaos实验,volumePath必须是 Pod 内实际挂载的路径,可通过kubectl exec -it <pod-name> -- mount | grep "on /"获取。 -
✅ 每个
StressChaos实验,workers数量必须 ≤ 该 Pod 的resources.limits.cpu,避免触发cgroupOOM Killer。 -
✅ 所有实验的
duration必须 ≤scheduler.cron间隔的 1/3,例如cron: "@every 6h",则duration最长设为120m,留出缓冲时间。
【实验运行必做】
- ✅ 实验开始前 5 分钟,手动保存当前所有核心指标的 Grafana 快照(Dashboard → Share → Direct link to snapshot)。
- ✅ 实验进行中,每 30 秒
更多推荐

所有评论(0)