LabHub
学习 学习路径 课程

ICA — Istio 认证助理

没被强制的状态,看着却像正常

在 LabHub 中继续学习

一句话总结

在 mesh 中,最危险的不是错误配置,而是没有应用到任何地方的配置。前者会报错,后者却看起来一切正常。

概念图: “配置没有应用到任何地方” · 之前 · 原生 sidecar · 明明存在 sidecar 时也回答不存在。

为什么这是个问题

Service mesh 中最危险的状态,不是“配置错误”,而是**“配置没有应用到任何地方”**。因为前者会报错,后者却悄无声息。

没有 sidecar,一切都毫无意义

在为 namespace 添加 istio-injection=enabled 之前创建的 Pod 没有 sidecar。该 Pod 处于 Running 状态,服务也会正常响应。然而流量没有经过 mesh,因此 mTLS、授权策略和路由都不会生效。

这就形成了安全策略已经全部写好,却什么都没有受到保护的状态。

检查方法已经改变

Istio 现在使用 Kubernetes 的原生 sidecaristio-proxy 不再位于 spec.containers,而是进入 spec.initContainers(并设置 restartPolicy: Always)。

因此,过去使用 kubectl get pod -o jsonpath='{.spec.containers[*].name}' 检查的习惯,如今会在明明存在 sidecar 时也回答不存在。 如果相信这个结果,就会误判注入失败,转而修复完全无关的地方。

迁移的原因是启动与结束顺序。旧方式下,应用可能比 sidecar 更早启动,在网络尚未就绪时发送请求;也可能应用已经结束,sidecar 却仍然运行,导致 Job 永远无法完成。原生 sidecar 会先于应用启动,并在应用之后结束。

两种“不工作”

症状 原因 检查位置
没有响应状态码(curl 000,exit 56) 不满足 mTLS——在 TLS 握手时中断 对端是否在 mesh 外,是否有 sidecar
403RBAC: access denied 授权拒绝——连接与 mTLS 已成功 AuthorizationPolicy 的 principals

仅凭响应状态码,就能区分连接层问题与策略层问题。没有这个区分,每次排查 mesh 故障时都只能从头检查。

Mesh 实际做什么,以及付出的代价

附加 sidecar 后,一次请求经过的路径会变长:应用 → 本端 proxy → 对端 proxy → 对端应用。这一结构让应用无需修改代码,就能获得 mTLS、重试、circuit breaker、精细路由以及每次调用的指标。但它也有成本,采用前必须充分了解。

会增加延迟。 请求需要经过两个 proxy,因此每次调用都会增加毫秒级耗时。单次调用可能可以忽略,但如果一个页面触发二十次内部调用,延迟就会累积。

会消耗资源。 每个 Pod 都附带一个 proxy,因此内存与 CPU 用量会随 Pod 数量增加。当 Pod 达到数百个时,总开销可能相当于数台节点。

会增加一个故障点。 如果 proxy 无法获取配置,流量就会停止。即使控制平面宕机,proxy 仍能依靠已经收到的配置维持一段时间,但期间的变更不会生效。

因此,判断标准应当如此:如果服务数量很少、调用关系也很简单,mesh 就是过度设计。 重试和 circuit breaker 可以通过 library 实现,mTLS 也可以在 gateway 终止。mesh 真正体现价值的场景,是服务数量超过数十个、存在多种语言而无法统一 library,并且不同团队的发布周期各异,难以通过代码强制执行共同策略。

引入时也不能一次全部启用。应先从一个 namespace 开启注入,并以 PERMISSIVE 启动 mTLS。 此模式同时接受加密连接与明文连接,因此即使 mesh 内外混合存在,通信也不会中断。通过指标确认明文连接降至 0 后,再提升为 STRICT。如果跳过这一步,一开始就启用 STRICT,与尚未注入的 Pod 之间的通信将全部中断,并形成难以看出原因的故障。来自 gateway 的外部流量与 mesh 内部流量往往适用不同规则,因此编写策略时,最好每次都明确所指的是哪一侧。

实务中真正重要的事

添加 label 后,必须重新创建 Pod。 istio-injection=enabled 只对未来创建的 Pod 生效。如果保留已经运行的 Pod,它将永远停留在 mesh 外;而服务在这种状态下仍会正常响应,因此无人察觉。

检查 sidecar 时也要查看 initContainers 迁移到原生 sidecar 后,只查看 spec.containers 的习惯会把实际存在的东西报告为不存在。还必须同时查看 kubectl get pod -o jsonpath='{.spec.initContainers[*].name}'

先通过响应状态码区分层次。 如果完全没有状态码(curl 000),就是连接层问题;如果是 403,就是策略层问题。没有这一次分流,每次 mesh 故障都只能从头排查。

下一项实验将让你在真实 Istio 环境中亲自验证这些现象。