测试环境 K8s 502 故障复盘:被 ClusterIP 表象误导,最终定位 kube-proxy 规则缺失

故障时间 :2026-08-06 上午

影响范围 :互联网医院系统测试环境(devcucc7000.xxx.cn),titan-cms、titan-doctor 服务持续 502,影响测试人员测试与交付进度!!

恢复时间 :全量重启 kube-proxy 后恢复

核心根因:kube-proxy 未能为 service-titan-zuul 生成 iptables/IPVS 转发规则,导致 ClusterIP 成为网络黑洞


一、背景:医生pc端业务突然大面积 502

当天上午,互联网医院系统测试环境访问异常【其实这种网络问题一般是运维来处理,但是我们的运维最近太忙了,只能自己先排查,给运维一个方向。第一次遇见这个kube-proxy 网络问题,k8s自己忘得差不多了,知识不用就容易忘啊,还好有AI。花了2天才最终确认问题,多亏是测试环境!!!如果是生产,运维还不在的情况,岂不是炸了!!! 】。查看 Nginx access.log,titan-cmstitan-doctor 相关接口持续返回 502 Bad Gateway,典型请求路径包括:

bash 复制代码
GET /api-ih/titan-cms/imSendMessage/doctorHeartbeat?doctorId=3160
GET /api-ih/titan-doctor/consultingRoom/counselDetailList/
POST /api-ih/titan-member/tenant/reg/list

环境信息:

  • 网关:Tengine 2.3.2,跑在 K8s 节点(132.xx.xx.81、132.xx.xx.244)的 Docker 容器内
  • Nginx upstream 配置使用的是 K8s Service 域名:service-titan-zuul.thc-dev-bjhlwyy-devihmb1.svc.unprod.bjhlwyy.xxxx.xxxx:9000
  • DNS 解析结果:10.xx.xx.210:9000
  • Zuul Pod:app-titan-zuul-0,实际 Pod IP 为 10.xx.xx.152

奇怪的是,member 服务看起来是正常的。为什么只有 cms 和 doctor 炸?这个疑问一直贯穿整个排查过程。


二、排查过程:从 Nginx 到 K8s 网络层的逐层剥离

2.1初步排查

cms服务我在kuboard(K8s的可视化管理界面)上看到是正常运行的啊,也能看到启动日志和一些定时任务日志的触发?只不过说不健康,刚开始怀疑是这个问题导致的:

  • Readiness Probe 失败 → K8s 认为这个 Pod 没准备好,会把它从 Service 的 Endpoints 中移除
  • 结果就是:流量不会被转发到这个 Pod,直到探针恢复正常
  • 但是当我触发接口的时候,发现请求根本没到cms服务。会不会是cms服务的代码有问题,导致测试环境cms服务没有真正起来?那本地起一下试试?
  • 本地起完发现都没问题,cms服务也注册到eureka上了?等等,那会不会是线上服务没有注册到eureka上?

  • 会不会是服务没有注册到eureka上,导致服务不可达出现502?【经过排查,测试环境的cms服务也注册到eureka上了!!!我丢,那这是啥原因啊!!!

  • 那会不会是zuul报的异常:502 Bad Gateway?

  • 但是经过排查,发现请求也没有到zuul!!!在zuul网关中没有查到相关日志!!!无解了!!

  • 哎,既然请求没有到cms,没有到zuul?那再往前推一层,会不会是nginx的问题啊【之前重启zuul网关的时候,发现每次重启zuul的pod节点ip都会变化,不会是nginx中用了硬编码了吧?写死ip,一直访问旧的zuul的IP?不会吧,不能这么蠢吧!!!!】?看看nginx中有没有异常日志!!!!

2.2 第一步:确认 Nginx 层

先找 Nginx 进程,确认它跑在容器里:

bash 复制代码
ps -ef | grep nginx

输出:

bash 复制代码
root  64233  64218  0  2025 ?  00:00:00 nginx: master process /opt/container/instances/nginx/sbin/nginx -g daemon off;

通过 /proc/<pid>/cgroup 确认是 K8s Pod,

bash 复制代码
cat /proc/19265/cgroup
bash 复制代码
kubepods-burstable-pod250dcfbd_4d45_4558_b355_c04b5603756c.slice/docker-6f243f01afbfa970...

docker exec 进入容器。

bash 复制代码
docker exec -it 6f243f01afbfa970 bash

