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-service Pod 的流量注入丢包。因为故障是“调用方收不到响应”,根源在服务端接收路径。
  • 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-apiserver Pod。这是 DOKS 托管的控制平面组件,我们无法直接操作其节点,但可以通过 ChaosMesh 对其 Pod 注入故障。
  • action: pod-failure :让 kube-apiserver Pod 进入 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-proxy iptables 规则应同步更新,剔除不可达地址。用 iptables -t nat -L KUBE-SVC-* | grep DROP 可验证。

5.3 第三层:业务层 —— 验证你的代码能否在混沌中存活

这是最重要的一层,也是最容易被忽视的。它不看 Kubernetes,只看你自己的代码。我强制要求所有新上线的服务,必须通过以下三个业务指标的混沌测试:

  1. 支付成功率(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 缺少重试或熔断。
  2. 搜索首屏时间(Search FCP) :在 StressChaos 注入 CPU 压力时,前端监控上报的 performance.getEntriesByType("navigation")[0].domContentLoadedEventEnd ,P95 应 < 2000ms。如果超时,说明你的 SSR 渲染逻辑存在 CPU 密集型阻塞。
  3. 库存扣减一致性(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 ,避免触发 cgroup OOM Killer。
  • ✅ 所有实验的 duration 必须 ≤ scheduler.cron 间隔的 1/3,例如 cron: "@every 6h" ,则 duration 最长设为 120m ,留出缓冲时间。

【实验运行必做】

  • ✅ 实验开始前 5 分钟,手动保存当前所有核心指标的 Grafana 快照(Dashboard → Share → Direct link to snapshot)。
  • ✅ 实验进行中,每 30 秒

更多推荐