【Linkerd】服务发现流:per-target Get 与 EndpointTranslator

💡 原文中文,约6000字,阅读约需15分钟。
📝

内容提要

本文介绍Linkerd 2.20服务发现机制:每个出站目标独立gRPC流,经EndpointTranslator处理EndpointSlice增量,富化身份、zone等元数据。排障需检查流内Update序列,区分no_endpoints的exists语义,注意队列溢出断流信号,与xDS EDS机制不同。

🔎

延伸解读

排障先看流,再看日志

遇到 Service 有 Endpoints 但 mesh 内连不上时,不要急着查数据面或 mTLS。先确认该出站目标的 destination.Get 流里有没有 WeightedAddr、有没有 no_endpoints{exists: true},以及流是否因队列溢出被关闭。流内 Update 序列才是发现轴的第一手证据。

no_endpoints 的 exists 语义

no_endpoints 的 exists 字段是关键:exists: false 表示服务不存在,客户端可回退 DNS;exists: true 表示服务存在但无端点,客户端不得回退 DNS。若忽略该字段,在重连窗口内可能错误清空缓存,导致流量中断。

队列溢出是断流信号

EndpointTranslator 用带缓冲 channel 异步处理 Informer 回调,默认队列容量 100。当 EndpointSlice 变化过快,队列满时递增 endpoint_updates_queue_overflow 并关闭流,迫使 proxy 重连。这是发现轴上的“流落后”信号,与 xDS 的 NACK 机制不同。

与 xDS EDS 的机制差异

Linkerd 对每个出站目标建立独立 gRPC 流,流内只有该目标的 Update 序列;而 xDS EDS 按 Cluster 名订阅,同一连接可挂多种类型。排障时不要套用 istiod 的“EDS 为空”runbook,应先确认 authority 字符串,再检查流内 Update 序列。

Q&A

Linkerd 2.20 中,destination.Get 流是如何为每个出站目标建立的?

Linkerd 2.20 中,linkerd2-proxy 对每个出站目标发起独立的 destination.Get(GetDestination) 服务端流。GetDestination.path 形如 svc.namespace.svc.cluster.local:port,省略端口时默认 80,省略 namespace 时默认 default。一次 Get 的生命周期包括解析 path 为 Kubernetes Service 三元组、区分本地/远程多集群/联邦服务三条订阅路径、为该流创建 EndpointTranslator 并注册为 listener,最后阻塞至客户端取消、控制面 shutdown 或流异常结束。

Linkerd 的 destination.Get 流与 xDS EDS 在订阅方式上有何不同?

xDS EDS 按 Cluster 名订阅端点集合,同一连接上可挂多种 xDS 类型;而 Linkerd 是每个逻辑目标一条 gRPC 流,流内只有该目标的 Update 序列。因此排障时应该关注“对这个 authority 的 Get 流收到了什么”,而不是“EDS 快照里有没有这个 Cluster”。

Linkerd destination 流中的 no_endpoints 消息有哪些语义?

destination.proto 规定三种 Update:add(WeightedAddrSet)、remove(AddrSet)、no_endpoints。no_endpoints 的 exists 字段有两种语义:exists: false 表示服务不存在,客户端可回退 DNS;exists: true 表示服务存在但无端点,客户端不得回退 DNS。此外,no_endpoints 后接 add 不等价于只发 add,客户端必须按 proto 注释处理 exists 字段,否则会在重连窗口内错误清空缓存。

EndpointTranslator 如何处理 EndpointSlice 增量,队列溢出时会发生什么?

EndpointTranslator 通过带缓冲 channel 异步处理 Informer 回调:Informer Add/Remove 触发 enqueueUpdate,goroutine processUpdate 调用 stream.Send(Update)。默认队列容量为 100,队列满时递增 endpoint_updates_queue_overflow 指标并关闭流(endStream),迫使 proxy 重连。这是发现轴上的“流落后”信号,不是 xDS NACK。

Linkerd 如何为端点富化 mTLS 身份和 zone 信息?

在 createWeightedAddr 中,如果 Pod 受同一控制面管理且端口未在 skip-inbound-ports 中跳过,会构造 {sa}.{ns}.serviceaccount.identity.{control-ns}.{trust-domain} 作为 TlsIdentity.DnsLikeIdentity。Zone 富化通过 getNodeTopologyZone 读取源 Pod 所在节点的 topology.kubernetes.io/zone,并在 metric_labels 中写入 zone 与 zone_locality(local/remote/unknown)。实验性 ExtEndpointZoneWeights 可将同 zone 端点权重乘以 10。

当 Service 有 Endpoints 但 mesh 内连不上时,应该如何排查?

首先确认该出站目标对应的 destination.Get 流里有没有 WeightedAddr、有没有 no_endpoints{exists: true}、流是否因队列溢出被掐断。具体步骤:先确认 authority 字符串,再确认流内 Update 序列,最后才看 proxy 出站日志。不要把 istiod“EDS 为空”的 runbook 原样套到 Linkerd。

Linkerd 中空流有哪几种形态,分别代表什么问题?

空流几种可核对形态:1) no_endpoints{exists: true}:Service 存在,但 Deployment 未 Ready、selector 不匹配或 EndpointSlice 尚未生成;2) 流建立后长时间无 add:Informer 未同步、RBAC 拒绝 watch、destination Pod 未 Ready;3) 有 add 后全被 remove:滚动更新正常抖动,若持续空集回到 no_endpoints;4) 队列溢出断流:EndpointSlice churn 过快,translator 跟不上,查 endpoint_updates_queue_overflow 与 destination 资源。

🏷️

标签

➡️

继续阅读