查 upstream 配置:

bash 复制代码
grep -r "titan-zuul" conf/

发现配置没问题,upstream 用的是 Service 域名 ,不是硬编码 IP【还好没这么蠢】:

nginx 复制代码
upstream usm_thc-dev-bjhlwyy-devihmb1_everxxxxx.cn_titan-zuul {
    server service-titan-zuul.thc-dev-bjhlwyy-devihmb1.svc.unprod.bjhlwyy.xxxx.thc:9000;
}

2.3 第二步:Nginx 日志暴露 upstream 不可达

(1)分析access.log:
bash 复制代码
grep -E "titan-cms|titan-doctor" access.log | tail -n 20

access.log 里所有 502 请求的 upstream 地址都是 10.xx.xx.210:9000,响应时间从 0.078s 到 5.850s 不等:

bash 复制代码
2026-08-06T09:27:18+08:00||132.xx.xx.94||...||GET||https||devcucc7000.everxxxxx.cn
  ||/api-ih/titan-cms/imSendMessage/doctorHeartbeat?doctorId=13120052
  ||502||0.336||10.xx.xx.210:9000||502||0.337
  ||<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
  <html><head><title>502 Bad Gateway</title></head>
  <body><center><h1>502 Bad Gateway</h1></center>
  <hr/>Powered by Tengine/2.3.2</body></html>

2026-08-06T09:27:18+08:00||132.xx.xx.94||...||GET||https||devcucc7000.everxxxxxx.cn
  ||/api-ih/titan-cms/imSendMessage/doctorHeartbeat?doctorId=3160
  ||502||3.374||10.xx.xx.210:9000||502||3.375

观察:

  • 所有请求状态码均为 502
  • upstream 地址统一为 10.xx.xx.210:9000
  • 响应时间 0.078s ~ 5.850s 不等(部分快速失败,部分超时)
(2)分析 error.log:
bash 复制代码
tail -n 100 error.log | grep -i "502\|upstream\|titan-cms\|titan-doctor"

关键错误信息:

bash 复制代码
[error] connect() failed (113: No route to host) while connecting to upstream,
  client: 132.xx.xx.94,
  server: ~^(devihmb1.everxxxxx.cn|NULL)$,
  request: "GET /api-ih/titan-cms/imSendMessage/doctorHeartbeat?doctorId=3160 HTTP/1.0",
  upstream: "http://10.57.163.210:9000/titan-cms/imSendMessage/doctorHeartbeat?doctorId=3160",
  host: "devcucc7000.everxxxx.cn"

error.log 里两种错误交替出现:

复制代码
connect() failed (113: No route to host) while connecting to upstream
connect() failed (110: Connection timed out) while connecting to upstream

这说明 Nginx 连不上 10.xx.xx.210:9000

2.4 第三步:网络层验证------能 ping 通,但端口完全拒绝

在 Nginx 容器所在节点测试:网络层可达,ping 正常。

bash 复制代码
ping 10.xx.xx.210
# 64 bytes from 10.xx.xx.210: icmp_seq=1 ttl=64 time=0.052 ms
# 13 packets transmitted, 13 received, 0% packet loss

ping 正常,但 telnet 端口直接暴毙:

bash 复制代码
# 节点 132.xx.xx.81
telnet 10.x.xx.210 9000
# telnet: connect to address 10.xx.xx.210: No route to host

# 节点 132.xx.xx.244
telnet 10.xx.xx.210 9000
# telnet: connect to address 10.xx.xx.210: Connection timed out

这时候我怀疑是不是防火墙或者网络策略出了问题,但其他服务又没问题,所以暂时没往全局网络故障上想。

2.5 第四步:DNS 解析没问题,但 IP 身份存疑

在 Nginx 容器内解析域名:

bash 复制代码
getent hosts service-titan-zuul.thc-dev-bjhlwyy-devixxx.svc.unprod.xxx.kuber.thc

输出:

bash 复制代码
10.xx.xx.210   service-titan-zuul.thc-dev-bjhlwyy-devihxxx.svc.unprod.xxx.kuber.thc

DNS 解析结果和日志里报错的ip一致,说明不是 Nginx DNS 缓存的问题

2.6 第五步:进 Zuul Pod 验证服务本身

(1)进入 app-titan-zuul-0
bash 复制代码
hostname -i
# 10.xx.xx.152

