TT Lab
开始
学习 学习路径 课程

ICA — Istio 认证助理

推算韧性配置的取值

在 TT Lab 中继续学习

目标

一边亲手编写重试、超时、熔断器(circuit breaker)、故障注入、镜像,一边理解每个值会对彼此施加什么约束。

为什么重要

复制来的弹性配置通常不能工作,更糟的是会放大故障。如果在调用链的每一层都配置重试,三层调用链在最坏情况下会有 9 倍、四层则有 27 倍的请求涌向最内层的服务。对于已经奄奄一息的服务,重试就成了最后一击。

超时也一样。timeout 是包含重试和 backoff 在内的整体预算,perTryTimeout 是每次尝试的上限。如果快速失败,即使整体预算小于各次尝试上限之和,也可以重试。反过来,如果一次尝试用掉了整个预算,下一次尝试可能不会开始。本实验中的 3 秒/1 秒/额外 2 次,是用来编写配置的值,并不保证三次尝试各自恰好执行 1 秒。实际次数必须同时观测失败条件、响应时间和 backoff。外层调用方即使结束了等待,也不能保证内层的业务处理被取消,所以要另行设计去重和取消传播,避免已经处理过的写操作被重新执行。

熔断器的值太大,事实上等于关闭;太小,则平时也会出现 503。所以要以实测的并发度为基准来估算,并用 maxEjectionPercent 设置安全阀。本实验就是把这种感觉落到清单中的训练。

Kubernetes 基本资源要真实 apply,Istio CRD 则写成 /root/ica-resilience/ 下的文件。这是基于 kwok 的设计实验,所以不要把它当作已经验证了真实 Envoy 的 HTTP 响应、重试次数、业务重复处理。

步骤

  1. 创建命名空间 ica-resilience,并添加标签 istio-injection=enabled。
  2. 在命名空间 ica-resilience 中,把 Deployment inventory 以 3 个副本部署。Pod 标签是 app=inventory,镜像是 nginx:1.27-alpine。
  3. 在同一个命名空间中创建两个 Service:inventory 和 inventory-canary,两者的端口都是 8080,端口名称都是 http。inventory 的 selector 是 app=inventory。
  4. 在 /root/ica-resilience/vs-inventory-retry.yaml 中编写 VirtualService。spec.hosts[0] 是 inventory.ica-resilience.svc.cluster.local,http 规则中要包含 timeout: 3s、retries.attempts: 2、retries.perTryTimeout: 1s,retries.retryOn 中要包含 5xx 和 connect-failure。
  5. 在 /root/ica-resilience/vs-inventory-fault.yaml 中编写 VirtualService。共有两条规则。第一条规则只适用于请求头 x-chaos-test 为 true 的请求,并带有 fault.delay.fixedDelay: 3s / fault.delay.percentage.value: 50 / fault.abort.httpStatus: 503 / fault.abort.percentage.value: 10。第二条规则没有 match 也没有 fault,只做路由。
  6. 在 /root/ica-resilience/dr-inventory.yaml 中编写 DestinationRule。spec.host 是 inventory.ica-resilience.svc.cluster.local,在 trafficPolicy 下放置 outlierDetection(consecutive5xxErrors 5、interval 10s、baseEjectionTime 30s、maxEjectionPercent 50)和 connectionPool(tcp.maxConnections 100、http.http1MaxPendingRequests 50)。
  7. 在 /root/ica-resilience/vs-inventory-mirror.yaml 中编写 VirtualService。向 inventory.ica-resilience.svc.cluster.local 路由 100%,同时向 inventory-canary.ica-resilience.svc.cluster.local 镜像 20%,并把整体 timeout 设为 5s。

参考

准备实验命名空间

创建命名空间 ica-resilience,并添加标签 istio-injection=enabled。

与前面的实验方式相同。别忘了自动注入标签。

部署有 3 个实例的 Deployment

在命名空间 ica-resilience 中,把 Deployment inventory 以 3 个副本部署。Pod 标签是 app=inventory,镜像是 nginx:1.27-alpine。

要想体会 outlierDetection 的 maxEjectionPercent,必须有多个实例。请等 Pod 变为 Ready 之后再评分。

创建主服务和金丝雀服务

在同一个命名空间中创建两个 Service:inventory 和 inventory-canary,两者的端口都是 8080,端口名称都是 http。inventory 的 selector 是 app=inventory。

把镜像目标放在单独的 Service 中更安全。两个 Service 的端口名称都要让协议一目了然。

协调超时与重试的关系

在 /root/ica-resilience/vs-inventory-retry.yaml 中编写 VirtualService。spec.hosts[0] 是 inventory.ica-resilience.svc.cluster.local,http 规则中要包含 timeout: 3s、retries.attempts: 2、retries.perTryTimeout: 1s,retries.retryOn 中要包含 5xx 和 connect-failure。

timeout 是包含重试在内的整体截止时间。perTryTimeout x (attempts + 1) 超过 timeout 时,重试就来不及执行。retryOn 中也要包含请求到达之前就失败的情况。

用请求头控制的故障注入

在 /root/ica-resilience/vs-inventory-fault.yaml 中编写 VirtualService。共有两条规则。第一条规则只适用于请求头 x-chaos-test 为 true 的请求,并带有 fault.delay.fixedDelay: 3s / fault.delay.percentage.value: 50 / fault.abort.httpStatus: 503 / fault.abort.percentage.value: 10。第二条规则没有 match 也没有 fault,只做路由。

在生产集群中对全部流量注入 fault,不是混沌测试,而是故障。请把匹配规则和普通流量规则分开,fault 只放在匹配到的那一条上。

确定熔断器的上限

在 /root/ica-resilience/dr-inventory.yaml 中编写 DestinationRule。spec.host 是 inventory.ica-resilience.svc.cluster.local,在 trafficPolicy 下放置 outlierDetection(consecutive5xxErrors 5、interval 10s、baseEjectionTime 30s、maxEjectionPercent 50)和 connectionPool(tcp.maxConnections 100、http.http1MaxPendingRequests 50)。

connectionPool 和 outlierDetection 并排放在同一个 DestinationRule 的 trafficPolicy 下。请想一想,实例只有 3 个时,把同时驱逐比例设为 100 会怎样。

放出影子流量

在 /root/ica-resilience/vs-inventory-mirror.yaml 中编写 VirtualService。向 inventory.ica-resilience.svc.cluster.local 路由 100%,同时向 inventory-canary.ica-resilience.svc.cluster.local 镜像 20%,并把整体 timeout 设为 5s。

镜像会丢弃响应,所以真正的 route 只放一个。镜像目标是第 3 步创建的金丝雀服务,比例用 mirror 旁边的单独字段指定。