土法炼钢 · 系统与基础设施

【Linkerd】destination 架构:四容器 Pod 与 Informer 驱动

文章导航

分类入口
kubernetesnetwork
标签入口
#linkerd#destination#endpoint-translator#informer#policy#sp-validator#v2.20

目录

identity 解决「我是谁、信任谁」;destination 解决「目标服务有哪些端点、允许什么策略、路由/重试/超时如何」——并通过 linkerd2-proxy-apidestination gRPC 流式推给每个 outbound proxy。读者若把 destination 当成「轻量 Pilot」却用 xDS 口令(CDS/EDS type URL)排障,会查错订阅形状;istio-xds/14 已说明应按目标 Get() 流理解。

本篇回答:destination Deployment 内部四容器各做什么,Kubernetes 事件如何变成 proxy 可见的端点更新,2.20 内存优化改变了什么。 API 字段级语义见本系列第 6–7 篇;本篇钉架构与 Informer 驱动模型。

本篇在系列中的位置

篇目 核心内容
第 4 篇 · identity 服务 CSR、mTLS 轮转
第 5 篇 · destination 架构 四容器、Informer、2.20 内存门
第 6 篇 · linkerd2-proxy-api gRPC 服务与 xDS 对照
系列目录 关键问题、阅读路径

版本锚定:Linkerd 2.20(源码 tag version-2.20)。架构对齐 Architecture destination 节、2026 维护者博客 Deep Dive: How linkerd-destination works in the Linkerd Service Mesh,以及 linkerd/linkerd2controller/api/destination(@ version-2.20)。无真实集群则不粘贴伪造 destination.Get 流内容或内存曲线。


一、destination 控制器

官方 Architecturedestination 服务列为数据面 proxy 的「行为配置源」:服务发现(发到哪里、对端期望 TLS 身份)、策略信息、ServiceProfile(按路由指标/重试/超时)等均经此服务提供。

实现上,linkerd-destination Pod 内的 destination 容器(Go)是事件驱动控制器:通过 client-goInformer / Reflector 监视 Kubernetes API(Pod、Service、EndpointSlice 等),避免对 apiserver 轮询。2026 年官方深度文强调:当 Pod 上线或 EndpointSlice 变更时,事件进入 EndpointTranslator 等翻译层,将 K8s 对象转为 proxy 可消费的端点描述(地址、权重、协议提示、身份元数据等)。

数据面向 destination 的交互形状是 按目标查询的长连接流:proxy 对某一权威名称(如 my-svc.ns.svc.cluster.local:port)调用 destination.Get(),服务端保持 gRPC 流打开并 推送后续变更——这与 xDS 的「按资源类型 + 名字集合订阅」不同,排障时应问「这条目标的 Get 流是否活着」,而不是「EDS 订阅 ACK 了吗」。

Get() 外,同一 API 还提供 GetProfile() 流,承载 ServiceProfile 中的按路由指标、重试与超时(第 9 篇规划)。destination 容器把 EndpointSlice 变更与 Profile 变更汇合为 proxy 可见的响应;sp-validator 与 policy 容器则在 admission 或并行 watch 路径上提前拦截非法输入,避免污染已建立的流状态。

flowchart LR
  k8s["Kubernetes API watches"] --> informer["Informer Reflector"]
  informer --> translator["EndpointTranslator"]
  translator --> grpc["destination.Get stream"]
  grpc --> proxy["linkerd2-proxy outbound"]

图:控制面从事件到 per-target 流;节点英文标签便于渲染。

谱系:Informer 模式源自 Kubernetes controller 生态(Borca et al. 的控制器模式实践),Linkerd 将其用于「把集群动态状态投影到 mesh 路由面」。争论点在于 per-target 流 的可扩展性:订阅粒度细、语义直接,但在超大服务名扇出时连接与状态成本成为开放问题(本系列第 16 篇收束;不在此写未测数字)。

与 istiod 合并进程 + xDS 广播 对照:Pilot 侧常为多种资源类型维护统一推送管道;Linkerd destination 为每个活跃目标维护独立 gRPC 流,2.20 内存优化减少的是等价过滤器订阅者之间的重复状态,不是把协议改成 xDS。来自 istio-xds 系列的读者应把「无 EDS 订阅」翻译成「检查该 service 的 Get() 流与 EndpointSlice watch 是否一致」。


二、sp-validator

同一 Pod 内的 sp-validator 容器运行 ValidatingAdmissionWebhook,在 ServiceProfile 资源持久化到 etcd 之前校验语法与约束。官方深度文将其定位为「守门人」:坏配置被挡在集群外,避免 destination 向数千 proxy 推送无效 Profile。

与 destination 控制器的关系是 分工而非串行依赖

组件 触发时机 失败后果
sp-validator ServiceProfile CREATE/UPDATE admission 资源被拒绝,客户端收到 webhook 错误
destination 运行时 watch 已合法 CR Profile 进入发现响应

排障「路由/重试不生效」时,先确认 ServiceProfile 是否通过校验(轴 4),再查 destination 是否 watch 到该资源(轴 3–4 交界)。本篇不展开每条 Profile 字段;见本系列第 9 篇规划。

Webhook 失败与运行时失败的证据包不同:前者在 kubectl apply 时返回 Denied 与 webhook 消息;后者 Profile 已存在但 proxy 仍用默认路由——需查 GetProfile() 流是否建立、destination 日志是否 watch 到对应 Service 名。不要把 admission 成功误当作「L7 行为已生效」,中间还有 destination 索引与流推送延迟(第 7 篇展开,不写未测延迟数字)。