curl -v --connect-timeout 5 http://localhost:9000/
# < HTTP/1.1 404
# {"timestamp":"2026-08-06 09:59:26","status":404,"error":"Not Found",...}

404 是正常响应,说明 Zuul 服务本身活着,端口也开着。

(2)从 Nginx 容器直接 curl Pod IP:
bash 复制代码
curl -v --connect-timeout 5 http://10.xx.xx.152:9000/
# Connected to 10.xx.xx.152 (10.xx.xx.152) port 9000
# < HTTP/1.1 404

Pod IP152 直接访问完全正常,但 ClusterIP 210就是不通。


三、踩坑记录:被 ClusterIP 和 Pod IP 的概念搞混了

坑一:误把 ClusterIP 当成"旧 Pod IP"

一开始我写的第一版报告,根因分析是这么写的:

"K8s Service 的 Endpoints 指向了一个已不存在的旧 Pod IP 10.xx.x.210,而当前实际运行的 Zuul Pod IP 为 10.x.xx.152。"

这个推断是错的【运维人员还是很专业的,虽然运维没空处理这个问题,但是运维一眼便看出来你这个方向是有问题的!!!!没办法,继续排查,找到问题再给运维确认!!!!】。

后来在 Pod 里查看环境变量,直接被打脸:

bash 复制代码
env | grep -i "TITAN_ZUUL"

输出:

复制代码
SERVICE_TITAN_ZUUL_SERVICE_HOST=10.xx.xx.210
SERVICE_TITAN_ZUUL_PORT=tcp://10.xx.xx.210:9000
SERVICE_TITAN_ZUUL_PORT_9000_TCP_ADDR=10.xx.xx.210

10.xx.xx.210 是 Service 的 ClusterIP,不是 Pod IP。 ClusterIP 是 K8s 分配的虚拟 IP,不会随 Pod 重启而变化。真正的问题是 ClusterIP 背后的转发规则没有指向当前 Pod,而不是"IP 变旧了"。

坑二:在精简容器里找网络工具,浪费了不少时间服务器我没权限啊,运维只给开了这个权限!!!

Zuul 的容器镜像是精简版,没有 ipsstcpdumpnslookup 这些工具。我一开始还想在 Pod 里抓包、看连接状态,结果:

bash 复制代码
ss -tlnp | grep 9000
# bash: ss: command not found

tcpdump -i any -n port 9000 -c 20
# bash: tcpdump: command not found

后来才反应过来,这种环境只能靠 /dev/tcp 做端口探测,或者用 curl + timeout 这种最原始的方式验证。

坑三:member 为什么正常?一开始没想清楚

这是整个排查过程中最大的疑问。如果所有流量都走同一个 zuul,member 也应该 502 才对。

我在 Pod 里测试了各个 Service 的连通性:

bash 复制代码
# member 的 Service
curl -s -o /dev/null -w "%{http_code}" --connect-timeout 3 \
  http://service-titan-member.thc-dev-bjhlwyy-xxxx.svc.unprod.bjhlwyy.kuber.thc:9000/
# 200

# cms 的 Service
curl -s -o /dev/null -w "%{http_code}" --connect-timeout 3 \
  http://service-titan-cms.thc-dev-bjhlwyy-xxxx.svc.unprod.bjhlwyy.kuber.thc:9000/
# 000 FAIL

member 的 Service 通,cms 的 Service 也不通。 这说明不只是 zuul 一个服务有问题,cms 的 ClusterIP 同样成了黑洞。

但 Nginx 层 member 接口正常,大概率是因为 member 在 Nginx 配置里不经过 zuul?,直接走了自己的 upstream。这个点虽然没影响最终根因定位,但确实干扰了早期的判断------让我一度以为故障是因为nginx的配置出了问题(例如:会不会是硬编码啊?会不会cms服务与member 走的不是同一个zuul啊?会不会member 根本没走zuul?打住,这些判断通通错误!!!)


四、根因定位:kube-proxy 丢了转发规则

4.1 关键证据:只有特定 ClusterIP 不通

我在 Zuul Pod 内部用 /dev/tcp 做了一组对比测试**【这个是确认问题的关键!!不容易啊,终于让我找到问题了!!!】**:

bash 复制代码
# K8s API Server(ClusterIP)
timeout 3 bash -c 'echo > /dev/tcp/10.xx.xx.1/443' && echo "API Server: 通" || echo "API Server: 不通"
# API Server: 通

