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

Istio 进阶 — 为什么会那样流

把流量策略搬进集群并让它溢出

在 TT Lab 中继续学习

目标

把 DestinationRule 的流量策略转换为 Envoy 集群的字段,并通过发送请求,统计异常点检测和断路器实际是如何工作的。

为什么重要

流量策略出事故,不是因为配置写错,而是因为对行为的期望错了。限额由每个 sidecar 分别统计,异常点检测是事后措施,子集策略会整块覆盖父策略。亲眼看过转换后的集群和实际行为之后,在配置评审中就能立刻指出这三点。

步骤

  1. 在 /root/ist2-dr/dr.yaml 中编写 DestinationRule——名称 ratings,命名空间 default,host 为 ratings.default.svc.cluster.local。trafficPolicy 包含 loadBalancer.simple: LEAST_REQUEST、connectionPool 中的 tcp.maxConnections: 3、http.http1MaxPendingRequests: 2、http.http2MaxRequests: 5,以及 outlierDetection 中的 consecutive5xxErrors: 2、interval: 5s、baseEjectionTime: 30s、maxEjectionPercent: 50。子集只有一个 v2(标签 version: v2),只给这个子集设置 trafficPolicy.connectionPool.tcp.maxConnections: 1。把 istioctl validate 的输出和退出码保存到 /root/ist2-dr/01-validate.txt(最后一行为 rc=0)。
  2. 在 /root/ist2-dr/02-map.txt 中分七行写出 DestinationRule 的字段会变成 Envoy 集群的哪个字段。每行的格式为 <trafficPolicy 아래 경로>=<클러스터 아래 경로>(占位符依次为 trafficPolicy 之下的路径与集群之下的路径),用点连接的路径中不要写数组下标。左侧是这七个——loadBalancer.simple、connectionPool.tcp.maxConnections、connectionPool.http.http1MaxPendingRequests、connectionPool.http.http2MaxRequests、outlierDetection.consecutive5xxErrors、outlierDetection.baseEjectionTime、outlierDetection.maxEjectionPercent。
  3. 在 /root/ist2-dr/pool.yaml 中编写 Envoy 配置——管理端口 9985,把监听器 127.0.0.1:10085 的所有路径发往集群 outbound|9080||ratings.default.svc.cluster.local。该集群包含三个端点(127.0.0.1:8107、127.0.0.1:8108、127.0.0.1:8115),以及把第 1 步父级流量策略按第 2 步对照表转换而来的字段(lb_policy、circuit_breakers.thresholds 的一项、outlier_detection——同时包含 interval)。把 envoy --mode validate 的输出和退出码保存到 /root/ist2-dr/03-validate.txt(最后一行为 rc=0)。
  4. 启动三个上游(8107 和 8115 用 ok,8108 用 fail),用 pool.yaml 启动 Envoy,然后从 localhost:9985/config_dump?resource=static_clusters 中取出这个集群的值,写成一行保存到 /root/ist2-dr/04-dump.txt——lb=<lb_policy> maxconn=<max_connections> pend=<max_pending_requests> req=<max_requests> c5xx=<consecutive_5xx> base=<base_ejection_time> pct=<max_ejection_percent>。
  5. 重新启动 Envoy(使用 pool.yaml)之后立即依次发送 30 次请求,并在 /root/ist2-dr/05-eject.txt 中写入四行——failed_requests=(30 次中收到 503 的数量)、ejected=(/clusters 中 health_flags 为 /failed_outlier_check 的端点的 주소:포트(占位符依次为地址与端口))、ejections_enforced_total=(统计 outlier_detection.ejections_enforced_total 的值)、ejections_active=(统计 outlier_detection.ejections_active 的值)。
  6. 把 pool.yaml 复制为 /root/ist2-dr/pool-subset.yaml,并添加子集 v2 的集群 outbound|9080|v2|ratings.default.svc.cluster.local(端点为 127.0.0.1:8115)。要与 Istio 根据第 1 步的 DestinationRule 生成的结果一致——lb_policy 和 outlier_detection 与父级相同,而 circuit_breakers.thresholds 只由子集的 connectionPool 生成:max_connections: 1,以及子集没有指定的 max_pending_requests、max_requests、max_retries,取 Istio 的默认值 4294967295。路由把前缀 /v2 发往这个集群,其余保持不变。重新启动之后,从 /clusters 的 default_priority 行中读取,并在 /root/ist2-dr/06-subset.txt 中写入 v2_max_connections=、v2_max_pending_requests=、default_max_pending_requests=(父集群的值)三行。
  7. 把 pool.yaml 复制为 /root/ist2-dr/pool-cb.yaml,并添加集群 outbound|9080|slow|ratings.default.svc.cluster.local——端点为 127.0.0.1:8116(需要 3 秒才响应的上游),circuit_breakers.thresholds 设为 max_connections: 1、max_pending_requests: 1(在 Istio 中对应 tcp.maxConnections: 1、http.http1MaxPendingRequests: 1),不设异常点检测。路由把前缀 /slow 发往这个集群。以 slow 模式在 8116 上启动上游,用 --concurrency 1 重新启动 Envoy,然后同时发送五个 /slow 请求(用 & 放到后台,再 wait)。在 /root/ist2-dr/07-overflow.txt 中写入 ok=(200 的数量)、overflow_503=(503 的数量)、pending_overflow=(slow 集群统计 upstream_rq_pending_overflow 的值)三行。
  8. 在 /root/ist2-dr/08-report.md 中写入 lb_policy=、failures_before_eject=(第 5 步中被移出之前遭遇的 503 数量)、v2_pending_limit=(第 6 步中看到的 v2 的队列限额)、overflow_status=(第 7 步中溢出的请求收到的状态码)四行,并在下面写至少四行以 - 开头的说明。