三、policy 容器

policy 容器是独立控制器,求值 ServerAuthorizationPolicyMeshTLSAuthentication 等策略 CRD(Linkerd 2.x policy 模型),并把结果供 destination 组合进对 proxy 的响应。官方深度文强调「解耦」:策略求值不与端点翻译缠在同一 goroutine 热路径,便于独立扩缩与故障隔离。

轴 4(策略)与轴 3(发现)在 proxy 侧汇合:outbound 发起连接前需要「端点列表 + 是否允许 + mTLS 要求」;inbound 侧由数据面 enforcement(第 10 篇规划)。destination Pod 内的 policy 容器负责 把 K8s 策略对象变成控制面决策,而非在应用 Pod 内执行 iptables。

策略求值与端点翻译解耦的好处是:policy 控制器可独立升级或扩容,且 admission 阶段的 ServiceProfile 错误不会污染已运行的 Get() 流。代价是排障「403 / denied」时必须分清是 policy 容器 deny 还是 数据面 inbound enforcement,二者在指标与日志入口不同(第 8、13 篇规划)。


四、反身 proxy

destination Pod 的第四个容器是注入的 linkerd-proxy——控制面「吃自己的狗粮」:组件间 gRPC 与控制面 HTTP 流量同样经 mesh,获得 mTLS 与黄金指标。这与三组件各自 Deployment 拓扑一致:不是三个裸 Pod,而是 meshed 控制面

反身 proxy 的运维含义:

flowchart TB
  subgraph dest_pod ["linkerd-destination pod"]
    dest["destination"]
    spv["sp-validator"]
    pol["policy"]
    px["linkerd-proxy"]
  end
  k8sapi["Kubernetes API"] --> dest
  k8sapi --> pol
  adm["ServiceProfile admission"] --> spv
  dest --> px
  pol --> dest

图:四容器分工;gRPC 对外仍通过 Service 暴露,Pod 内协作。

EndpointTranslator 的角色(官方 2026 深度文):EndpointSlice 事件进入 translator 后,被转换为带地址、权重、可用区与 TLS 身份提示的端点集,再附着到对应目标的 Get() 流。translator 不替代 kubelet 就绪语义——selector 错误时 destination 会诚实推送空集,与「对端未 mesh」或「策略拒绝」表象不同(第 13 篇分列)。

四容器扩缩容:Kubernetes 对单 Pod 内容器统一调度;destination Pod 内任一容器 OOM 会导致整 Pod 重启,四进程共享同一生命周期边界。这与「三独立 Deployment」的拓扑看似矛盾——实际上 跨 Pod 的故障域(destination 挂而 identity 仍活)在 Deployment 层成立,Pod 内仍是单故障域。2.20 内存优化降低的是 destination 容器 OOM 概率,不改变四容器同 Pod 的事实。


五、2.20 内存优化门(指针 12)

Linkerd 2.20 公告将 destination 控制器内存优化列为控制面 headline:在端点变更频繁的大集群中,destination 往往占控制面内存大头。2.20 重构 内部状态管理——据公告与 changelog #15166,在负载测试中通过 在具有相同过滤条件的订阅者之间共享端点过滤与 diff 状态,内存最高约降 84%(官方 load test 口径;非本站 benchmark)。

机制含义(工程判断,依据公告措辞):

运维门登记为 第 12 篇 · 运维与升级 的主话题:升级后应结合 Prometheus 观察 linkerd-destination 内存,再调整 request/limit——不要直接把公告百分比当作你的集群节省量。本节只钉「2.20 改变了 destination 状态模型」,不展开 cert-manager 或制品边界(第 15 篇)。

工程间隙:论文与公告中的负载测试通常假设高 Pod churn、大量并发 Get() 流;若你的集群服务名扇出小、churn 低,内存下降可能不明显,但行为语义不变。相反,在 churn 极高且策略复杂的集群,destination 仍可能是第一个需要水平扩容的控制面组件——优化的是单副本内存曲线,不是取消 watch 成本。与 istiod 合并进程相比,Linkerd 选择拆分 destination 使 OOM 故障域局部化,代价是组件间 gRPC 依赖需在五轴排障中单独点名。

2.20 事实 主篇
destination 内存共享优化 有(#15166) 本篇、12
rate-limit-aware LB 有(数据面) 10
native sidecar 默认 02、03

下一篇衔接:第 6 篇将展开 linkerd2-proxy-api 四个 gRPC 服务(destination / identity / inbound / tap)的 proto 边界与相对 xDS 的寻址差;本篇四容器拓扑是理解「为何 policy 与 destination 同 Pod 却不同进程」的前提。读者若只记一个结论:destination Pod 是发现、Profile 校验、策略求值与自 mesh 的合体,2.20 优化的是其中 destination 容器的内存状态模型。

空端点 vs 流断开Get() 流因网络或 destination 重启而断开时,proxy 会按 gRPC 客户端语义重连;若 Kubernetes 侧确实无 Ready 端点,流可能仍活着但推送空集——二者在应用层都可表现为超时,但证据包不同:前者查 destination Pod 事件与 gRPC 日志,后者查 EndpointSlice 与 Service selector(第 7 篇)。


参考资料

规范 / 官方文档 / 源码(A)

站内对照

争论 / 开放问题


上一篇identity 服务

下一篇linkerd2-proxy-api

读完这篇,下一步读什么

优先读同系列或同问题的下一篇,把单篇消费变成主题集群。

2026-08-23 · kubernetes / network

【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。


By .