# CoreDNS(ClusterIP)
timeout 3 bash -c 'echo > /dev/tcp/10.xx.xx.2/53' && echo "CoreDNS: 通" || echo "CoreDNS: 不通"
# CoreDNS: 通

# Zuul Service(ClusterIP)
timeout 3 bash -c 'echo > /dev/tcp/10.xx.xx.210/9000' && echo "Zuul Service: 通" || echo "Zuul Service: 不通"
# bash: connect: No route to host
# Zuul Service: 不通

# Pod 自身 IP
timeout 3 bash -c 'echo > /dev/tcp/10.xx.xx.153/9000' && echo "Pod自身IP: 通" || echo "Pod自身IP: 不通"
# Pod自身IP: 通

# localhost
timeout 3 bash -c 'echo > /dev/tcp/127.0.0.1/9000' && echo "localhost: 通" || echo "localhost: 不通"
# localhost: 通

这组数据非常关键:

  • API Server 和 CoreDNS 的 ClusterIP 都能通 → CNI 网络插件(Calico)和 Pod 网络本身没问题
  • 唯独 zuul 的 ClusterIP 不通 → 不是全局网络故障
  • Pod 自身 IP 和 localhost 都能通 → Zuul 应用完全健康

4.2 核心定位( Pod 无法通过 Service 的 ClusterIP 访问自己)

又执行了几个测试:

bash 复制代码
[root@app-titan-zuul-0 spring]# # 测试 1:Zuul 自身是否响应(从 Pod 内部)
[root@app-titan-zuul-0 spring]# curl -I http://localhost:9000/actuator/health
HTTP/1.1 200 
Content-Type: application/vnd.spring-boot.actuator.v2+json;charset=UTF-8
Transfer-Encoding: chunked
Date: Wed, 12 Aug 2026 02:40:05 GMT

[root@app-titan-zuul-0 spring]# 
[root@app-titan-zuul-0 spring]# # 测试 2:通过 Service ClusterIP 访问自身(从 Pod 内部)
[root@app-titan-zuul-0 spring]# curl -I http://10.xx.xx.210:9000/actuator/health

curl: (7) Failed connect to 10.xx.xx.210:9000; No route to host
[root@app-titan-zuul-0 spring]# 
[root@app-titan-zuul-0 spring]# # 测试 3:模拟完整请求路径
[root@app-titan-zuul-0 spring]# curl -I http://10.xx.xx.210:9000/api-ih/titan-cms/imSendMessage/doctorHeartbeat?doctorId=3160
curl: (7) Failed connect to 10.xx.xx.210:9000; No route to host
[root@app-titan-zuul-0 spring]# 
  • 测试 1 成功curl localhost:9000 返回 200,证明 Zuul 应用本身是健康的,正在正常运行。
  • 测试 2 失败curl 10.xx.xx.210:9000 返回 No route to host,这证明了 Pod 无法通过 Service 的 ClusterIP 访问自己

极大概率是 Pod 所在节点的 kube-proxy 组件异常CNI 网络插件(如 Calico, Flannel)故障

简单来说,Kubernetes 集群内部有一个"虚拟网络" ,Service 的 ClusterIP 就是这个虚拟网络里的一个"虚拟地址"。kube-proxy 组件负责设置规则,把所有发往这个"虚拟地址"的流量,正确地转发到后端的真实 Pod 上。

现在的情况是,这个"虚拟网络 "的规则在 Pod 所在的节点上失效了。流量从 Pod 发出后,找不到去往 10.xx.xx.210 这个地址的路,所以直接报错 No route to host

4.3 控制面确认:Endpoints 其实是对的

通过 Kuboard 查看,Service -> Endpoints -> Pod 的链路看起来是正常的:

  • Service 配置service-titan-zuul 的 ClusterIP 为 10.xx.xx.210,端口 9000,配置正确。
  • Endpoints 状态 :已正确关联 Pod app-titan-zuul-0,IP 为 10.xx.xx.153 (Pod 重建后从 10.xx.xx.152 变过来的),状态显示 Ready
  • Label 匹配:Service Selector 与 Pod Labels 匹配正常,没有不匹配的情况。
  • Pod 重建记录:Pod 确实被重建过,但 Endpoints 已经自动更新为新 IP,控制面同步看起来没问题。

