第 6 篇
钉了四套 gRPC 与 xDS 寻址差;第 5
篇 说明 destination 容器靠 Informer
驱动。读者仍常把「Service 有 Endpoints 但 mesh
内连不上」误判成数据面或 mTLS
问题——实际要先问:该出站目标对应的
destination.Get 流里有没有
WeightedAddr、有没有
no_endpoints{exists: true}、流是否因队列溢出被掐断。
本篇展开发现轴内核:per-target
查询语义、EndpointTranslator 与 EndpointSlice
增量、identity/zone 富化,以及相对 xDS EDS
的失败落格。不写未测的发现延迟数字。
本篇在系列中的位置
篇目 核心内容 第 6 篇 · linkerd2-proxy-api 四套 gRPC、专用 API vs xDS 第 7 篇 · 服务发现流 per-target Get、EndpointTranslator、空流第 8 篇 · 策略模型 Server、AuthorizationPolicy 系列目录 关键问题、阅读路径
版本锚定:Linkerd 2.20(tag
version-2.20)。2.20 默认启用 EndpointSlice 路径(enableEndpointSlices);机制对齐controller/api/destination/server.go、endpoint_translator.go与destination.proto@ linkerd2-proxy-api v0.20.0。无真实集群则不粘贴伪造端点推送日志或 viz 指标。
一、per-target 查询语义
linkerd2-proxy 对每个出站目标发起独立的
destination.Get(GetDestination)
服务端流。GetDestination.path 形如
svc.namespace.svc.cluster.local:port;省略端口时默认
80,省略 namespace 时默认
default(server.go 注释)。
一次 Get 的生命周期:
- 解析 path → Kubernetes Service 三元组(name / namespace / port)。
- 区分本地发现、远程多集群发现、联邦服务(federated service)三条订阅路径。
- 为该流创建
EndpointTranslator,向EndpointsWatcher注册为 listener。 - 阻塞至客户端取消、控制面 shutdown、或流异常结束。
与 xDS EDS 的对照:EDS 按 Cluster 名
订阅端点集合,同一连接上可挂多种 xDS 类型;Linkerd 是
每个逻辑目标一条 gRPC 流,流内只有该目标的
Update 序列。排障口令因此是「对这个 authority
的 Get 流收到了什么」,而不是「EDS
快照里有没有这个 Cluster」。
destination.proto 规定三种
Update:
add:WeightedAddrSet,可含多个带权端点。remove:AddrSet,撤下部分地址。no_endpoints:exists: false表示服务不存在,客户端可回退 DNS;exists: true表示服务存在但无端点,客户端不得回退 DNS。
语义细节:no_endpoints 后接 add
不等价于只发 add——客户端必须按
proto 注释处理 exists
字段,否则会在重连窗口内错误清空缓存。
二、EndpointTranslator / EndpointSlice
EndpointsWatcher 基于 client-go Informer
监听 Endpoints 或 EndpointSlice(2.20 由
EnableEndpointSlices 控制)。Informer
回调必须非阻塞,因此 EndpointTranslator
用带缓冲 channel 异步处理:
Informer Add/Remove → enqueueUpdate → goroutine processUpdate → stream.Send(Update)
endpoint_translator.go 实现
EndpointUpdateListener:Add 翻译为
Update_Add,Remove 翻译为
Update_Remove。默认队列容量
DefaultStreamQueueCapacity = 100;队列满时递增
endpoint_updates_queue_overflow 并
关闭流(endStream),迫使
proxy 重连——这是发现轴上的「流落后」信号,不是 xDS
NACK。
翻译时每个 WeightedAddr 可附带:
TlsIdentity(meshed Pod 的 DNS-like 身份字符串)ProtocolHint(H2 / Opaque / opaque_transport 入站端口)metric_labels(Pod 标签、zone 等)Http2ClientParams(meshed 流量 HTTP/2 参数)
destination
RBAC(destination-rbac.yaml)在启用
EndpointSlice 时授予 endpointslices 的
list/get/watch/create/update/patch/delete——控制面既消费也必要时维护切片,与纯只读
EDS 翻译器的权限模型不同。本篇不展开 2.20 destination
内存重构细节,指针见 第 12
篇。
flowchart TD
eps["EndpointSlice Informer"] --> ew["EndpointsWatcher"]
ew -->|"Add/Remove"| et["EndpointTranslator"]
et -->|"stream Send"| get["destination.Get stream"]
get --> proxy["linkerd2-proxy outbound"]
三、identity/zone 富化
端点从 Kubernetes 地址到 proxy 可消费的
WeightedAddr,中间有一层
富化,排障时常与「无端点」混淆:
mTLS
身份:createWeightedAddr 在 Pod
受同一控制面管理、端口未在 skip-inbound-ports
中跳过时,构造
{sa}.{ns}.serviceaccount.identity.{control-ns}.{trust-domain}
作为
TlsIdentity.DnsLikeIdentity。无身份富化的端点可能是未
mesh 工作负载、opaque 端口、或 ExternalWorkload 的 URI-like
身份——需与 第 4
篇 轴 2 分列。
Zone
富化:getNodeTopologyZone 读取源 Pod
所在节点的 topology.kubernetes.io/zone,在
metric_labels 写入 zone 与
zone_locality(local /
remote / unknown)。实验性
ExtEndpointZoneWeights 可把同 zone 端点权重乘以
10——机制门,非 benchmark。
协议提示:annotation / Server CRD 决定的
opaque 端口、forceOpaqueTransport、H2 upgrade
影响 ProtocolHint,与 第 3
篇 的 opaque/inbound 端口轴衔接。
富化失败(如读不到 inbound 端口环境变量)会在 translator 日志记 error 并 跳过该地址,表象是「Endpoints 有 Ready 地址但流里少了几台」,不是 Service 不存在。
四、相对 xDS EDS 失败落格
| 症状 | xDS / EDS 常见落格 | Linkerd destination 落格 |
|---|---|---|
| 配置被拒 | NACK、warming、PushOrder | 不适用;查 gRPC 状态码与流是否存活 |
| 集群无端点 | EDS 空 ClusterLoadAssignment | no_endpoints{exists: true} 或空
add |
| 服务不存在 | CDS/EDS 无 Cluster 或 404 类行为 | NotFound 或
no_endpoints{exists: false} |
| 推送落后 | Delta 版本错乱、SotW 全量重推 | endpoint_updates_queue_overflow、流被关闭 |
| 错后端 | EDS 订阅错 Cluster 名 | Get path 解析错、远程发现 label 错 |
口诀:先确认 authority 字符串,再确认流内 Update 序列,最后才看 proxy 出站日志。 不要把 istiod「EDS 为空」的 runbook 原样套到 Linkerd。
与「对端未 mesh」区分:未 mesh 的对端可能仍有 TCP 端点但
无 TlsIdentity
或握手失败;no_endpoints{exists: true} 则是
discovery 层认定 Service 存在但当前无 Ready 地址——第 13 篇
五轴会分列。
五、空流与延迟
空流几种可核对形态:
no_endpoints{exists: true}:Service 存在,Deployment 未 Ready、selector 不匹配、或 EndpointSlice 尚未生成——Kubernetes 轴 + 发现轴。- 流建立后长时间无
add:Informer 未同步、RBAC 拒绝 watch、destination Pod 未 Ready——控制面健康轴。 - 有
add后全被 remove:滚动更新正常抖动;若持续空集回到no_endpoints。 - 队列溢出断流:EndpointSlice churn
过快,translator 跟不上;查
endpoint_updates_queue_overflow与 destination 资源(2.20 内存优化见第 12 篇)。
延迟:proto 要求订阅开始尽快首包;客户端重连可暂用旧缓存直至收到首条新消息。Informer 反射延迟 + translator 队列深度决定「Kubernetes 已 Ready 但 mesh 仍用旧端点」的窗口——本篇无实测延迟数字,不写成 SLA。
开放问题(工程判断):per-target 流在超大服务名基数下连接税高于 xDS 多路复用——第 16 篇 选型收束时回指;机制事实是不做 type-subscription 聚合。
参考资料
规范 / 官方文档 / 源码(A)
linkerd2-proxy-apiproto/destination.proto(Get、Update、NoEndpoints语义)@ v0.20.0linkerd/linkerd2version-2.20:controller/api/destination/server.go;endpoint_translator.gocharts/linkerd-control-plane/templates/destination-rbac.yaml(EndpointSlice RBAC)
博客 / 维护者文章(B)
- Linkerd 官方博客,Deep Dive: How linkerd-destination works(EndpointSlice / 四容器分工)
站内对照(A/B)
下一篇:策略模型
读完这篇,下一步读什么
优先读同系列或同问题的下一篇,把单篇消费变成主题集群。
【Linkerd】destination 架构:四容器 Pod 与 Informer 驱动
拆解 linkerd-destination Pod 内 destination、sp-validator、policy 与反身 proxy 四容器分工;Informer/EndpointTranslator 如何把 Kubernetes 状态流式推给数据面,并钉住 2.20 内存优化门。
【Linkerd】控制面全景:缺口、五轴坐标系与 16 篇路线
相对 istio-xds/14 对照篇与 architecture/76 税文,钉清 Linkerd 2.20 非 xDS 控制面缺口;定义注入、identity、destination、策略与数据面五条轴,并强调 native sidecar 与 legacy 路径分列证据包。
【Linkerd】linkerd2-proxy-api:专用 gRPC 与 xDS 寻址差
钉清 Linkerd 2.20 控制面与 linkerd2-proxy 之间的四套专用 gRPC 服务(destination/identity/inbound/tap),说明 per-target Get 流式寻址与 xDS type-subscription 的机制差,以及错误落在 gRPC 层而非 ACK/NACK。
【Linkerd】运维与升级:证书轮转与 2.20 destination 内存门
按 2.20 tag 钉控制面升级顺序、trust anchor/issuer 分层轮转、destination 内存重构运维门;附 live vs tag 门禁与否证检查表。无集群则不写升级成功叙事。