Kubernetes 排障从哪拆:先看流量、调度还是依赖

Kubernetes 排障从哪拆:先看流量、调度还是依赖

Kubernetes 排障从哪拆:先看流量、调度还是依赖

细分主题:Kubernetes 生产环境运维与排障实战:核心链路的逐步实现与关键代码取舍
分类:[工程技术]


以从单 CoreDNS 实例迁移到 NodeLocal DNSCache 为例,Endpoint 变更可能使kube-proxy的 IPVS 规则刷新延迟,并加剧conntrack表竞争。即使节点 CPU 利用率不高,也可能观察到 UDP 53 丢包和 DNS 延迟。

迁移核心网络链路前,应先识别 DNS、CNI 与 Ingress 的依赖关系,再逐步切换流量。


1. 重构第一刀切在哪里:剥离核心依赖链中的单点隐患

大规模 Kubernetes 集群的拓扑重构中,运维工程师最容易犯的错误就是“先剥离入口 Ingress-Nginx”。Ingress 只是南北向流量的汇聚点,真正的强依赖隐患埋藏在 DNS 解析与 CNI 插件构成的东西向控制链条中。

如果直接对 Ingress 节点进行 Pod 迁移或流量切流,Ingress 内部 upstream 动态解析组件(如lua-nginx-module内部的 resolver)会瞬间向 CoreDNS 抛出数万 QPS 的 A 记录查询。如果此时 CoreDNS 恰好处于节点亲和性调整或 Endpoint 刷新窗口期,DNS 的超时(默认 5s 延迟)将迅速沿调用链路向上传导,导致 Ingress 内部连接池全部挂起,彻底吞噬 Nginx worker 进程。

+-----------------------------------------------------------------------+ | K8s 核心网络链路渐进式拆解拓扑 | +-----------------------------------------------------------------------+ ┌──────────────────┐ │ External Client │ └────────┬─────────┘ │ ▼ ┌──────────────────────┐ │ Ingress-Nginx (Edge) │ └──────────┬───────────┘ │ ┌─────────────────────┴─────────────────────┐ │ (阶段三:Ingress Canary 切流) │ ▼ ▼ ┌──────────────────┐ ┌──────────────────┐ │ Old Node Group │ │ New Node Group │ │ (Legacy Pods) │ │ (Refactored) │ └────────┬─────────┘ └────────┬─────────┘ │ │ └──────────────────────┬────────────────────┘ │ (阶段二:NodeLocal DNSCache 剥离) │ ▼ ┌────────────────────────┐ │ NodeLocal DNSCache │ │ (DaemonSet 169.254) │ └────────────┬───────────┘ │ (阶段一:CoreDNS 独立隔离集群) │ ▼ ┌────────────────────────┐ │ CoreDNS Cluster IP │ └────────────────────────┘

拆解顺序的绝对铁律是:先沉降 DNS,再隔离 CNI 侧 Pod 路由,最后切流 Ingress。必须将全局 CoreDNS 升级为“NodeLocal DNSCache + 集中式 CoreDNS 独立节点池”的两层结构。通过在每个 Worker 节点部署 DaemonSet 监听169.254.20.10,将 95% 以上的本地解析吸收到节点内存中,切断 Ingress Nginx 与中心 DNS 之间的强耦合链路。


2. DNS 与 CNI 插件层面的防爆拆解:死锁与级联失效的防护设计

在进行 DNS 链路剥离时,最危险的隐患是 Linux 内核网络栈的conntrack冲突。UDP 协议在内核中是无状态的,当 Pod 通过ClusterIP访问 CoreDNS 时,kube-proxy依靠 IPVS 或 iptables 进行 DNAT 转换。在流量高并发场景下,如果 CoreDNS 的 Pod 发生重建或 Endpoint 列表变动,内核中大量的 UDP conntrack 表项无法及时被清除,导致后续发送给旧 CoreDNS IP 的报文被直接丢弃,触发 DNS 客户端 5 秒超时的级联失效。

sequenceDiagram autonumber participant App as 业务 Pod (Client) participant LocalDNS as NodeLocal DNSCache participant IPVS as K8s IPVS / Conntrack participant CoreDNS as CoreDNS (Upstream) Note over App, CoreDNS: 阶段一:建立本地缓存与降级机制 App->>LocalDNS: 发送 UDP 53 DNS Query alt 本地命中 (Hit) LocalDNS-->>App: 0.5ms 内返回 A 记录 else 本地未命中 (Miss) LocalDNS->>IPVS: 转发至 10.96.0.10:53 IPVS->>CoreDNS: DNAT 路由至后端的 CoreDNS Pod CoreDNS-->>LocalDNS: 返回解析结果 LocalDNS->>LocalDNS: 写入本地 LRU 缓存 LocalDNS-->>App: 返回解析结果 end Note over LocalDNS, CoreDNS: 阶段二:Upstream 抖动与双发 (Shadow) 验证 CoreDNS-->>IPVS: 节点迁移 / Endpoint 突然变更 LocalDNS->>IPVS: 发生 UDP 丢包或 200ms 超时 LocalDNS->>LocalDNS: 触发 熔断 (Circuit Breaker) LocalDNS->>CoreDNS: 降级走 TCP 53 备用通道 CoreDNS-->>LocalDNS: TCP 稳定返回 LocalDNS-->>App: 无感知交付结果

为了彻底杜绝级联失效,必须在 Pod 的dnsConfig中进行防爆设计,配置single-request-reopentimeout:1策略,避免 IPv4/IPv6 双栈查询在内核同一conntracktuple 上的竞争。同时,在部署 NodeLocal DNSCache 时,强制采用 TCP 协议向集中式 CoreDNS 向上游转发,消除 UDP 协议在 DNAT 刷新期间的丢包死锁。