参考

编写带有流量策略的 DestinationRule

在 /root/ist2-dr/dr.yaml 中编写 DestinationRule——名称 ratings,命名空间 default,host 为 ratings.default.svc.cluster.local。trafficPolicy 包含 loadBalancer.simple: LEAST_REQUEST、connectionPool 中的 tcp.maxConnections: 3、http.http1MaxPendingRequests: 2、http.http2MaxRequests: 5,以及 outlierDetection 中的 consecutive5xxErrors: 2、interval: 5s、baseEjectionTime: 30s、maxEjectionPercent: 50。子集只有一个 v2(标签 version: v2),只给这个子集设置 trafficPolicy.connectionPool.tcp.maxConnections: 1。把 istioctl validate 的输出和退出码保存到 /root/ist2-dr/01-validate.txt(最后一行为 rc=0)。

DestinationRule 决定“发送之后如何处理”。流量策略分为三块——发给谁(loadBalancer)、一次能接纳多少(connectionPool)、什么时候把不健康的端点移出(outlierDetection)。在子集之下也可以放置形状相同的 trafficPolicy,它如何与父级合并,正是第 6 步的主题。

做出七个字段的对照表

在 /root/ist2-dr/02-map.txt 中分七行写出 DestinationRule 的字段会变成 Envoy 集群的哪个字段。每行的格式为 <trafficPolicy 아래 경로>=<클러스터 아래 경로>(占位符依次为 trafficPolicy 之下的路径与集群之下的路径),用点连接的路径中不要写数组下标。左侧是这七个——loadBalancer.simple、connectionPool.tcp.maxConnections、connectionPool.http.http1MaxPendingRequests、connectionPool.http.http2MaxRequests、outlierDetection.consecutive5xxErrors、outlierDetection.baseEjectionTime、outlierDetection.maxEjectionPercent。

在 Envoy 的集群配置文档(cluster.proto)中找名称相近的字段即可。连接池的限额在 Envoy 中位于 circuit_breakers.thresholds 之下,按优先级排列的列表中——Istio 所说的“连接池”和 Envoy 所说的“断路器”是同样的数值。异常点检测的字段名称,如 consecutive5xxErrors,从驼峰式命名变成下划线命名时,会略有缩短。

按对照表建立集群

在 /root/ist2-dr/pool.yaml 中编写 Envoy 配置——管理端口 9985,把监听器 127.0.0.1:10085 的所有路径发往集群 outbound|9080||ratings.default.svc.cluster.local。该集群包含三个端点(127.0.0.1:8107、127.0.0.1:8108、127.0.0.1:8115),以及把第 1 步父级流量策略按第 2 步对照表转换而来的字段(lb_policy、circuit_breakers.thresholds 的一项、outlier_detection——同时包含 interval)。把 envoy --mode validate 的输出和退出码保存到 /root/ist2-dr/03-validate.txt(最后一行为 rc=0)。

circuit_breakers.thresholds 是一个列表——因为可以按优先级(DEFAULT、HIGH)分别设置限额。Istio 只使用 DEFAULT 一项。时间值要写成带单位的字符串,如 30s。8108 是用作始终返回 503 的端点(第 5 步)。

从 config_dump 中读回六个值

启动三个上游(8107 和 8115 用 ok,8108 用 fail),用 pool.yaml 启动 Envoy,然后从 localhost:9985/config_dump?resource=static_clusters 中取出这个集群的值,写成一行保存到 /root/ist2-dr/04-dump.txt——lb=<lb_policy> maxconn=<max_connections> pend=<max_pending_requests> req=<max_requests> c5xx=<consecutive_5xx> base=<base_ejection_time> pct=<max_ejection_percent>。