也就是说,K8s 控制面认为一切正常,但数据面就是不通。

4.4 唯一的可能:kube-proxy 没写规则

ClusterIP 的访问路径是这样的:

typescript 复制代码
Pod/Nginx 发起请求 -> ClusterIP (10.xx.xx.210)
    -> kube-proxy 的 iptables/IPVS 规则做 DNAT
        -> 转发到后端 Pod IP (10.xx.xx.153)
        
┌─────────────────────────────────────────────────────────┐
│                     Kubernetes 集群                       │
│                                                          │
│   ┌──────────────────────┐                               │
│   │    Nginx/Pod         │                               │
│   │    (客户端)           │                               │
│   │                      │                               │
│   │    curl              │                               │
│   │    10.xx.            │                               │
│   │    xx.210           │                               │
│   │    :9000             │                               │
│   └──────────────────────┘                               │
│          │                                               │
│          ▼                                               │
│     ① 发起请求到 Service ClusterIP (10.xx.xx.210)          │
│          ▼                                               │
│   ┌──────────────────────┐                               │
│   │  kube-proxy 转发规则   │                               │
│   │ (iptables/IPVS DNAT) │                               │
│   │                      │                               │
│   │  ⚠ 规则缺失或错误!    │                               │
│   │  没有 ClusterIP       │                               │
│   │  → Pod IP 的映射      │                               │
│   └──────────────────────┘                               │
│          │                                               │
│          ▼                                               │
│   ┌──────────────────────┐                               │
│   │  ② 找不到转发规则      │                               │
│   │  内核返回              │                               │
│   │  "No route to host"  │                               │
│   │          ▼           │                               │
│   │  ✗ 请求失败            │                               │
│   │  (无法到达任何后端 Pod) │                               │
│   └──────────────────────┘                               │
│          │                                               │
│          ✗  流量中断,到不了 ↓                              │
│                                                          │
│   ┌──────────────────────┐                               │
│   │  app-titan-zuul-0    │                               │
│   │  Pod (服务端)         │                               │
│   │                      │                               │
│   │  10.xx.xx.153:9000    │                               │
│   │  (可达但未被路由到)    │                               │
│   └──────────────────────┘                               │
│                                                          │
└─────────────────────────────────────────────────────────┘

现在控制面(Endpoints)正确,但数据面不通,唯一的解释就是 kube-proxy 没有为这条 Service 生成对应的 iptables/IPVS 转发规则 。请求到达 ClusterIP 后,内核找不到路由,直接返回 No route to host

4.5 通过cms服务再次验证ClusterIP是否通

bash 复制代码
[root@app-titan-zuul-0 /]# # 测试 1:直连 CMS Pod IP(验证 Pod-to-Pod 网络是否通)
[root@app-titan-zuul-0 /]# timeout 3 bash -c 'echo > /dev/tcp/10.xx.xx.50/9000' && echo "CMS Pod IP (10.xx.xx.50:9000): 通 ✅" || echo "CMS Pod IP (10.xx.xx.50:9000): 不通 ❌"
CMS Pod IP (10.xx.xx.50:9000): 通 ✅

[root@app-titan-zuul-0 /]# # 测试 2:通过短服务名访问(验证同命名空间 DNS 解析 + Service 转发)
[root@app-titan-zuul-0 /]# timeout 3 bash -c 'echo > /dev/tcp/service-titan-cms/9000' && echo "CMS Service 短名 (service-titan-cms:9000): 通 ✅" || echo "CMS Service 短名 (service-titan-cms:9000): 不通 ❌"
CMS Service 短名 (service-titan-cms:9000): 不通 ❌

[root@app-titan-zuul-0 /]# # 测试 3:通过 FQDN 全名访问(验证跨命名空间 DNS 解析)
[root@app-titan-zuul-0 /]# timeout 3 bash -c 'echo > /dev/tcp/service-titan-cms.thc-dev-xxx-xxx.svc.unprod.bjhlwyy.kuber.thc/9000' && echo "CMS Service FQDN: 通 ✅" || echo "CMS Service FQDN: 不通 ❌"
CMS Service FQDN: 不通 ❌

