istio 常见的 10 个异常

总结使用 istio 常见的10个异常:node

  1. Service 端口命名约束
  2. 流控规则下发顺序问题
  3. 请求中断分析
  4. sidecar 和 user container 启动顺序
  5. Ingress Gateway 和 Service 端口联动
  6. VirtualService 做用域
  7. VirtualService 不支持 host fragment
  8. 全链路跟踪并不是彻底透明接入
  9. mTLS 致使链接中断
  10. 用户服务监听地址限制

1. Service 端口命名约束

istio 支持多平台,不过 Istio 和 k8s 的兼容性是最优的,不论是设计理念,核心团队仍是社区, 都有一脉相承的意思。但 istio 和 k8s 的适配并不是彻底没有冲突, 一个典型问题就是 istio 须要 k8s service 按照协议进行端口命名(port naming)。安全

端口命名不知足约束而致使的流量异常,是使用 mesh 过程当中最多见的问题,其现象是协议相关的流控规则不生效,这一般能够经过检查该 port LDS 中 filter 的类型来定位。网络

缘由

k8s 的网络对应用层是无感知的,k8s 的主要流量转发逻辑发生在 node 上,由 iptables/ipvs 来实现,这些规则并不关心应用层里是什么协议。app

istio 的核心能力是对 7层流量进行管控,但前提条件是 istio 必须知道每一个受管控的服务是什么协议,istio 会根据端口协议的不一样,下发不一样的流控功能(envoy filter),而 k8s 资源定义里并不包括七层协议信息,因此 istio 须要用户显式提供。负载均衡

istio 的解决方案:Protocol sniffing

协议嗅探概要:ide

  • 检测 TLS CLIENT_HELLO 提取 SNI、ALPN、NPN 等信息
  • 基于常见协议的已知典型结构,尝试检测应用层 plaintext 内容 a. 基于HTTP2 spec: Connection Preface,,判断是否为 HTTP/2 b. 基于 HTTP header 结构,判断是不是 HTTP/1.x
  • 过程当中会设置超时控制和检测包大小限制, 默认按照协议 TCP 处理

最佳实践

Protocol sniffing 减小了新手使用 istio 所需的配置,可是可能会带来不肯定的行为。不肯定的行为在生产环境中是应该尽可能避免的。微服务

一些嗅探失效的例子:spa

  • 客户端和服务端使用着某类非标准的七层协议,客户端和服务端均可以正确解析,可是不能确保 istio 自动嗅探逻辑承认这类非标准协议。好比对于 http 协议,标准的换行分隔是用 CRLF (0x0d 0x0a), 可是大部分 http 类库会使用并承认 LF (0x0a)做为分隔。
  • 某些自定义私有协议,数据流的起始格式和 http 报文格式相似,可是后续数据流是自定义格式:未开启嗅探时:数据流按照 L4 TCP 进行路由,符合用户指望 若是开启嗅探:数据流最开始会被认定为 L7 http 协议,可是后续数据不符合 http 格式,流量将被中断

建议生产环境不使用协议嗅探, 接入 mesh 的 service 应该按照约定使用协议前缀进行命名。设计

2. 流控规则下发顺序问题

异常描述

在批量更新流量规则的过程当中,偶尔会出现流量异常(503),envoy 日志中 RESPONSE_FLAGS 包含「NR」标志(No route configured),持续时间不长,会自动恢复。3d

缘由分析

当用户使用 kubectl apply -f multiple-virtualservice-destinationrule.yaml时,这些对象的传播和生效前后顺序是不保证的,所谓最终一致性,好比 VirtualService 中引用了某一个 DestinationRule 定义的子版本,可是这个 DestinationRule 资源的传播和生效可能在时间上落后于 该 VirtualService 资源。

最佳实践:make before break

将更新过程从批量单步拆分为多步骤,确保整个过程当中不会引用不存在的 subset:

当新增 DestinationRule subset 时,应该先 apply DestinationRule subset,等待 subset 生效后,再 apply 引用了该 subset 的 VirtualService。

当删除 DestinationRule subset 时,应该先 删除 VirtualService 中对 该 subset 的引用,等待 VirtualService 的修改生效后,在执行删除 DestinationRule subset。

3. 请求中断分析

请求异常,究竟是 istio 流控规则致使,仍是业务应用的返回,流量断点出如今哪一个具体的 pod?

这是使用 mesh 最多见的困境,在微服务中引入 envoy 做为代理后,当流量访问和预期行为不符时,用户很难快速肯定问题是出在哪一个环节。客户端收到的异常响应,诸如 40三、40四、503 或者链接中断等,多是链路中任一 sidecar 执行流量管控的结果, 但也有多是来自某个服务的合理逻辑响应。

envoy 流量模型

Envoy 接受请求流量叫作 Downstream,Envoy 发出请求流量叫作Upstream。在处理Downstream 和 Upstream 过程当中, 分别会涉及2个流量端点,即请求的发起端和接收端:

在这个过程当中, envoy 会根据用户规则,计算出符合条件的转发目的主机集合,这个集合叫作 UPSTREAM_CLUSTER, 并根据负载均衡规则,从这个集合中选择一个 host 做为流量转发的接收端点,这个 host 就是 UPSTREAM_HOST。