这一步是把写在文件里的内容与 Envoy 实际读入的内容对照。在生产环境中,istioctl proxy-config cluster <파드> --fqdn … -o json(占位符为 Pod)显示的就是这份转储。用 .configs[].cluster | select(.name==…) 选出之后,再用 jq 的字符串插值生成一行。转储会省略取默认值的字段,所以如果看到缺少的值,说明它没有写进配置。

异常点检测是在经历失败之后才移出

重新启动 Envoy(使用 pool.yaml)之后立即依次发送 30 次请求,并在 /root/ist2-dr/05-eject.txt 中写入四行——failed_requests=(30 次中收到 503 的数量)、ejected=(/clusters 中 health_flags 为 /failed_outlier_check 的端点的 주소:포트(占位符依次为地址与端口))、ejections_enforced_total=(统计 outlier_detection.ejections_enforced_total 的值)、ejections_active=(统计 outlier_detection.ejections_active 的值)。

异常点检测是在遭遇之后才移出的装置。当某个端点连续 consecutive5xxErrors 次返回 5xx 时,才会在 baseEjectionTime 期间把它移出负载均衡。所以在达到这个次数之前,用户会先遭遇 503。被移出之后,只剩另外两个端点接收请求,503 就停止了。maxEjectionPercent: 50 的意思是,三个中最多只能移出一个。/clusters 的行格式为 클러스터::주소::health_flags::값(占位符依次为集群名称、地址与值)。

子集策略会整块覆盖

把 pool.yaml 复制为 /root/ist2-dr/pool-subset.yaml,并添加子集 v2 的集群 outbound|9080|v2|ratings.default.svc.cluster.local(端点为 127.0.0.1:8115)。要与 Istio 根据第 1 步的 DestinationRule 生成的结果一致——lb_policy 和 outlier_detection 与父级相同,而 circuit_breakers.thresholds 只由子集的 connectionPool 生成:max_connections: 1,以及子集没有指定的 max_pending_requests、max_requests、max_retries,取 Istio 的默认值 4294967295。路由把前缀 /v2 发往这个集群,其余保持不变。重新启动之后,从 /clusters 的 default_priority 行中读取,并在 /root/ist2-dr/06-subset.txt 中写入 v2_max_connections=、v2_max_pending_requests=、default_max_pending_requests=(父集群的值)三行。

官方文档只写了“子集级别的流量策略会覆盖 DestinationRule 级别的对应设置”。覆盖的单位是 connectionPool、loadBalancer、outlierDetection、tls 这样的整块。所以如果在子集中只写了一个 tcp.maxConnections,整个 connectionPool 块就会被整体替换,父级的 http 限额(2、5)不会延续到 v2。空缺的部分是 Istio 填入的默认值(4294967295,实际上不设上限)——比 Envoy 自身的默认值 1024 还要宽松。请在 /clusters 中找 <이름>::default_priority::max_pending_requests::<값>(占位符依次为名称与值)这一行。

数一数断路器是如何真正打开的

把 pool.yaml 复制为 /root/ist2-dr/pool-cb.yaml,并添加集群 outbound|9080|slow|ratings.default.svc.cluster.local——端点为 127.0.0.1:8116(需要 3 秒才响应的上游),circuit_breakers.thresholds 设为 max_connections: 1、max_pending_requests: 1(在 Istio 中对应 tcp.maxConnections: 1、http.http1MaxPendingRequests: 1),不设异常点检测。路由把前缀 /slow 发往这个集群。以 slow 模式在 8116 上启动上游,用 --concurrency 1 重新启动 Envoy,然后同时发送五个 /slow 请求(用 & 放到后台,再 wait)。在 /root/ist2-dr/07-overflow.txt 中写入 ok=(200 的数量)、overflow_503=(503 的数量)、pending_overflow=(slow 集群统计 upstream_rq_pending_overflow 的值)三行。

如果上游很慢,一个连接会被占用 3 秒。在此期间到来的请求进入等待队列,如果队列也满了,Envoy 连上游都不会去,就立刻返回 503 和 x-envoy-overloaded: true 头。所以能接纳的数量是“连接数 + 队列长度”。如果依次发送,请求会一次完成一个,什么都不会溢出,所以一定要同时发送。统计数据是累积的,所以重新启动之后只统计一次。

整理成 DestinationRule 转换对照表

在 /root/ist2-dr/08-report.md 中写入 lb_policy=、failures_before_eject=(第 5 步中被移出之前遭遇的 503 数量)、v2_pending_limit=(第 6 步中看到的 v2 的队列限额)、overflow_status=(第 7 步中溢出的请求收到的状态码)四行,并在下面写至少四行以 - 开头的说明。

数值请从前面步骤的文件中抄录。说明行中最好写下“修改 DestinationRule 时我要小心什么”——尤其是子集策略覆盖父级的方式,在评审时很容易被忽略。