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-4gbDroplet 上的 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-managerPod 卡在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-daemonDaemonSet 会在每个节点上尝试连接/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 秒