以上就是 envoy 请求处理的 流量五元组信息, 这是 envoy 日志里最重要的部分,经过这个五元组咱们能够准确的观测流量「从哪里来」和「到哪里去」。

  • UPSTREAM_CLUSTER
  • DOWNSTREAM_REMOTE_ADDRESS
  • DOWNSTREAM_LOCAL_ADDRESS
  • UPSTREAM_LOCAL_ADDRESS
  • UPSTREAM_HOST

日志分析示例

经过日志重点观测 2 个信息:

  • 断点是在哪里 ?
  • 缘由是什么?

示例一:一次正常的 client-server 请求

能够看到 2 端日志包含相同的 request ID,所以能够将流量分析串联起来。

示例二:no healthy upstream, 好比目标 deployment 健康副本数为 0

日志中 flag「UH」表示 upstream cluster 中没有健康的 host。

示例三:No route configured , 好比 DestinationRule 缺少对应的 subset

日志中 flag「NR」表示找不到路由。

示例四,Upstream connection failure,好比服务未正常监听端口。

日志中 flag「UF」表示 Upstream 链接失败,据此能够判断出流量断点位置。

4. sidecar 和 user container 启动顺序

异常描述

Sidecar 模式在kubernetes 世界很流行,但对目前的 k8s (V1.17)来讲,并无 sidecar 的概念,sidecar 容器的角色是用户主观赋予的。

对 Istio 用户来讲,一个常见的困扰是:sidecar 和用户容器的启动顺序:

sidecar(envoy) 和用户容器的启动顺序是不肯定的,若是用户容器先启动了,envoy 还未完成启动,这时候用户容器往外发送请求,请求仍然会被拦截,发往未启动的 envoy,请求异常。

在 Pod 终止阶段,也会有相似的异常,根源仍然是 sidecar 和普通容器的生命周期的不肯定性。

解决方案

目前常规的规避方案主要是有这样几种:

  • 业务容器延迟几秒启动, 或者失败重试
  • 启动脚本中主动探测 envoy 是否ready,如 127.0.0.1:15020/ healthz/ready

不管哪一种方案都显得很蹩脚,为了完全解决上述痛点,从 kubernets 1.18版本开始,k8s 内置的 Sidecar 功能将确保 sidecar 在正常业务流程开始以前就启动并运行,即经过更改pod的启动生命周期,在init容器完成后启动sidecar容器,在sidecar容器就绪后启动业务容器,从启动流程上保证顺序性。而 Pod 终止阶段,只有当全部普通容器都已到达终止状态, 才会向sidecar 容器发送 SIGTERM 信号。

5. Ingress Gateway 和 Service 端口联动

Ingress Gateway 规则不生效的一个常见缘由是:Gateway 的监听端口在对应的 k8s Service 上没有开启,首先咱们须要理解 Istio Ingress Gateway 和 k8s Service 的关系:

上图中,虽然 gateway 定义指望管控端口 b 和 c,可是它对应的 service (经过腾讯云CLB)只开启了端口 a 和 b,所以最终从 LB 端口 b 进来的流量才能被 istio gateway 管控。

  • Istio Gateway 和 k8s Service 没有直接的关联,两者都是经过 selector 去绑定 pod,实现间接关联
  • Istio CRD Gateway 只实现了将用户流控规则下发到网格边缘节点,流量仍须要经过 LB 控制才能进入网格
  • 腾讯云 tke mesh 实现了 Gateway-Service 定义中的 Port 动态联动,让用户聚焦在网格内的配置。

6. VirtualService 做用域

VirtualService 包含了大部分 outbound 端的流量规则,它既能够应用到网格内部数据面代理中, 也能够应用到网格边缘的代理中。

VirtualService 的属性gateways用于指定 VirtualService 的生效范围:

  • 若是 VirtualService.gateways为空,则 istio 为其赋默认值 mesh, 表明生效范围为网格内部
  • 若是但愿 VirtualService 应用到具体边缘网关上,则须要显示为其赋值:gateway-name1,gateway-name2...
  • 若是但愿 VirtualService 同时应用到网格内部和边缘网关上,则须要显示地把mesh值加入VirtualService.gateways, 如 mesh,gateway-name1,gateway-name2...一个常见的问题是以上的第三种状况,VirtualService 最开始做用于网关内部,后续要将其规则扩展到边缘网关上,用户每每只会添加具体 gateway name,而遗漏 mesh:

Istio 自动给VirtualService.gateways设置默认值, 本意是为了简化用户的配置,可是每每会致使用户应用不当,一个 feature 一不当心会被用成了 bug。

7. VirtualService 不支持 host fragment

异常案例

对某一 host 新增、修改 VirtualService,发现规则始终没法生效,排查发现存在其余 VirtualService 也对该 host 应用了其余规则,规则内容可能不冲突,但仍是可能出现其中一些规则没法生效的状况。

背景

  • VirtualService 里的规则,按照 host 进行聚合
  • 随着业务的增加,VirtualService 的内容会快速增加,一个 host 的流控规则,可能会由不一样的团队分布维护。如安全规则和业务规则分开,不一样业务按照子 path 分开

