1. kube-proxy 到底在集群里扮演什么角色
1.1 一个追包问题引发的思考
先抛个场景。某天你创建了一个 Service,kubectl get svc 看到 ClusterIP 是 10.96.0.10,端口 80。然后你在节点上直接 curl 10.96.0.10,通了。再换一个节点,也能通。但你有没有想过,这个包到底是谁帮你转发到后端 Pod 上的?
答案就是 kube-proxy。
我刚接触 Kubernetes 那会儿,一直以为 Service 的 ClusterIP 是某种虚拟 IP,由内核或者某个组件直接实现。后来追包才发现,真正干活的是 kube-proxy:它负责把发往 Service 的流量转发到后端 Pod。而它之所以能实现这个能力,靠的是 Linux 内核里的一堆机制,比如 iptables、IPVS,或者以前老版本里的 userspace 代理。
这篇文章我会把 kube-proxy 的工作模式、规则链路、排查思路、性能调优一次讲透。适合已经把 Kubernetes 跑起来、但对 Service 底层转发链路还不够清楚的运维和开发,也适合准备面试需要系统梳理网络原理的读者。
1.2 Service 与 kube-proxy 的分工逻辑
理解 kube-proxy,首先得把 Service 和它的关系搞清楚。Service 是 Kubernetes 里一个逻辑抽象,它定义了一组后端 Pod 的访问入口,并且通过 label selector 关联这组 Pod。但 Service 本身不干活,它只是一个声明。
真正干活的是 kube-proxy 这个 DaemonSet 跑在每个节点上的代理进程。它的任务是:持续 watch API Server 上 Service 和 EndpointSlice(旧版本是 Endpoints)的变更,然后把这种“声明式”的期望状态,转成节点上真正生效的转发规则。
我把这个关系用一句话总结:Service 是 API 层的抽象,kube-proxy 是数据面的执行者。
当初 API Server 设计上没有把网络规则下发直接塞进 kube-proxy,而是采用 watch 机制,这是 Kubernetes 一贯的控制循环思想。kube-proxy 不是被某个事件临时触发的,它始终在监听,任何 Service 增删改、Pod 变化导致端点变化,都会驱动它重新同步一遍规则。所以它更像是一个“常驻管家”,而不是“一次性搬家工人”。
1.3 三种工作模式:iptables、IPVS、userspace
kube-proxy 历史上出现过三种模式,我用一张表格先给你一个整体印象:
| 模式 | 转发机制 | 性能 | 调度算法 | 现状 |
|---|---|---|---|---|
| userspace | 用户态代理 | 最差 | 轮询 | 基本淘汰 |
| iptables | 内核态 Netfilter 规则 | 中等 | 随机 | 默认模式 |
| IPVS | 内核态 LVS 负载均衡 | 最好 | rr、wrr、lc 等 | 大规模推荐 |
userspace 模式最早出现,流量先进用户态,再由 kube-proxy 转发,链路长、性能差,现在基本不用了。
iptables 模式是目前大多数集群的默认选择。它把 Service 对应的 DNAT 规则写成 iptables 链,流量到达节点后,在 PREROUTING 或 OUTPUT 链匹配到 KUBE-SERVICES,再跳转到对应的后端规则链,最终 DNAT 到具体 Pod IP。这个模式成熟稳定,但规则多了之后性能下降明显,因为每一条新连接都要线性匹配规则。
IPVS 模式是 kube-proxy 从 1.9 版本左右引入的。IPVS 本身就是 Linux 内核专门为负载均衡设计的模块,基于哈希表匹配,时间复杂度是 O(1),规则多的时候比 iptables 快很多。这也是对大规模集群来说最推荐的模式。
关于模式切换,后面我会单独拿出来讲。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. iptables 模式的规则链与转发逻辑
2.1 从 ClusterIP 到 Pod IP 的完整链路
iptables 模式里,创建了一个 Service 之后,kube-proxy 会在 NAT 表里生成三类链:KUBE-SERVICES、KUBE-SVC-XXX、KUBE-SEP-XXX。
我拿一个实际例子拆解。假设你有一个 Service:
- ClusterIP: 10.96.0.10
- Port: 80
- 后端两个 Pod IP:172.16.1.5、172.16.1.6
你从节点上访问 10.96.0.10:80,数据包会走这条链路:
- 包进入 OUTPUT 链(本机访问)或 PREROUTING 链(外部访问),系统会自动跳到 KUBE-SERVICES 链。
- KUBE-SERVICES 里有一条匹配规则:目标 IP 是 10.96.0.10 且目标端口是 80,动作是跳转到 KUBE-SVC-XXX。
- KUBE-SVC-XXX 链里有两个后端规则,分别对应两个 Pod。每条规则用 statistic 模块的 random 模式,比如第一条概率是 50%,第二条概率是 100%。这样保证一半流量去 172.16.1.5,剩下去 172.16.1.6。
- 命中后跳转到 KUBE-SEP-XXX,这里真正做 DNAT,把目标改写为具体的 Pod IP:Port。
最后包顺着 Pod 网络(比如 Overlay 网络)到达目标 Pod,Pod 返回时源 IP 会先回到节点,再回到发起方。
注意:iptables 模式的负载均衡策略不是轮询,而是随机选择。每一条新连接建立时内核才匹配一次规则,之后的包都走 conntrack 里记录的 DNAT 结果,不会重新匹配。
2.2 NodePort 与 LoadBalancer 的特殊处理
ClusterIP 只是 Service 的一种类型。把 Service 配成 NodePort 后,kube-proxy 会额外做两件事:一是把服务质量端口暴露在节点的某个高位端口上(默认 30000-32767),二是生成对应的 iptables 规则。
NodePort 的流量链路上,包从外部进入节点的 PREROUTING 链,匹配到目标端口是 NodePort,同样会跳到对应的 KUBE-SVC 链。这里有个细节:如果请求的目的 IP 是本机 IP,那么 KUBE-SERVICES 链会有两条匹配规则,一条匹配 ClusterIP,一条匹配 NodePort,最终会汇聚到同一个 KUBE-SVC 链。
这里就容易出现一个常见问题——SNAT。当外部客户端通过 NodePort 访问时,数据包最终要被 DNAT 到某个节点上的 Pod。如果这个 Pod 不在当前节点,包还要二次转发到其他节点,这时后端的 Pod 看到的源 IP 是当前节点的 IP,而不是客户端真实 IP,所以 Kubernetes 会在这个包出去之前做一次 MASQUERADE(SNAT),保证回程包能回到中间节点。
我见过很多新手排查 NodePort 访问异常,纠结后端 Pod 拿不到真实客户端 IP,然后去改各种网络插件。其实这跟 CNI 没关系,是 kube-proxy 的 SNAT 策略设计的。如果你确实需要保留客户端真实 IP,可以设置 externalTrafficPolicy: Local,这样流量只会转发到本节点的 Pod,不做二次转发,也就不需要 SNAT。代价是如果某个节点上没有 Pod,访问这个节点的 NodePort 就会失败。
2.3 iptables 模式的问题:规则膨胀与随机转发
iptables 模式最大的痛点有两个。
一个是规则膨胀。假设一个集群有 2000 个 Service、每个 Service 5 个后端,那就是几千条规则。每次新连接进入,内核都要在 KUBE-SERVICES 链上线性匹配,最坏情况下要遍历全部规则。虽然 iptables 匹配本身在 CPU 层面很快,但规则上千条之后,延迟和 CPU 消耗都会明显上升。
另一个是随机转发的问题。iptables 的 statistic random 模式本质是概率性选择,如果后端权重有变化,或者 Pod 频繁扩缩容,短时间内连接分布会非常不均匀。哪怕两个后端权重各 50%,由于随机数本身存在波动,短时间窗口内可能出现某个后端连接特别多的情况。
所以我个人的建议是:如果集群规模不大(几百个 Service 以内),iptables 模式用着没毛病;但如果 Service 数量上千,或者对负载均衡的均匀性要求高,直接切 IPVS,别犹豫。
3. IPVS 模式:大规模集群的更优选择
3.1 IPVS 的原理与性能优势
IPVS 全称 IP Virtual Server,是 Linux 内核自带的传输层负载均衡模块,LVS 项目就是构建在它上面的。IPVS 在内核态维护一张哈希表,通过调度算法把 TCP/UDP 连接分发给后端的真实服务器。
kube-proxy 切到 IPVS 模式后,规则下发的实现方式变了:它不再去写各种 iptables 链,而是通过 netlink 接口直接在内核里创建虚拟服务(Virtual Service)。这台“虚拟服务”绑定的是 ClusterIP,后端就是一组 Pod IP。
IPVS 的性能优势主要体现在这几个方面:
- 哈希匹配,O(1) 时间复杂度,规则数量对匹配速度影响很小
- 支持原生 TCP、UDP、SCTP
- 调度算法丰富,不局限于随机
- 内核态直接转发,不经过用户态拷贝
我测试过一个 4000 多个 Service 的集群,iptables 模式下新增 Service 时 kube-proxy 同步规则要 2~5 秒,切到 IPVS 后基本 1 秒内能完成同步。对追求稳定和快速扩缩容的业务来说,这个差距还是很明显的。
3.2 调度算法选型
IPVS 模式支持多种调度算法,kube-proxy 默认是 rr(轮询)。你也可以在 kube-proxy 的 ConfigMap 里配 ipvs.scheduler 来切换。
常用算法我列一下:
| 算法 | 全称 | 特点 | 适用场景 |
|---|---|---|---|
| rr | Round Robin | 轮流分发 | 后端负载均衡,通用 |
| wrr | Weighted RR | 按权重分发 | 后端规格不一致 |
| lc | Least Connection | 最小连接数 | 长连接场景 |
| wlc | Weighted LC | 加权最小连接数 | 后端规格不一致的长连接 |
| sh | Source Hashing | 源地址哈希 | 需要会话保持 |
我的经验是:默认的 rr 在大多数场景下够用了。如果你的业务是大量长连接(比如 WebSocket、gRPC),用 lc 反而更合理,因为 rr 不考虑当前连接数,某台机器上的连接可能堆积。如果后端 Pod 规格不一致(比如 4C8G 和 2C4G 混部),用 wrr 或者 wlc,手动分配权重。
3.3 切换 IPVS 模式的完整配置
切 IPVS 模式,我建议你这几步操作,顺序不能乱。
第一步,确认内核模块已加载。IPVS 依赖的内核模块有 ip_vs、ip_vs_rr、ip_vs_wrr、ip_vs_sh,其中 ip_vs_rr 必须加载,其他的要看调度算法用不用。可以通过 lsmod | grep ip_vs 检查。如果没有,手动加载:
bash复制modprobe -- ip_vs
modprobe -- ip_vs_rr
modprobe -- ip_vs_wrr
modprobe -- ip_vs_sh
如果用的是 kubeadm 部署,初始化时一般会自动加载这些模块。但如果你是自己二进制部署,很容易漏掉这一步。
第二步,修改 kube-proxy 的 ConfigMap。在 kube-system 命名空间下,编辑 kube-proxy 配置:
bash复制kubectl -n kube-system edit configmap kube-proxy
找到 mode 字段,改成 ipvs:
yaml复制mode: "ipvs"
ipvs:
scheduler: "rr"
如果不写 mode,默认是空字符串,kube-proxy 会退回 iptables。
第三步,滚动重启 kube-proxy Pod,让配置生效:
bash复制kubectl -n kube-system rollout restart daemonset kube-proxy
第四步,验证规则是否生效:
bash复制ipvsadm -L -n
看到类似这样的输出就代表成功了:
text复制IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 10.96.0.10:80 rr
-> 172.16.1.5:8080 Masq 1 0 0
-> 172.16.1.6:8080 Masq 1 0 0
提示:IPVS 模式下 kube-proxy 仍然会用少量 iptables 规则做兜底(比如 NodePort 可能还是需要 iptables 跳转,以及 kube-proxy 自带的健康检查),这没问题,不影响整体性能。
4. kube-proxy 核心流程、参数与系统调优
4.1 核心流程:watch、sync、write
kube-proxy 不管用哪种模式,核心逻辑都遵循一套流程。理解这个流程对排查问题很有帮助。
第一步是 watch。kube-proxy 启动后,通过 client-go 库的 informer 机制监听 API Server 上的 Service、EndpointSlice、Node 等资源。informer 有本地缓存,并且带资源版本号,增量更新,不会每次全量拉取。
第二步是 sync。本地收到变更事件后,kube-proxy 把这些事件放入队列,触发一次“同步”。同步的逻辑是:重新计算当前应该有哪些 Service、哪些端点,再和之前下发的规则做对比,找出需要增删改的部分。
第三步是 write。把计算结果写到内核。iptables 模式下,它是每隔一段时间调用 iptables-restore 批量刷新规则。IPVS 模式下,它通过 netlink 调用创建/更新/删除虚拟服务。
这中间有个关键细节:kube-proxy 不会因为 API Server 一时不可用就崩溃。它本地有缓存,即使 watch 断了,也能继续用最后的状态维护转发规则。等 API Server 恢复后再重新同步。这一点在大规模故障时特别有用。
4.2 关键启动参数解读
kube-proxy 的启动参数挺多,但日常运维你主要关注这几个。
--proxy-mode:指定工作模式,可选 userspace、iptables、ipvs。命令行参数优先级高于 ConfigMap,所以如果你在 systemd unit 里写了这个参数,改 ConfigMap 是没用的。
--cluster-cidr:指定集群 Pod CIDR。这个参数主要影响的是 IPVS 模式下,kube-proxy 是否对目标地址是 Pod 网段的流量做 SNAT。如果集群开了 masquerade 但这里写错了,跨节点访问 Pod 时回包路由就会出问题。
--masquerade-all:设置为 true 时,对所有流量做 SNAT,包括集群内部访问 ClusterIP 的流量。默认是 false,kube-proxy 会按规则决定哪些流量需要 SNAT。一般不建议全局开启,会影响性能,而且会丢客户端真实 IP。
--conntrack-max-per-core:控制节点上每个 CPU 核的 conntrack 表项最大数量。这个参数和 conntrack 表满问题直接相关,后面排查部分我会展开。
4.3 性能调优与系统层参数
就算 kube-proxy 本身性能很好,如果系统层参数没调好,转发也可能出问题。这里分享几个我实际调过的参数。
首先是 conntrack 表大小。Linux 内核会为每个连接维护一条 conntrack 记录,尤其是 NAT 场景下。如果 conntrack 表满了,新连接会直接丢包,现象就是“Service 有时候通有时候不通”,非常难受。
查看当前 conntrack 使用情况:
bash复制cat /proc/sys/net/netfilter/nf_conntrack_count
cat /proc/sys/net/netfilter/nf_conntrack_max
如果 count 接近 max,就需要调大。注意这里不是随便改一个值就完了。nf_conntrack_max 建议按内存估算,通常每 4GB 内存可以支撑 65536 条左右。同时还要调整 conntrack 的哈希表大小,这个参数在 /sys/module/nf_conntrack/parameters/hashsize,它决定了查找效率。如果表项多但 hashsize 小,查找变慢,CPU 飙高。
第二是 net.ipv4.ip_forward。kube-proxy 做跨节点转发的时候,必须开启 IP 转发,否则 NodePort 到其他节点 Pod 的流量就断了。
bash复制echo 1 > /proc/sys/net/ipv4/ip_forward
第三是文件描述符限制。kube-proxy 每个实例会保持与 API Server 的多个 watch 连接,连接数不多,但它要处理大量事件,如果 ulimit -n 太低,可能出现连接被拒或异常退出。
这些参数建议写进节点初始化脚本,不要只靠手动调。因为 Kubernetes 节点重启后,这些 sysctl 配置默认不会持久化。
5. 故障排查:Service 不通时我在做什么
5.1 一条完整的排查路径
Service 不通是 Kubernetes 网络故障里最常遇到的。遇到这种问题,我一般按下面的顺序排查,基本能覆盖大部分情况。
第一步,确认 Service 和后端 Pod 的状态。先看 Service 的 selector 有没有选到 Pod,Endpoints 或者 EndpointSlice 里有没有地址:
bash复制kubectl get svc <service-name> -n <namespace>
kubectl get endpoints <service-name> -n <namespace>
kubectl get endpointslices -n <namespace> -l kubernetes.io/service-name=<service-name>
如果 Endpoints 是空的,八成是 selector 写错了,或者 Pod 没就绪。注意 readnessProbe 失败也会让 Pod 从 Endpoints 里被摘掉。
第二步,检查 kube-proxy 的日志。找到节点上对应的 kube-proxy Pod:
bash复制kubectl -n kube-system get pods -o wide | grep kube-proxy
kubectl -n kube-system logs kube-proxy-xxxxx --tail=200
日志里经常能看到类似“Failed to load kernel module ip_vs”或者规则同步报错的信息。
第三步,直接看节点上的转发规则。iptables 模式看 iptables,IPVS 模式看 ipvsadm:
bash复制iptables -t nat -L KUBE-SERVICES -n -v --line-numbers
# 或者
ipvsadm -L -n
通过规则确认这个 Service 是否真的下发到了节点。
第四步,在节点上手动抓包验证。用 tcpdump 抓一下访问 ClusterIP 的包,看看包有没有进来、有没有被 DNAT 到 Pod:
bash复制tcpdump -i any host 10.96.0.10 and port 80 -nn
如果包到了节点但没转发出去,问题大概率在 kube-proxy 规则或者 conntrack 上。
5.2 常见故障速查表
我把这几年遇到的典型问题整理成一张表,方便照着排查:
| 现象 | 可能原因 | 排查方向 |
|---|---|---|
| ClusterIP 不通,Pod IP 通 | Service 规则没下发,或 kube-proxy 异常 | 查 kube-proxy 日志、检查 iptables/ipvsadm 规则 |
| NodePort 不通,ClusterIP 通 | NodePort 端口没监听、防火墙拦截 | 看节点端口监听、查防火墙规则 |
| 有时候通有时候不通 | conntrack 表满、DPDK/网卡多队列问题 | 检查 nf_conntrack_count 是否接近 max |
| Pod 访问自身 Service 不通 | hairpin 模式问题,或 kube-proxy 没开 hairpin | 排查 hairpin 配置,设置 hairpinMode: promiscuous-bridge |
| 访问 Service 延迟高 | iptables 规则过多、网络插件问题 | 查看规则数量,考虑切 IPVS |
| 跨节点 Pod IP 不通 | 网络插件路由异常、IP 转发未开启 | 查 CNI 路由表、检查 ip_forward |
| 外部访问 NodePort 拿不到真实客户端 IP | Service externalTrafficPolicy 为 Cluster | 改成 Local 或用 LB 方案 |
5.3 几个真实案例
先讲一个 conntrack 表满的案例。有一次客户反馈某个节点上所有 Service 间歇性超时,我们上去看,ossec 之类的日志全没问题,CPU 也不高。后来查 conntrack,发现 nf_conntrack_count 已经到 65536,而 nf_conntrack_max 也是 65536,表满了。原因是一个跑批量任务的应用在短期内建立了大量 TCP 连接,占满了 conntrack。我们把 max 调大到 262144,同时把 hashsize 也调整了,再观察,超时消失。这里提醒一句,sysctl net.netfilter.nf_conntrack_max 修改后最好配合 sysctl -w net.netfilter.nf_conntrack_buckets 一起调。
另一个是 IPVS 模式下 Pod 访问自身 Service 失败的案例。集群里某个应用启动时自己调用自己的 Service 做健康检查,结果一直失败。后面定位到是 hairpin 问题——当 Pod 访问 Service IP 时,流量被 DNAT 到它自己,需要内核支持 hairpin(发夹)转发。解决办法是给 kube-proxy 设置 --hairpin-mode=promiscuous-bridge,或者使用能正确处理 hairpin 的 CNI 插件。后来确认不是 kube-proxy 的问题,是网络插件没开 hairpin,但排查过程一开始也是集中在 kube-proxy 上,所以这个坑值得记一笔。
还有一个是 kube-proxy 同步延迟导致新 Service 暂时不可用。一次改动后大量创建 Service,iptables 模式下 kube-proxy 需要重新计算并加载几千条规则,期间新 Service 会有一小段时间无法访问。后来我们直接切到 IPVS,这个延迟几乎消失了。
6. 会话保持、健康检查与 conntrack 细节
6.1 会话保持的实现原理
有些业务要求同一个客户端的请求始终落在同一个后端 Pod 上,典型场景有 WebSocket 连接、带本地缓存的 HTTP 服务、需要状态保持的有状态应用。这时候就要用到 Service 的会话保持特性。
Service 配置里有一个 sessionAffinity 字段,默认是 None,改成 ClientIP 即可开启基于客户端 IP 的会话保持:
yaml复制apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
sessionAffinity: ClientIP
sessionAffinityConfig:
clientIP:
timeoutSeconds: 10800
selector:
app: my-app
ports:
- port: 80
targetPort: 8080
这个特性在 iptables 模式下是怎么实现的?kube-proxy 会把会话保持的规则写进 iptables,通过 recent 模块记录源 IP,在一定时间内把同一个源 IP 的连接固定到同一个后端。在 IPVS 模式下,则会通过调度器实现,比如 sh(源地址哈希)算法天然支持基于源 IP 的会话保持。
不过需要注意,timeoutSeconds 默认 10800 秒,也就是 3 小时。如果你设置的会话保持时间太短,客户端请求超过这个时间后就会重新分发,可能引发业务异常。还要注意,会话保持是基于源 IP 的,如果客户端经过 NAT 或代理访问,所有客户端看起来都是同一个 IP,负载均衡效果会大打折扣。
6.2 conntrack 对转发的影响
conntrack 是 Linux 内核连接跟踪机制,kube-proxy 做 DNAT/SNAT 时,每一连接都会记录一条 conntrack entry。这就带来一个隐患:如果 conntrack 表容量不足或条目失效,会导致连接异常。
典型场景是 UDP 服务。UDP 是无连接的,但 conntrack 依然会为 UDP 流量建立跟踪记录,而且 UDP entry 的超时时间比 TCP 短一截,但如果业务大量使用 UDP 并且流量密集,conntrack 表很容易被打满。这在高并发 DNS 或游戏服务器上尤其明显。
另一个常见问题是 conntrack 条目没有及时清理。比如一个 Pod 被删除后,已有连接可能还停留在 conntrack 表里,指向一个已经不存在的后端 IP,导致后续包被丢弃。遇到这种情况,手动清一下 conntrack 条目可以临时恢复:
bash复制conntrack -D -d 10.96.0.10
但这个操作要谨慎,清了之后所有相关连接都会断。更推荐的做法是调整超时时间参数,减少过期条目堆积:
bash复制sysctl -w net.netfilter.nf_conntrack_tcp_timeout_established=3600
调整 conntrack 相关参数前,先评估业务对长连接、空闲连接的需求,不要一刀切。
6.3 健康检查与端点更新的联动
kube-proxy 虽然不做健康检查本身,但它依赖 EndpointSlice 里每个端点的 readiness 状态来决定是否把流量转发过去。这个链路是这样的:kubelet 负责执行 Pod 的 readinessProbe,把结果汇报给 API Server,API Server 更新 EndpointSlice,kube-proxy watch 到变化后更新转发规则,把不健康的 Pod 从后端列表里摘掉。
这里有个时间差的问题。从 Pod 开始不健康,到 kube-proxy 把规则更新完,中间可能有好几秒。如果正好有连接打过来,就可能转发到不健康的 Pod。应对方法有两个层面:
一是合理设置 readinessProbe 的 initialDelaySeconds 和 periodSeconds,不要过于激进,避免误判。二是接受这个时间差,在设计高可用架构时不要把单条连接的状态太当回事,尽量在应用层做重试。
我实际排查过一个案例:一个后端 Pod 已经 CrashLoopBackOff,但从 Service 访问仍然有部分请求失败。查了 EndpointSlice,发现 Pod 的 readiness 状态还没有更新,原因是 kubelet 更新上报有延迟。这个问题不是 kube-proxy 造成的,但如果不了解完整链路,很容易误判。排查的时候别忘了看一眼 Pod 状态和 Event。
写在最后的个人经验
做了这么多年 Kubernetes 网络排障,我对 kube-proxy 的体会是:它的逻辑不复杂,但坑都在细节里。模式选型、系统参数、conntrack、健康检查、hairpin,每一个点单独看都不难,组合起来却足够折腾人。如果你正在维护一个中型以上的集群,我会建议你尽早把 kube-proxy 切到 IPVS 模式,省掉规则膨胀的隐患。同时把系统参数调优写进节点初始化配置里,不要等故障了再去改。
最后分享一个小技巧:排查网络问题时,先别急着动 kube-proxy 的配置。先把 Service、EndpointSlice、kube-proxy 日志、内核转发规则这四个层面的状态看清楚,基本能定位 90% 的问题。剩下的 10%,就交给 tcpdump 和 conntrack 慢慢查。