[root@app-titan-zuul-0 /]# # 测试 4:查看 service-titan-cms 的 DNS 解析结果(看解析到哪个 IP)
[root@app-titan-zuul-0 /]# cat /etc/hosts | grep cms
[root@app-titan-zuul-0 /]# # 如果没结果,用以下方式尝试解析:
[root@app-titan-zuul-0 /]# python3 -c 'import socket; print(socket.gethostbyname("service-titan-cms"))' 2>/dev/null || \
> python -c 'import socket; print(socket.gethostbyname("service-titan-cms"))' 2>/dev/null || \
> echo "无法解析,需要手动获取 CMS 的 ClusterIP"
10.xx.xx.123

核心发现:不仅是 Zuul,CMS 的 ClusterIP 也不通

服务名称 Pod IP(直连) Service ClusterIP(通过 DNS 解析) 结论
Zuul 10.xx.xx.153:9000 ✅ 通 10.xx.xx.210:9000 ❌ 不通 ClusterIP 规则缺失
CMS 10.xx.xx.50:9000 ✅ 通 10.xx.xx.123:9000 ❌ 不通 ClusterIP 规则缺失

结论:

  • Pod 底层网络(CNI)是完全正常的(Pod 互访畅通)。
  • DNS 解析也是正常的(能正确解析出 10.xx.xx.123)。
  • 根本原因: 该节点(或集群)的 kube-proxy 彻底罢工或卡死了,导致它没有为任何 Service 写入底层的 iptables 或 IPVS 转发规则。

4.6 在 Pod 内部继续深挖(调用 K8s API 排查)

(1)检查 CMS 的 Endpoints 绑定情况(确认后端 Pod IP 是否注册成功)
bash 复制代码
[root@app-titan-zuul-0 /]# # 1. 查询 CMS 的 Endpoints
[root@app-titan-zuul-0 /]# curl -s -k -H "Authorization: Bearer $TOKEN" https://10.xx.xx.1/api/v1/namespaces/thc-dev-xx-xx/endpoints/service-titan-cms
{
  "kind": "Status",
  "apiVersion": "v1",
  "metadata": {
    
  },
  "status": "Failure",
  "message": "endpoints \"service-titan-cms\" is forbidden: User \"system:serviceaccount:thc-dev-xx-xx:default\" cannot get resource \"endpoints\" in API group \"\" in the namespace \"thc-dev-xx-xx\"",
  "reason": "Forbidden",
  "details": {
    "name": "service-titan-cms",
    "kind": "endpoints"
  },
  "code": 403
}
(2)检查 Zuul 的 Endpoints 绑定情况
bash 复制代码
[root@app-titan-zuul-0 /]# # 2. 查询 Zuul 的 Endpoints
[root@app-titan-zuul-0 /]# curl -s -k -H "Authorization: Bearer $TOKEN" https://10.xx.xx.1/api/v1/namespaces/thc-dev-xx-xx/endpoints/service-titan-zuul
{
  "kind": "Status",
  "apiVersion": "v1",
  "metadata": {
    
  },
  "status": "Failure",
  "message": "endpoints \"service-titan-zuul\" is forbidden: User \"system:serviceaccount:thc-dev-xx-xx:default\" cannot get resource \"endpoints\" in API group \"\" in the namespace \"thc-dev-xx-xx\"",
  "reason": "Forbidden",
  "details": {
    "name": "service-titan-zuul",
    "kind": "endpoints"
  },
  "code": 403
}[root@app-titan-zuul-0 /]# 
(3)最终结论:集群的 kube-proxy 组件彻底故障

我们之前的排查已经确认:

  1. Pod 间网络是通的app-titan-zuul-0 能直接访问 app-titan-cms-0 的 Pod IP。
  2. Service 的 ClusterIP 是不通的:无论是 Zuul 还是 CMS,它们的 ClusterIP 都无法访问。

现在,这个 403 错误揭示了根本原因:

kube-proxy 组件用于连接 Kubernetes API Server 的 ServiceAccount 权限被撤销了。

kube-proxy 的工作就是监听 API Server 中 Service 和 Endpoints 的变化,然后在每个节点上更新 iptables/IPVS 规则。现在,它因为权限不足(cannot get resource "endpoints"),无法获取到最新的 Endpoints 列表,导致它无法为任何 Service 生成转发规则。

这完美解释了为什么:

  • Pod 到 Pod 直连正常:因为底层 CNI 网络是独立的。
  • 所有 Service 的 ClusterIP 都不通 :因为 kube-proxy 无法工作,集群内没有任何 Service 的转发规则。

4.7 深挖 kube-proxy 权限问题【可选,如果还想深挖,可以试试】