目前 istio 对 cross-resource VirtualService 的支持状况:

  • 在网格边缘(gateway),同一个 host 的流控规则,支持分布到多个 VirtualService 对象中,istio 自动聚合,但依赖定义顺序以及用户自行避免冲突。
  • 在网格内部(for sidecar),同一个 host 的流控规则,不支持分布到多个 VirtualService 对象中,若是同一个 host 存在多个 VirtualService,只有第一个 VirtualService 生效,且没有冲突检测。

VirtualService 不能很好支持 host 规则分片,使得团队的维护职责不能很好的解耦,配置人员须要知悉目标 host 的全部流控规则,才有信心去修改 VirtualService。

Istio 解决方案:Virtual Service chaining(plan in 1.6)

Istio 计划在 1.6 中支持 Virtual Service 代理链:

  • Virtual Service 支持分片定义 + 代理链
  • 支持团队对同一 host 的 Virtual Service 进行灵活分片,好比按照 SecOps/Netops/Business 特性分离,各团队维护各类独立的 Virtual Service

8. 全链路跟踪并不是彻底透明接入

异常案例

微服务接入后 service mesh 后,链路跟踪数据没有造成串联。

缘由

service mesh 遥测系统中,对调用链跟踪的实现,并不是彻底的零入侵,须要用户业务做出少许的修改才能支持,具体地,在用户发出(http/grpc) RPC 时, 须要主动将上游请求中存在的 B3 trace headers写入下游 RPC 请求头中,这些 headers 包括:

有部分用户难以理解:既然 inbound 流量和 outbound 流量已经彻底被拦截到 envoy,envoy 能够实现彻底的流量管控和修改,为何还须要应用显示第传递 headers?

对于 envoy 来讲,inbound 请求和 outbound 请求彻底是独立的,envoy 没法感知请求之间的关联。实际上这些请求到底有无上下级关联,彻底由应用本身决定。举一个特殊的业务场景,若是 Pod X 接收到 请求 A,触发的业务逻辑是:每隔 10 秒 发送一个请求到 Pod Y,如 B1,B2,B3,那么这些扇出的请求 Bx(x=1,2,3...),和请求 A 是什么关系?业务可能有不一样的决策:认为 A 是 Bx 的父请求,或者认为 Bx 是独立的顶层请求。

9. mTLS 致使链接中断

在开启 istio mTLS 的用户场景中,访问出现 connection termination 是一个高频的异常:

这个异常的缘由和 DestinationRule 中的 mTLS 配置有关,是 istio 中一个不健壮的接口设计。

  • 当经过 MeshPolicy 开启全局 mTLS, 若是网格中没有定义其余的 DestinationRule,mTLS 正常运行
  • 若是后续网格中新增了 DestinationRule,而 DestinationRule 中能够覆盖子版本的 mTLS 值(默认是不开启!), 用户在使用 DestinationRule 时,每每不多去关注 mTLS 属性(留空)。最终致使增 DestinationRule 后 mTLS 变成了不开启,致使connection termination
  • 为了修复以上问题,用户不得不在全部 DestinationRule 中增长 mTLS 属性并设置为开启

这种 istio mtls 用户接口极度不友好,虽然 mtls 默认作到了全局透明, 业务感知不到 mtls 的存在, 可是一旦业务定义了 DestinationRule,就必需要知道当前 mtls 是否开启,并做出调整。试想 mtls 配置交由安全团队负责,而业务团队负责各自的 DestinationRule,团队间的耦合会很是严重。

10. 用户服务监听地址限制

异常描述

若是用户容器中业务进程监听的地址是具体ip (pod ip),而不是0.0.0.0, 该用户容器没法正常接入 istio,流量路由失败。这是又一个挑战 Istio 最大透明化(Maximize Transparency)设计目标 的场景。

缘由分析

Istio-proxy 中的一段 iptables:

其中,ISTIO_IN_REDIRECT 是 virtualInbound, 端口 15006;ISTIO_REDIRECT 是 virtualOutbound,端口 15001。

关键点是规则二:若是 destination 不是127.0.0.1/32, 转给15006 (virtualInbound, envoy监听),这里致使了对 pod ip 的流量始终会回到 envoy。

对该规则的解释:

# Redirect app calls back to itself via Envoy when using the service VIP or endpoint # address, e.g. appN => Envoy (client) => Envoy (server) => appN.

该规则是但愿在这里起做用: 假设当前Pod a属于service A, Pod 中用户容器经过服务名访问服务A, envoy中负载均衡逻辑将此次访问转发到了当前的pod ip, istio 但愿这种场景服务端仍然有流量管控能力. 如图示:

改造建议

建议应用在接入 istio 以前, 调整服务监听地址,使用 0.0.0.0 而不是具体 IP。若是业务方认为改造难度大,能够参考以前分享的一个解决方案:服务监听pod ip 在istio中路由异常分析

【腾讯云原生】云说新品、云研新术、云游新活、云赏资讯,扫码关注同名公众号,及时获取更多干货!!
相关文章
相关标签/搜索