Linkerd:从 native sidecar 到身份、策略与可回退流量治理
凌晨的订单发布看起来一切正常:orders-v2 已经滚动完成,client 与订单 Pod 都多了一个代理容器,控制面检查也全部变绿。可安全同事从一个没有注入代理的临时 Pod 发起请求,仍然拿到了订单响应。值班同学先怀疑证书失效,随后又发现代理指标里的连接确实标记为 TLS。两个证据都是真的,却回答了不同问题:已入网格的两端可以自动建立 mTLS,不代表目标会自动拒绝所有明文来源。
继续排查时,另一个矛盾浮出来:发布人员把重试写到了路由上,但一次写请求在上游抖动时执行了两遍;准备回滚的人删掉控制面后,业务 Pod 里的旧代理仍在等待已经不存在的 identity 与 policy 服务。事故不是某条 YAML 写错,而是团队把“安装成功、身份认证、业务授权、流量韧性和退出”当成了同一个开关。Linkerd 要真正进入生产链路,这五件事必须分别建立证据。
先看清一条请求真正经过哪里
Linkerd 是面向 Kubernetes 的专用服务网格。控制面通常位于 linkerd namespace:injector 在 Pod 创建时修改规格,destination 向代理提供 endpoint、协议和目标身份,identity 签发工作负载证书,policy 分发入站授权与路由配置。linkerd CLI 通常运行在工程师终端,通过 Kubernetes API 读取这些状态;它不在业务请求路径上。
业务请求的数据面仍然是每个已纳管 Pod 内的 Rust linkerd2-proxy。在 2.20 中,代理默认采用 Kubernetes native sidecar 形态,生命周期由 sidecar container 语义协调,但流量仍要由 Linkerd CNI 或 linkerd-init 设置的 iptables 规则重定向。native sidecar 解决启动、终止与 Job 生命周期问题,不等于代理进了内核,也不等于不再需要网络接管。
这张图也给出了第一条排障纪律:应用返回、代理本地拒绝、上游返回和控制面状态是四层证据。linkerd check 全绿只说明已知组件和配置检查通过;Pod 多一个容器只说明准入发生过;单次 200 只说明某条请求成功。必须再确认两端身份、代理实际版本、命中 endpoint、策略结论和真实请求路径。
固定 distribution,再安装 CLI 与控制面
Linkerd 项目当前直接产出 edge 工件,稳定分发与生产支持由供应商提供。2.20 是产品/代码版本,并对应具体 edge artifact;生产不能把 install-edge 脚本理解为不带供应商责任的“稳定版”。选型时先从 Releases and Versions 确认代码版本和工件关系,再为所选 distribution 记录 CLI 来源、镜像仓库、支持矩阵、签名或摘要、SLA 与升级渠道。开源仓库采用 Apache License 2.0,也不自动覆盖供应商附加组件和支持合同。
CLI 安装到管理员工作站或受控运维容器后,先保存版本并执行前检:
linkerd version --client
kubectl config current-context
linkerd check --pre前检需要配合 Kubernetes 兼容矩阵 与 Gateway API 支持矩阵 判断。Gateway API CRD 由平台独立管理;Linkerd 2.20 使用 gateway.networking.k8s.io/v1 的 HTTPRoute、GRPCRoute,但并不实现所有 Gateway API 资源和过滤器。先固定 Kubernetes、Gateway API bundle、CNI、Linkerd distribution 和 CLI 版本,再生成清单,避免让浮动版本进入 GitOps。
隔离集群可用 CLI 生成两阶段 manifest。命令把 YAML 输出到标准输出,适合先保存、审查 RBAC 与镜像,再提交:
linkerd install --crds > linkerd-crds.yaml
linkerd install --ha > linkerd-control-plane.yaml
kubectl diff -f linkerd-crds.yaml
kubectl diff -f linkerd-control-plane.yaml
kubectl apply -f linkerd-crds.yaml
kubectl apply -f linkerd-control-plane.yaml
linkerd check长期环境更适合 Helm 安装:先安装 linkerd-crds,再安装 linkerd-control-plane。Helm 与 CLI 最大的操作差异不是命令风格,而是 Helm 不替你生成身份根与 issuer。团队必须预先提供 identityTrustAnchorsPEM、identity.issuer.tls.crtPEM、identity.issuer.tls.keyPEM,或采用经过验证的 cert-manager 流程。
LINKERD_CHART_REPO="<所选 distribution 的 Helm 仓库别名>"
LINKERD_CHART_VERSION="<已校准并固定的 chart 版本>"
helm pull --untar "${LINKERD_CHART_REPO}/linkerd-control-plane" \
--version "${LINKERD_CHART_VERSION}"
helm upgrade --install linkerd-crds "${LINKERD_CHART_REPO}/linkerd-crds" \
--namespace linkerd --create-namespace \
--version "${LINKERD_CHART_VERSION}" \
-f linkerd-crds-values.yaml
helm upgrade --install linkerd-control-plane "${LINKERD_CHART_REPO}/linkerd-control-plane" \
--namespace linkerd \
--version "${LINKERD_CHART_VERSION}" \
-f linkerd-control-plane/values-ha.yaml \
-f linkerd-values.yaml
linkerd checkvalues-ha.yaml 必须从同一个已固定 chart 包中提取,和 chart 一起校验摘要、审查差异,不能从浮动分支下载后套到另一个版本。它会把关键控制面扩为三个副本,并设置资源请求和反亲和;它不会替团队保证故障域真的分开,也不会替 identity、Kubernetes API、DNS 与节点网络提供冗余。linkerd-values.yaml 应由密钥系统在部署时注入私密字段,Git 中只保留引用和非敏感配置。trust anchor 是验证根,issuer 是为代理叶证书签名的中间职责,两者轮换节奏不同。默认工作负载叶证书有效期较短并由代理自动轮换;issuer 与 trust anchor 不会因此自动完成长期轮换。监控必须同时覆盖叶证书、issuer 到期、根信任分发和节点时钟偏差。
几个安装字段会直接改变运行语义:
| 字段或选择 | 改变的执行面 | 配错后的现场 |
|---|---|---|
proxy.defaultInboundPolicy | 所有未被下级覆盖的入站默认策略 | 保持 all-unauthenticated 时,自动 mTLS 仍不阻止明文来源 |
| native sidecar / legacy sidecar | 代理与应用容器的启动、终止顺序 | Job 不退出、应用早于代理启动或终止时连接被截断 |
Linkerd CNI / linkerd-init | iptables 规则由节点插件还是 Pod init 写入 | 缺少权限或链冲突时,容器存在但流量绕过代理 |
| identity trust/issuer | 代理证书签发与验证 | issuer 过期会让新证书续签失败,根不一致会让对端拒绝 |
| access log 与 Viz | 遥测量、敏感字段和 CPU/存储 | 高流量下尾延迟、时序基数与日志费用上升 |
高可用先证明故障域,再证明副本数
Linkerd 的 HA 配置会扩展 destination、identity、policy 和 proxy injector 等关键组件,并为它们提供反亲和与生产资源基线。三副本仍可能被调度到同一节点池、同一可用区或同一电源域;软反亲和在资源不足时也可能让步。安装后要把 Deployment、Pod、Node 和可用区放在同一份证据里:
kubectl -n linkerd get deploy
kubectl -n linkerd get pod -o custom-columns='POD:.metadata.name,NODE:.spec.nodeName,READY:.status.containerStatuses[*].ready'
kubectl get node -L topology.kubernetes.io/zone
kubectl get mutatingwebhookconfiguration linkerd-proxy-injector-webhook-config -o yaml
kubectl -n linkerd get endpointslice -l kubernetes.io/service-name=linkerd-proxy-injector预期关键 Deployment 的期望副本为三,Pod 跨节点和可用区分布,webhook service 至少有一个健康 endpoint。HA 模式会把 proxy injector 的失败策略收紧:已标记注入的 Pod 在 injector 不可用时应被准入拒绝,而不是无代理落地。这个 fail-closed 选择保护了流量身份边界,也意味着 injector、API server 到 webhook 的网络和证书一旦全断,业务发布会停住。
隔离环境应逐个删除一台承载控制面 Pod 的节点或临时缩掉一个关键 Pod,再同时观察四条链路:既有代理请求、新 Pod 准入、策略 generation 传播和新证书签发。单副本损失时,既有请求和新部署应继续,控制面副本应在预算时间内恢复;若只看到旧连接没断,不能证明 HA。随后在实验集群把 injector endpoints 全部阻断,带注入标记的新 Pod 应创建失败,事件中应出现 webhook 错误,未注入 Pod 数仍为零。恢复网络后再创建一次,证明发布链恢复。
PodDisruptionBudget 只约束自愿驱逐,不约束节点宕机、进程崩溃或错误 rollout。升级前还要检查可调度余量,避免 PDB、反亲和和资源不足互相锁死。控制面 RTO 以“新代理可以加入、策略可传播、证书可签发”计时;数据面 RTO 则以请求成功率和长尾恢复计时,两者不能用同一个 Ready 指标替代。
把 mesh-lab 的三个工作负载逐步纳管
先在 mesh-lab 部署返回固定版本标识的 orders-v1、orders-v2,以及发起请求的 client。三个 Deployment 分别使用 client、orders ServiceAccount,不把默认 ServiceAccount 当成长期身份。下面用公开测试镜像建立可执行骨架;进入团队环境时应把镜像同步到受控仓库并固定 digest。纳管前保存直连基线:Pod IP、Service endpoint、响应中的 version、延迟分布和容器列表。
kubectl create namespace mesh-lab
kubectl -n mesh-lab create serviceaccount client
kubectl -n mesh-lab create serviceaccount orders
kubectl -n mesh-lab create deployment orders-v1 \
--image=hashicorp/http-echo:1.0.0 --port=8080 -- \
/http-echo '-listen=:8080' -text=v1
kubectl -n mesh-lab create deployment orders-v2 \
--image=hashicorp/http-echo:1.0.0 --port=8080 -- \
/http-echo '-listen=:8080' -text=v2
kubectl -n mesh-lab create deployment client \
--image=curlimages/curl:8.14.1 -- sleep infinity
kubectl -n mesh-lab set serviceaccount deployment/orders-v1 orders
kubectl -n mesh-lab set serviceaccount deployment/orders-v2 orders
kubectl -n mesh-lab set serviceaccount deployment/client client
kubectl -n mesh-lab patch deployment orders-v1 --type=merge \
-p '{"spec":{"template":{"metadata":{"labels":{"app.kubernetes.io/name":"orders","app.kubernetes.io/version":"v1"}}}}}'
kubectl -n mesh-lab patch deployment orders-v2 --type=merge \
-p '{"spec":{"template":{"metadata":{"labels":{"app.kubernetes.io/name":"orders","app.kubernetes.io/version":"v2"}}}}}'
kubectl -n mesh-lab expose deployment orders-v1 --port=8080 --target-port=8080
kubectl -n mesh-lab expose deployment orders-v2 --port=8080 --target-port=8080
kubectl -n mesh-lab create service clusterip orders --tcp=8080:8080
kubectl -n mesh-lab patch service orders --type=merge \
-p '{"spec":{"selector":{"app":"orders-v1"}}}'
kubectl -n mesh-lab rollout status deploy/client
kubectl -n mesh-lab get pod -o wide
kubectl -n mesh-lab exec deploy/client -- curl -fsS http://orders:8080/orders这里先让稳定入口 orders 指向 v1;后续 HTTPRoute 接管后,它再把请求分给 orders-v1 与 orders-v2。若基础请求尚未返回 v1,不要继续注入代理。
给 namespace 增加注解只会影响后续创建的 Pod。存量 Pod 不会原地得到代理,因此要显式滚动:
kubectl annotate namespace mesh-lab linkerd.io/inject=enabled
kubectl -n mesh-lab rollout restart deploy/client deploy/orders-v1 deploy/orders-v2
kubectl -n mesh-lab rollout status deploy/client
kubectl -n mesh-lab get pod -o jsonpath='{range .items[*]}{.metadata.name}{" regular="}{.spec.containers[*].name}{" native/init="}{.spec.initContainers[*].name}{"\n"}{end}'
linkerd check --proxy
linkerd version --proxy2.20 默认 native sidecar 时,linkerd-proxy 位于 initContainers 且带 restartPolicy: Always;关闭 native sidecar 后它才位于普通 containers。使用 CNI 时不应据此期待 linkerd-init,不用 CNI 时则要核对负责重定向的 init container 和 NET_ADMIN 相关事件。再从 client 连续请求订单 Service,确认应用响应、两端代理指标和实际 endpoint 同时变化。若容器存在但指标没有请求,优先查 Pod 是否在注解前创建、端口是否被 skip、hostNetwork/opaque 配置以及 iptables/CNI 是否真正接管。
正向实验:证明身份、授权与版本路由同时成立
Linkerd 代理启动后在内存型 tmpfs 中生成私钥,通过 CSR 从 identity 取得绑定 Kubernetes ServiceAccount 的证书,私钥不离开 Pod。只有通信双方都经过 Linkerd 代理时,才具备 automatic mTLS 的完整条件。Viz 不是核心控制面的内置 UI,需要从所选 distribution 对应的 CLI/工件独立安装和升级;先审查生成清单,再使用 Viz 命令:
linkerd viz install > linkerd-viz.yaml
kubectl diff -f linkerd-viz.yaml
kubectl apply -f linkerd-viz.yaml
linkerd viz check随后保存两端证据:
linkerd -n mesh-lab identity deploy/client
linkerd -n mesh-lab identity deploy/orders-v1
linkerd viz -n mesh-lab edges deploy
linkerd viz -n mesh-lab stat deploy接着把订单端口定义为受策略管理的 Server,用 MeshTLSAuthentication 选择 client ServiceAccount,再由 AuthorizationPolicy 关联目标和认证条件。字段名应以目标版本的 Authorization Policy 为准,下面的核心关系是“端口目标、可信客户端、允许关联”三步,而不是一条万能 allow:
apiVersion: policy.linkerd.io/v1beta3
kind: Server
metadata:
name: orders-http
namespace: mesh-lab
spec:
podSelector:
matchLabels:
app.kubernetes.io/name: orders
port: 8080
proxyProtocol: HTTP/1
---
apiVersion: policy.linkerd.io/v1alpha1
kind: MeshTLSAuthentication
metadata:
name: client-sa
namespace: mesh-lab
spec:
identityRefs:
- kind: ServiceAccount
name: client
---
apiVersion: policy.linkerd.io/v1alpha1
kind: AuthorizationPolicy
metadata:
name: client-to-orders
namespace: mesh-lab
spec:
targetRef:
group: policy.linkerd.io
kind: Server
name: orders-http
requiredAuthenticationRefs:
- name: client-sa
kind: MeshTLSAuthentication
group: policy.linkerd.io把三个对象保存为 orders-policy.yaml 并执行 kubectl apply -f orders-policy.yaml。从正确身份请求应成功;证据至少包括源、目标 identity,授权对象 generation,代理已加载的策略,以及连续请求计数。然后创建指向订单 Service 的 producer HTTPRoute,把 90% 权重给 v1、10% 给 v2。Linkerd 的 Service parent reference 使用 group: core;这不是通用 Gateway parent 的默认组值。Route 资源只负责选择,两个 Service selector 必须分别只命中自己的版本:
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: orders-split
namespace: mesh-lab
spec:
parentRefs:
- group: core
kind: Service
name: orders
port: 8080
rules:
- backendRefs:
- name: orders-v1
port: 8080
weight: 90
- name: orders-v2
port: 8080
weight: 10把该对象保存为 orders-route.yaml 并执行 kubectl apply -f orders-route.yaml。不能用十次请求验证精确权重。应固定连接复用方式,发送足够样本,分别统计响应版本和两个后端代理请求数;同时读取 Route 的 status.parents[].conditions,确认 observed generation 已追上 spec。Accepted=True 说明控制器接受配置,不证明 endpoint 可达,真实流量计数才补上最后一环。
反向实验:让错误身份和重试放大显形
第一个反例是保留目标策略,分别让临时 client 使用 default ServiceAccount、完全不注入代理。可以创建两个临时 Deployment;namespace 默认注入使 wrong-client 获得错误 mesh identity,而 workload 注解让 plain-client 明确跳过注入:
kubectl -n mesh-lab create deployment wrong-client \
--image=curlimages/curl:8.14.1 -- sleep infinity
kubectl -n mesh-lab create deployment plain-client \
--image=curlimages/curl:8.14.1 -- sleep infinity
kubectl -n mesh-lab patch deployment plain-client --type=merge \
-p '{"spec":{"template":{"metadata":{"annotations":{"linkerd.io/inject":"disabled"}}}}}'
kubectl -n mesh-lab rollout status deployment/wrong-client
kubectl -n mesh-lab rollout status deployment/plain-client
kubectl -n mesh-lab exec deploy/wrong-client -- \
curl -sS -o /dev/null -w '%{http_code}\n' http://orders:8080/orders
kubectl -n mesh-lab exec deploy/plain-client -- \
curl -sS -o /dev/null -w '%{http_code}\n' http://orders:8080/orders两种来源访问同一订单端口都应被目标代理拒绝,而不是由订单应用返回业务 403。判断时同时查看客户端错误、orders inbound proxy 的授权指标/日志和应用访问日志:应用没有收到请求而代理出现 deny,才能证明执行点正确。
linkerd -n mesh-lab authz deploy/orders-v1
linkerd viz -n mesh-lab stat deploy/orders-v1
kubectl -n mesh-lab logs deploy/orders-v1 -c linkerd-proxy --since=5m
kubectl -n mesh-lab logs deploy/orders-v1 -c http-echo --since=5m取证完成后立即删除两个临时来源,避免它们继续制造拒绝流量:kubectl -n mesh-lab delete deployment wrong-client plain-client。
若未注入的 client 成功,先检查 Server selector 和端口名是否命中、是否仍受更宽松的默认入站策略、请求是否绕过代理端口。若错误身份被当成正确身份,检查实际 Pod 的 serviceAccountName,不要从 Deployment 名猜身份。NetworkAuthentication 只证明 CIDR 来源,不等同于 ServiceAccount 的密码学身份。
第二个反例是在仅供读取的 Route 上配置一次受限重试,并让 orders-v2 可控地首个 attempt 返回 503。Linkerd 的重试和超时主要在调用方 outbound proxy 执行,因此未纳管 client 不会获得同样行为。证据应出现一个客户端逻辑请求、两个上游 attempts、最终成功,以及总 deadline 内的耗时。把同样策略错误地作用到非幂等写请求时,即使最终 200,两个订单副作用也应触发失败判据。
重试预算要写成等式:总尝试数 = 原始请求数 × 每层最大 attempts。客户端 SDK、Linkerd proxy、入口网关和上游如果各自重试,乘法会迅速耗尽连接、CPU 和下游配额。超时要覆盖排队、连接、每次 attempt 和 backoff;本地限流策略按代理实例计数,副本扩缩会改变集群总容量,不能充当全局配额。
项目接入时把策略与应用发布绑定
真实项目不应让平台组单独维护一份与应用版本脱节的网格 YAML。工作负载仓库至少共同版本化 ServiceAccount、端口名、注入注解、Server、认证对象、授权策略、Route 和回滚值;平台仓库持有 cluster-wide 默认策略、Gateway API CRD、trust anchor 发行与 admission/RBAC。业务 owner 决定允许哪些调用、哪些方法可重试,平台 owner 决定身份材料、默认拒绝和高权限控制面。
策略推广按“观察、窄目标、扩大”进行。先对一个非关键 Deployment 验证代理、identity 和 Route status,再收紧单端口授权;错误身份和明文反例稳定拒绝后,才推广 namespace 默认策略。audit 可以发现潜在阻断,但不产生真实安全边界;上线门禁仍要有实际 deny 请求。
凭证与敏感数据也要分层治理。issuer 私钥、trust bundle 变更、Tap 输出、代理访问日志、Route 配置转储和拓扑都可能暴露服务名、调用关系、请求头与身份。Secret 只授权给控制面 ServiceAccount;工程师通过短期、可审计的 Kubernetes 权限使用 tap 和诊断命令;日志在进入集中平台前删除令牌、Cookie 和业务敏感字段。示例中的 example.test 不能替换成真实内部域名后直接提交公开仓库。
从现象沿四层证据排障
当请求失败时,先判定错误由谁产生,再动配置:
客户端 DNS/连接失败:检查 Service、EndpointSlice、destination 返回和 outbound proxy 日志。代理本地拒绝:检查 Server 是否命中端口、实际源 identity、AuthorizationPolicy 和默认入站策略。mTLS 握手失败:检查双方是否纳管、证书有效期、issuer/trust anchor、时钟和目标身份。
上游 5xx:检查命中 endpoint、应用日志、重试 attempts;不要把上游错误归为控制面不同步。Route 无效:比较 generation、observed generation、condition reason,核对 Gateway API CRD 与 Linkerd 兼容版本。Viz 空白:确认 Viz 自身健康、Prometheus 抓取、时间窗和真实流量;拓扑空白不自动等于数据面中断。
控制面短时不可用时,代理可能继续使用既有或缓存状态,但 endpoint 变化、策略变更、证书续签和新代理启动的表现不同。隔离环境应分别断开 destination、policy、identity,观察既有连接、新连接与新 Pod,不能用“老连接没断”概括控制面容灾。诊断包和配置转储按敏感资产存储,设置短保留期并限制下载。
容量、成本与架构选择
Linkerd 的优势是专用微代理、成熟的 ServiceAccount 身份和自动 mTLS,且无需替换现有 CNI;代价是每个 Pod 都有代理,注入、版本偏差、证书和遥测都随 Pod 数增长。容量测试要固定协议、payload、连接复用、RPS、Pod 密度和策略规模,分别记录应用基线与入网格后的 p50/p99、代理 CPU/RSS、连接队列、reset、控制面收敛时间和 Prometheus active series。
平均 CPU 很低不能证明峰值安全。证书轮换、批量 rollout、EndpointSlice 风暴、控制面重连和 Viz 抓取可能同时发生;应在这些事件下观察长尾。access log 和 Tap 会增加 CPU、尾延迟与敏感数据面,Viz 默认更适合短期诊断,长期保留应接入有容量预算的 Prometheus/日志平台。成本模型至少包含每 Pod requests/limits、控制面副本、遥测存储、供应商 distribution/support 与升级演练人力。
若团队首要需求是保留现有 CNI、快速获得稳定的工作负载 mTLS 与身份授权,并能接受 per-Pod proxy,Linkerd 通常更直接。若目标是把 CNI、NetworkPolicy、节点网络、eBPF 可观测与服务网格统一为一个平台,且愿意承担内核、节点权限和 CNI 迁移,Cilium 更可能合适。两者不能拿各自官方基准横向比较;必须在同一集群、同一负载和同一遥测配置下测量。
升级不是只替换控制面镜像
升级前导出 Helm values、策略与 Route、Gateway API ownership、issuer/trust 状态、扩展版本和代理版本分布。CLI 路径按 CRD、控制面、旧资源清理执行:
linkerd upgrade --crds > linkerd-crds-next.yaml
linkerd upgrade > linkerd-control-plane-next.yaml
kubectl diff -f linkerd-crds-next.yaml
kubectl diff -f linkerd-control-plane-next.yaml
kubectl apply -f linkerd-crds-next.yaml
kubectl apply -f linkerd-control-plane-next.yaml
linkerd prune > linkerd-prune.yaml
kubectl delete -f linkerd-prune.yamlHelm 同样先升级 linkerd-crds 再升级 linkerd-control-plane。跨 chart 版本要审查新默认值,不机械使用 --reuse-values。控制面完成后分 cohort 滚动业务 Pod,并用 linkerd version --proxy、linkerd check --proxy 找出旧数据面;扩展独立升级。Gateway API ownership 从旧集群迁移时尤其不能直接切换开关,否则可能连 CRD 和依赖对象一起删除,应按 ownership migration 先交给外部 owner。
回滚要区分配置回退、流量回切、控制面二进制回退和 CRD 状态恢复。每一步都重新执行正确身份成功、错误身份拒绝、两后端路由和代理证书检查。Deployment Ready 只证明进程存活,不证明旧代理能够解释新策略。
退出时先让业务脱离,再删平台
安全退出从恢复非网格语义开始:确认应用或替代设施承担 TLS、授权、超时重试、观测和跨集群访问;把 Route 权重回到稳定路径,撤销策略依赖。随后移除注入注解并滚动每个 workload:
kubectl annotate namespace mesh-lab linkerd.io/inject-
kubectl -n mesh-lab rollout restart deploy/client deploy/orders-v1 deploy/orders-v2
kubectl -n mesh-lab rollout status deploy/client
kubectl -n mesh-lab get pod -o jsonpath='{range .items[*]}{.metadata.name}{" regular="}{.spec.containers[*].name}{" native/init="}{.spec.initContainers[*].name}{"\n"}{end}'确认新 Pod 无 linkerd-proxy、真实请求不再命中旧代理、替代告警正常之后,先删除 Viz 等扩展,最后按 卸载指南 生成并审查核心删除清单:
linkerd viz uninstall > linkerd-viz-delete.yaml
kubectl delete -f linkerd-viz-delete.yaml
linkerd uninstall > linkerd-delete.yaml
kubectl delete -f linkerd-delete.yaml最后扫描残留的 webhook、CRD/CR、RBAC、namespace 注解、Secret、Service、代理连接和遥测抓取目标。Gateway API CRD 若由平台或其他控制器共用,不得随 Linkerd 删除;issuer 与跨集群凭据在确认无消费者后撤销。隔离实验确认不再需要证据后,删除测试 namespace,并确认 namespace 终止且测试 Pod、策略与 Route 都已消失:
kubectl delete namespace mesh-lab --wait=true
kubectl get namespace mesh-lab第二条命令预期返回 NotFound;若 namespace 卡在 Terminating,先检查 finalizer,不得直接删除共享 CRD。这样交付的不是“控制面消失”,而是业务已经拥有可解释、可验证、可回退的非网格路径。