3. 渐进式切流的关键代码:标准与自定义 CRD 配合的流量收敛

在核心链路的切流阶段,直接替换 Ingress 对应的 Service 节点指针是极其危险的操作。我们采用Canary渐进式切流策略,配合 Nginx Ingress Annotation 动态注入权重,同时使用 EnvoyFilter 在 Envoy/Istio 拓扑中建立流量双发(Traffic Mirroring/Shadowing)机制。

以下是实现 Ingress 流量按 5% 梯度无损切流到新构建网络拓扑架构下的生产级Ingress配置:

apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: core-api-canary namespace: prod-service annotations: kubernetes.io/ingress.class: "nginx" # 开启 Canary 灰度切流机制 nginx.ingress.kubernetes.io/canary: "true" # 设置灰度流量比例为 5% nginx.ingress.kubernetes.io/canary-weight: "5" # 强制客户端 Hash 黏性,避免跨 Session 缓存错乱 nginx.ingress.kubernetes.io/upstream-hash-by: "$remote_addr" # 针对 upstream 状态异常配置即时熔断与重试 nginx.ingress.kubernetes.io/proxy-connect-timeout: "2" nginx.ingress.kubernetes.io/proxy-read-timeout: "5" nginx.ingress.kubernetes.io/proxy-next-upstream: "error timeout http_502 http_503" nginx.ingress.kubernetes.io/proxy-next-upstream-tries: "3" spec: rules: - host: api.production.internal http: paths: - path: / pathType: Prefix backend: service: name: core-api-service-refactored port: number: 8080

如果集群已启用 Service Mesh(如 Istio),为了验证新链路中的 CNI 策略是否正确释放了防火墙与 ACL 规则,可以通过VirtualService注入流量镜像(Mirroring),将线上真实流量复制一份打入新拓扑集群,而忽略其响应,实现零风险验证:

apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: core-api-shadow-routing namespace: prod-service spec: hosts: - api.production.internal http: - route: - destination: host: core-api-service-legacy subset: v1 weight: 100 # 将 100% 的生产流量无感双发至新拓扑服务链路 mirror: host: core-api-service-refactored subset: v2 mirrorPercentage: value: 100.0

4. 线上排障命令实操:kubectl exec抓包与tcpdump流量分析

在核心网络链路拆分排障现场,不要依赖图形化 UI。你必须通过命令行直击内核网络栈和集群内部的抓包数据。

+-----------------------------------------------------------------------+ | 级联失效隔离与双发验证时序拓扑 | +-----------------------------------------------------------------------+ ┌──────────────────┐ ┌──────────────────┐ │ Netshoot Pod │ │ Target Worker │ │ (Debug Tool) │ │ (Node eth0) │ └────────┬─────────┘ └────────┬─────────┘ │ │ │ 1. kubectl exec 动态注入 │ ├─────────────────────────────►│ │ │ │ 2. tcpdump 抓取 UDP 53 │ │ (过滤 SERVFAIL / 延迟) │ ├─────────────────────────────►│ │ │ │ 3. ip route / ipvsadm 检查 │ │ (定位 DNAT 丢包与路由表) │ ├─────────────────────────────►│ │ │ ▼ ▼ ┌─────────────────────────────────────────────────┐ │ 分析结果:精准定位死锁节点与失效 Pod Endpoint │ └─────────────────────────────────────────────────┘

步骤 1:在目标命名空间临时注入netshoot网络诊断工具容器

# 启动轻量级网络诊断 Pod,挂载至主机网络栈进行底层观测 kubectl run netshoot-diagnoser --rm -it --image=nicolaka/netshoot --namespace=prod-service -- /bin/bash

步骤 2:实时抓取 DNS 查询 UDP 报文并分析响应延迟与错误码

netshoot容器内部执行以下抓包命令,精确捕获所有发送至169.254.20.10和集群 DNS 的 53 端口流量:

# 捕获eth0网卡上的DNS流量,打印绝对时间戳,不进行域名反向解析 tcpdump -i eth0 port 53 -nn -tttt -vvv | grep -E "SERVFAIL|NXDomain|refused|A\?"

诊断示例输出解析:

2026-08-09 02:15:32.104921 IP 10.244.3.15.54201 > 169.254.20.10.53: 12401+ A? api.internal.domain. (39) 2026-08-09 02:15:37.105210 IP 169.254.20.10.53 > 10.244.3.15.54201: 12401 SERVFAIL 0/0/0 (39)

响应延迟整整差了 5000ms(5 秒),极大概率是 upstream CoreDNS 连接超时触发了客户端默认退避。

步骤 3:排查 IPVS 转发规则与 Endpoint 收敛状态

如果抓包显示报文发出但没有任何回包,立即在节点上使用以下诊断命令验证 Pod Endpoint 与路由表收敛情况:

# 1. 检查 CoreDNS Service 对应的 Endpoint 列表是否包含无效 Pod IP kubectl get endpoints kube-dns -n kube-system -o wide # 2. 在 Worker 节点上查看 IPVS DNAT 路由条目与 Active/Inact 连接数 ipvsadm -ln -t 10.96.0.10:53 # 3. 检查节点路由表中针对 NodeLocal DNSCache 虚拟 IP 的路由指引 ip route show type local | grep 169.254.20.10

通过上述“先隔离 DNS 本地化、再配置 Canary 流量双发、配合底层的命令行抓包审计”三步法,可以在任何大规模 Kubernetes 集群中,彻底消除核心网络链路拆分时的死锁与抖动风暴。