服务网格 16:故障注入与流量镜像怎样避免误判
影子请求失败,为什么主请求仍成功
把 orders → inventory-v1 的只读报价复制一份给 inventory-v2,主链路可能照常返回 v1 的结果,影子服务也记录一条请求。这并不矛盾:流量镜像(mirroring/shadowing)复制请求,但主请求不等待影子目标返回。若被复制的是 POST /reserve,影子库存即使不参与主响应,也可能单独写入副作用账本;“不影响主响应”绝不等于“不会执行写入”。
隔离写入不是只给 v2 改个名字。两个实例都执行同一业务副作用时,必须使用不会触达真实库存、账务和通知的独立账本;测试输入也要与生产数据隔离。镜像尽力而为,影子响应和耗时不能替代主请求的成功判据,影子收到的请求数也不能不经对照就当作主请求数。
故障发生在哪一侧,决定了能观察到什么
Istio VirtualService 可在客户端侧为 HTTP 流量指定延迟或 abort(注入错误响应),也可以定义镜像目标和比例。延迟、直接 abort 与应用提交后丢失响应有不同的执行路径:代理若在发往上游前直接返回错误,上游账本可能没有记录;应用已写账本再延迟,则客户端失败也可能对应真实写入。不能用一个代理注入的 503 来模拟“操作已经提交但响应丢失”;应由应用控制提交后的延迟/断开,随后核对账本。
还要注意组合边界:Istio VirtualService 字段说明,客户端侧启用 fault 时,该侧超时或重试不会启用。不能一边在同侧加故障注入,一边将“未发生重试”归因于业务策略。对重试放大的验证应优先保留应用注入故障,另以不组合 fault 的代理配置测试重试;每组记录发往上游的尝试数,而不是只看客户端最终状态码。第 14 篇的合成账本正是应用提交后延迟响应的另一类故障。
正常与失败路径:先跑进程基线,再补代理
在 examples/service-mesh/ 可从只读调用和合成写入基线开始:
1 | |
run-00.sh 的成功报价是主链路基线,但工程中 inventory-v2 的存在不表示已有镜像;run-01.sh 在库存提交后延迟响应、人工重发同一操作 ID,能检验“客户端失败不等于没有副作用”,但这仍不是代理注入或影子请求。要做本机模拟,可在两个隔离的库存进程上用同一合成输入分别执行 POST /reserve,再分别查 GET /ledger;它只模拟两个业务副本会各自写账,不能据此声称观察到 Envoy 的异步镜像。错误路径还应分别检验“影子写入被隔离”与“错误地指向主账本导致重复写入”;只在完全隔离的测试账本里演示后者。
真正的代理实验须在隔离环境记录已生效的路由/镜像/fault 配置、代理出站请求、v1/v2 各自账本和主客户端退出码;分别测试正常镜像、影子不可用、注入 abort、应用提交后丢失响应。主链路的判据看主响应,影子副作用看隔离账本,故障位置由代理与应用同一请求 ID 的日志对齐。当前故障注入、真正的镜像和影子不可用对照均为 NOT_RUN;下表不填写假设结果。
| 待验收场景 | 主客户端结果 | 主账本 | 影子隔离账本 | 代理注入/镜像证据 |
|---|---|---|---|---|
| 正常镜像 | 待运行 | 待运行 | 待运行 | 待运行 |
| 影子不可用 | 待运行 | 待运行 | 待运行 | 待运行 |
| 注入 abort | 待运行 | 待运行 | 待运行 | 待运行 |
| 应用提交后响应丢失 | 待运行 | 待运行 | 待运行 | 待运行 |
把镜像观察和故障注入分两次执行
以下命令在 examples/service-mesh 目录执行,使用 Kubernetes 1.33.4、Istio 1.27.0 的 sidecar 实验基线。先完成 04 的安装与累计应用部署;容器名为 app,Service 端口为 8080。先恢复上一章策略,再执行本章,避免超时、授权或出口规则互相污染。所有预期结果都是验收条件,未运行集群时保留 NOT_RUN。
1 | |
sm-probe 使用 orders ServiceAccount,明确注入 sidecar;它提供 curl,不依赖 scratch 应用镜像里不存在的 shell。每次请求同时保存客户端结果、来源代理配置和目标应用日志。
mainline/16/mirror.yaml 只镜像 GET /quote,比例为 100%,主目标 v1、影子 v2。POST /reserve 落入不镜像的后备路由,避免把写入复制到另一份账本。比例只描述选择,不保证影子目标每次都收到请求;镜像仍是尽力而为。
1 | |
镜像组要求主响应库存版本是 v1,同时 v2 日志出现 ch16-shadow;响应不能因影子结果变成 v2。abort 组要求源代理产生 503,而 v1 不应出现 ch16-abort 的 handler 记录;未带故障头的控制组和恢复组均成功。如果错误请求实际进入应用,要检查规则是否被更宽泛的路由抢先匹配。日志缺失本身不够,必须先证明同一采集窗口内正常组的应用日志完整。
abort.yaml 替换镜像配置,而不是叠加在同侧 timeout/retry 上;这样故障注入的交互限制不会被误判成重试实现故障。
固定源码入口:Istio 1.27.0 pilot/pkg/networking/core/route/route.go。该链接用于核对配置生成或执行机制;完整资源和命令保存在 examples/service-mesh/mainline/16/。静态检查通过不等于集群实验通过。
练习与参考解答
- 请求经代理收到注入的 503,库存没有该请求 ID,能证明“服务端执行后丢失响应”吗?参考解答:不能。先核对代理注入点;如果请求根本没有转发到库存,就无法推出应用提交。要验证结果未知,应在应用写入后制造响应延迟,再检查客户端与账本。
- 主请求 200、影子服务返回 500,如何修改实验让影子操作可安全观察?参考解答:影子独立部署并隔离账本、外部写入与通知,给两条路径带可关联的请求 ID,各自读取服务端结果;不把影子 500 当主请求 500,也不把 200 当影子没有副作用的证明。
官方参考与系列导航
- Istio VirtualService 参考:fault、mirror 及客户端侧 fault 与 timeout/retry 的组合限制。
- Istio 流量镜像:影子请求在关键响应路径之外;示例模板是否可直接复制须以固定版本与实际渲染核验。