既然怀疑 kube-proxy,那就得看看它为什么没写规则。最可能的原因是 kube-proxy 的 ServiceAccount 权限出了问题,导致它无法 watch API Server 的 endpoints 资源,进而无法生成或更新任何 Service 的转发规则。

在有 kubectl 权限的节点上,执行了以下排查命令:

(1)查看 kube-proxy Pod 状态和日志

bash 复制代码
# 查看所有 kube-proxy Pod
kubectl get pods -n kube-system -l k8s-app=kube-proxy

# 查看具体节点上的 kube-proxy Pod
kubectl get pods -n kube-system -o wide | grep kube-proxy | grep 132.xx.xx.76

# 查看日志,重点关注是否有 403 Forbidden 或 cannot list/watch endpoints 等权限错误
kubectl logs -n kube-system <kube-proxy-pod-name> --tail=100

(2)检查 ClusterRoleBinding

bash 复制代码
# 查看 kube-proxy 的 ClusterRoleBinding 是否存在
kubectl get clusterrolebinding | grep kube-proxy

# 查看绑定的 ClusterRole 是否包含 endpoints 的 get/list/watch 权限
kubectl describe clusterrolebinding kube-proxy

(3)检查 ServiceAccount

bash 复制代码
# 确认 kube-system namespace 下的 kube-proxy ServiceAccount 状态
kubectl get sa -n kube-system kube-proxy

(4)检查节点上的 iptables 规则(辅助验证)

bash 复制代码
# 在故障节点上直接查看 iptables nat 表,看是否有 10.xx.xx.210 的相关规则
iptables -t nat -nL | grep 10.xx.xx.210

# 或者查看 KUBE-SVC 链
iptables -t nat -nL KUBE-SVC-XXX | grep 10.xx.xx.210

排查发现,kube-proxy 的 ClusterRoleBinding 可能被误删或修改 ,导致其 ServiceAccount(system:serviceaccount:kube-system:kube-proxy)失去了对 endpoints 资源的 list/watch 权限。kube-proxy 无法感知 Endpoints 的变更,自然也就不会生成新的 iptables/IPVS 转发规则。


五、解决方案:全量重启 kube-proxy

5.1 修复 ClusterRoleBinding(如权限丢失)

如果确认是 ClusterRoleBinding 丢失,先重新绑定:

bash 复制代码
kubectl apply -f - <<EOF
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: kube-proxy
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: system:node-proxier
subjects:
- kind: ServiceAccount
  name: kube-proxy
  namespace: kube-system
EOF

5.2 全量重启 kube-proxy(建议)

确认根因后,操作就很简单了。在 Master 节点或具有 kubectl 权限的跳板机上执行:

bash 复制代码
kubectl rollout restart daemonset kube-proxy -n kube-system

kube-proxy 的 DaemonSet 控制器会自动在每个节点上重建 Pod。新 Pod 启动后,会重新从 API Server 拉取所有 Service 和 Endpoints 信息,生成正确的 iptables/IPVS 规则。

5.3 重启后验证

(1)在 Zuul Pod 内测试 ClusterIP 连通性

bash 复制代码
timeout 3 bash -c 'echo > /dev/tcp/10.xx.xx.210/9000' && echo "Zuul Service: 通" || echo "Zuul Service: 不通"
# Zuul Service: 通

(2)验证业务接口

bash 复制代码
curl -v http://devcucc7000.xxx.cn/api-ih/titan-cms/admin/v1.1/getServiceItem/10017/
# 返回正常

(3)检查 kube-proxy 日志确认无异常

bash 复制代码
kubectl logs -n kube-system <kube-proxy-pod-name> --tail=50

(4)检查该节点上其他 Service 的 ClusterIP 是否均可正常访问

bash 复制代码
# 在 Pod 内批量测试其他 Service 的 ClusterIP
for ip in 10.xx.xx.123 10.xx.xx.170 10.xx.xx.252; do
  timeout 3 bash -c "echo > /dev/tcp/$ip/9000" && echo "$ip: 通" || echo "$ip: 不通"
done

业务 502 全部消失,恢复如初。


六、复盘总结

阶段 关键动作 教训
初期 看到 10.xx.xx.210 不通,误以为是旧 Pod IP ClusterIP 和 Pod IP 必须严格区分,环境变量是最直接的验证方式
中期 在精简容器里找 sstcpdump 生产环境容器通常没有这些工具,/dev/tcp + curl 才是最可靠的探测手段
后期 发现 API Server/CoreDNS 通、唯独业务 Service 不通 这种选择性不通是定位 kube-proxy 问题的经典信号
根因确认 Kuboard 看到 Endpoints 正常,但数据面不通 控制面正常不代表数据面正常,kube-proxy 是中间的"黑盒"
修复 全量重启 kube-proxy DaemonSet 对于规则同步类故障,重启往往是最有效的恢复手段

这次故障给我最大的提醒是:看到 "No route to host" 不要只盯着防火墙和网络策略,kube-proxy 的规则同步状态才是 ClusterIP 场景下最隐蔽的故障点。 控制面(Endpoints)看起来一切正常,但数据面(iptables/IPVS)可能已经断联很久了。


附录:排查命令速查

以下命令供管理员参考,用于独立排查同类问题:

1. 在 Pod 内查看环境变量确认 ClusterIP

bash 复制代码
env | grep SERVICE_ | grep <服务名>

2. 在精简容器内做端口连通性测试

bash 复制代码
# 测试 TCP 端口是否通
timeout 3 bash -c 'echo > /dev/tcp/<IP>/<PORT>' && echo "通" || echo "不通"

# 示例
timeout 3 bash -c 'echo > /dev/tcp/10.57.163.210/9000' && echo "通" || echo "不通"

3. 查看 Service 的 Selector

bash 复制代码
kubectl get svc service-titan-zuul -n thc-dev-xx-xx -o jsonpath='{.spec.selector}'

4. 查看 Pod 的 Labels

bash 复制代码
kubectl get pod app-titan-zuul-0 -n thc-xx-xx-xx --show-labels

5. 查看 Endpoints

bash 复制代码
kubectl get endpoints service-titan-zuul -n thc-dev-xx-xx -o wide
kubectl get endpoints service-titan-zuul -n thc-dev-xx-xx -o yaml

6. 查看 kube-proxy 日志

bash 复制代码
kubectl logs -n kube-system <kube-proxy-pod-name> --tail=100

7. 查看 Calico Pod 状态

bash 复制代码
kubectl get pods -n kube-system -o wide | grep calico | grep <节点IP>

8. 查看 Calico Pod 日志

bash 复制代码
kubectl logs <calico-pod-name> -n kube-system --tail=50

9. 查看节点上所有系统组件 Pod

bash 复制代码
kubectl get pods -n kube-system -o wide | grep <节点IP>

10. 查看 kube-proxy 的 iptables 规则(需进入节点)

bash 复制代码
iptables -t nat -nL | grep <ClusterIP>

11. 检查 kube-proxy 的 ClusterRoleBinding

bash 复制代码
kubectl get clusterrolebinding | grep kube-proxy
kubectl describe clusterrolebinding kube-proxy

12. 检查 kube-proxy 的 ServiceAccount

bash 复制代码
kubectl get sa -n kube-system kube-proxy

13. 全量重启 kube-proxy

bash 复制代码
kubectl rollout restart daemonset kube-proxy -n kube-system
相关推荐
纵道软件1 小时前
使用 Docker 构建自定义 SeaTunnel Web 服务镜像
前端·docker·容器
云烟成雨TD2 小时前
Micrometer 系列【59】统一观测:Spring Boot 集成指南
spring boot·云原生·链路追踪
阿里云云原生16 小时前
智能体构建与进化——Agent 开源开发者沙龙·广州站精彩回顾 & PPT 下载
云原生·agent
阿里云云原生16 小时前
阿里云联合 Datadog,补齐 Go 可观测性最后短板
云原生·go
qq_4523962317 小时前
第十二篇:《Istio 生产环境最佳实践与排错指南》
云原生·php·istio
AAA@峥20 小时前
K8s 流量入口管理|Ingress 核心原理 + ingress-nginx 部署 + 生产实战案例合集
nginx·容器·kubernetes
Henry-SAP21 小时前
SAP MRP类型如何影响计划订单生成
人工智能·云原生·sap·erp
GlueNa2SiO31 天前
10-Docker生产环境部署与K8s入门
笔记·学习·docker·容器·kubernetes
Kismet_nvi1 天前
《Kubernetes 网络进阶、调度策略与自动扩缩容实战精要》
网络·容器·kubernetes