Kubernetes生产运维06:Service访问不通,怎么顺着EndpointSlice和kube-proxy找到断点
写在前面
Service访问不通时,常见的第一反应是怀疑网络出了问题,然后开始抓包、重启kube-proxy或者重建Service。
但Service只是一层抽象,它把稳定的访问入口和一组随时变化的Pod关联起来。请求从客户端到达真正的容器,中间要经过DNS解析、ClusterIP、Service、EndpointSlice、kube-proxy规则、Pod就绪状态、容器端口,还可能受NetworkPolicy约束。任何一段断了,表现都是访问不通,但根因和修复完全不同。
因此,Service排查要沿着这条链路逐段确认断点在哪:
访问不通 → 确认要访问的Service和端口是什么 → Service是否有Endpoint → 没有Endpoint:Selector不匹配还是Pod未就绪 → 有Endpoint:端口、kube-proxy、NetworkPolicy还是应用本身 → 定位断点后针对性修复 → 补链路监控与配置校验本文按照Kubernetes官方机制和命令参考整理。由于当前没有连接可验证的实验集群,命令没有在统一版本的真实集群完整执行,案例和终端输出均为C级生产化重建,不是生产原始记录。不同Kubernetes版本、CNI插件、kube-proxy模式、DNS实现和云厂商可能改变字段、命令和行为,执行前应以目标集群的kubectl explain、命令帮助和实际状态为准。
一、先理解Service到Pod的链路
1.1 EndpointSlice是Service和Pod之间的名册
对于带Selector的Service,控制平面会自动创建EndpointSlice,其中引用所有匹配Selector且就绪的Pod。kube-proxy根据这些端点在节点上编程转发规则,DNS把Service名解析到ClusterIP。
这条链路里有几个关键事实:
- Service通过Selector选择Pod,匹配关系体现在EndpointSlice里。
- 只有通过就绪检查的Pod才会作为可用端点被加入。
- 一个Service可能对应多个EndpointSlice,需要合并看全部端点。
- kube-proxy把端点转成内核转发规则,规则没生效时即使有端点也连不上。
1.2 没有Endpoint和连接被拒是两回事
两种现象都表现为访问不通,但断点位置不同:
| 现象 | 含义 | 断点位置 |
|---|---|---|
| Service没有Endpoint | 没有匹配且就绪的后端Pod | Selector或Pod就绪 |
| 连接超时 | 请求发出后没有响应 | 网络策略、路由、kube-proxy或节点网络 |
| 连接被拒绝 | 目标可达但端口上无人监听或拒绝 | 端口不匹配、容器未监听、应用拒绝 |
先判断是"落空"(没有后端)还是"被拒/超时"(有后端但连不上),能快速把排查范围减半。
1.3 端口的三段对应
Service访问涉及三个端口,必须一一对应:
- Service的
port:客户端访问Service用的端口。 - Service的
targetPort:转发到Pod上的端口。 - 容器的
containerPort与实际监听端口:应用真正监听的端口。
targetPort要对上Pod实际监听的端口。如果targetPort写错,或应用监听的端口和声明不一致,即使有端点也会连接被拒或超时。
二、Service排查决策树
访问不通 │ ├─ 确认目标Service、命名空间和端口 │ ├─ Service有Endpoint吗 │ ├─ 无端点 │ │ ├─ Selector与Pod标签不匹配 │ │ ├─ Pod未就绪(Readiness失败) │ │ └─ Pod不存在或不在同命名空间 │ └─ 有端点 │ ├─ 端口/targetPort不匹配 → 连接被拒 │ ├─ NetworkPolicy拦截 → 超时 │ ├─ kube-proxy规则未生效 → 超时 │ └─ 应用本身错误 → 有响应但报错 │ ├─ 定位断点后针对性修复 │ └─ 补链路监控与校验先用一条命令区分有没有端点:
NS='<namespace>'SVC='<service-name>'kubectl get endpointslices-n"$NS"-l"kubernetes.io/service-name=$SVC"-owide有就绪端点则往端口、策略、kube-proxy查;没有端点则往Selector和Pod就绪查。
三、取证:逐段确认
3.1 确认Service本身
kubectl get svc"$SVC"-n"$NS"-owide kubectl describe svc"$SVC"-n"$NS"关注:
Type、ClusterIP、Ports(port和targetPort)Selector- describe底部的
Endpoints一行是否为空
3.2 确认端点
kubectl get endpointslices-n"$NS"-l"kubernetes.io/service-name=$SVC"\-ocustom-columns='NAME:.metadata.name,ADDRESSES:.endpoints[*].addresses,READY:.endpoints[*].conditions.ready,PORTS:.ports[*].port'- 如果没有任何EndpointSlice或地址为空,是"没有端点"分支。
- 如果有地址但
ready为false,说明匹配到了Pod但未就绪。
3.3 没有端点:查Selector和就绪
对比Service的Selector和Pod的标签:
kubectl get svc"$SVC"-n"$NS"-ojsonpath='{.spec.selector}{"\n"}'kubectl get pods-n"$NS"--show-labels kubectl get pods-n"$NS"-l'<selector-from-service>'-owide- 用Service的Selector去筛Pod,如果筛不到,是Selector不匹配。
- 如果筛得到但
READY不是全就绪,是Pod未就绪,转向Readiness探针和应用启动。
3.4 有端点:查端口、策略和kube-proxy
kubectl get svc"$SVC"-n"$NS"\-ojsonpath='{range .spec.ports[*]}{.name}{" port="}{.port}{" targetPort="}{.targetPort}{"\n"}{end}'kubectl get pod'<backend-pod>'-n"$NS"\-ojsonpath='{range .spec.containers[*]}{.name}{" ports="}{.ports}{"\n"}{end}'kubectl get networkpolicy-n"$NS"targetPort要对上Pod实际监听端口。- 有NetworkPolicy时,确认是否放行了来源到该端口的流量。
- 如果端口和策略都正常仍超时,再查kube-proxy和节点网络,这类问题往往需要节点侧和CNI排查,属于更深一层。
3.5 边界
kubectl get endpointslices受版本和权限影响,旧集群也可用kubectl get endpoints作为补充视角。- NetworkPolicy行为取决于CNI是否支持,未安装支持的CNI时策略可能不生效。
- 抓包和内核规则排查会改变现场或需要节点权限,属于只读命令之后的深入手段。
四、常见断点与判断
| 现象 | 更可能的断点 | 优先检查 | 不能直接得出的结论 |
|---|---|---|---|
| Service无Endpoint,Selector筛不到Pod | Selector或Pod标签错 | Service selector与Pod labels | 不一定是网络问题 |
| Service无Endpoint,Pod能筛到但未就绪 | Readiness未通过 | Readiness探针、应用启动 | 不一定是Service配置错 |
| 有Endpoint但连接被拒 | targetPort或监听端口不符 | targetPort与容器实际监听 | 不一定是应用崩溃 |
| 有Endpoint但连接超时 | NetworkPolicy或网络 | NetworkPolicy、CNI、kube-proxy | 不一定是应用无响应 |
| 有响应但返回错误 | 应用本身 | 应用日志、依赖 | 不一定是Service链路问题 |
| 跨命名空间访问不通 | DNS名或策略 | FQDN、NetworkPolicy命名空间选择器 | 不一定是Selector问题 |
一个关键区分:没有Endpoint时,问题几乎一定在Service到Pod的关联(Selector或就绪),而不在更下层的网络;有Endpoint但不通时,才需要往端口、策略和网络查。先看端点能避免一开始就抓包走弯路。
五、C级生产化重建案例:改了标签后Service突然没有后端
5.1 先说明哪些是真的,哪些是重建的
下面不是作者声称亲历的生产事故,而是根据Kubernetes Service、Selector和EndpointSlice机制构造的生产化重建,用于展示从访问不通走到可验证根因的取证过程。
| 内容 | 证据属性 |
|---|---|
| Service、Selector、EndpointSlice和就绪端点之间的机制 | Kubernetes官方机制 |
| 改动标签后Service端点变空、访问返回无后端 | 机制一致的重建场景 |
| Namespace、资源名、标签、相对时间和终端输出 | 为讲解构造的说明性信息 |
| Deployment模板的Pod标签被改动 | 模拟根因,不是作者生产记录 |
| 修正单一变量后验证端点恢复 | 受控验证设计,不声称已在当前集群执行 |
所有输出按真实对象关系编排,但没有连接实际集群采集,读者不能把下面的时间、标签或输出引用为真实事故数据。
5.2 现场卡片
一次发布后,某内部服务的调用方开始报连接失败。被调用的Deployment Pod看起来都在Running。团队最初怀疑是网络或kube-proxy问题。
重建的相对时间线:
| 相对时间 | 观察或动作 | 当时能够得出的结论 |
|---|---|---|
| T+00 | 发布更新Deployment | 只能确认发生了配置变更 |
| T+02 | 调用方报连接失败,Pod仍Running | 访问不通,原因未知 |
| T+04 | 有人怀疑网络或kube-proxy | 待验证假设 |
| T+06 | 查到Service没有任何Endpoint | 断点在Service到Pod关联,不在下层网络 |
| T+09 | 对比Selector与Pod标签发现不一致 | 得到可验证的主假设 |
| T+验证 | 只修正标签,观察端点是否恢复 | 单变量验证 |
相对时间只表示排查顺序,不代表真实恢复时长。
5.3 先确认有没有端点
NS=example-prodSVC=orders-internal kubectl get svc"$SVC"-n"$NS"-owide kubectl get endpointslices-n"$NS"-l"kubernetes.io/service-name=$SVC"\-ocustom-columns='NAME:.metadata.name,ADDRESSES:.endpoints[*].addresses,READY:.endpoints[*].conditions.ready'机制一致的说明性输出:
NAME TYPE CLUSTER-IP PORT(S) SELECTOR orders-internal ClusterIP 10.96.12.34 80/TCP app=orders,tier=api NAME ADDRESSES READY orders-internal-abcde <none> <none>Service存在、有ClusterIP,但EndpointSlice没有任何地址。这把断点直接定位到Service与Pod的关联,不是下层网络或kube-proxy。因此先不抓包。
5.4 判断是Selector不匹配还是未就绪
kubectl get svc"$SVC"-n"$NS"-ojsonpath='{.spec.selector}{"\n"}'kubectl get pods-n"$NS"-lapp=orders --show-labels kubectl get pods-n"$NS"-l'app=orders,tier=api'-owide机制一致的说明性输出:
map[app:orders tier:api] NAME READY STATUS LABELS orders-api-newhash-a1 1/1 Running app=orders,tier=backend orders-api-newhash-b2 1/1 Running app=orders,tier=backend (使用 app=orders,tier=api 精确筛选) No resources found in example-prod namespace.关键发现:
- Pod都是Running且Ready,不是就绪问题。
- Service的Selector要求
tier=api,但Pod标签是tier=backend。 - 用Service的完整Selector精确筛选,筛不到任何Pod。
因此不是网络问题,也不是Pod未就绪,而是Selector与Pod标签不匹配,导致EndpointSlice为空。
5.5 对比新旧模板确认来源
helm list-n"$NS"helm get values'<release-name>'-n"$NS">release-values.yamlgrep-n-A3'labels'release-values.yamlgitdiff'<known-good-ref>..<candidate-ref>'-- values-prod.yaml机制一致的说明性差异:
labels: app: orders - tier: api + tier: backend证据链:
Service存在且有ClusterIP + EndpointSlice为空 + Pod都Running且Ready + Service Selector要求tier=api + 本次发布把Pod标签tier改成backend + 用完整Selector筛不到Pod = 强烈支持本次标签改动导致Service失去后端这仍是主假设,验证前不写成根因已闭环。
5.6 止损与单变量验证
调用方正在报错,最安全的止损通常是回滚到已知可用版本,或在变更窗口修正标签。两者都属于变更操作,执行前确认可用副本、PDB、maxUnavailable、maxSurge、兼容性和可回退版本。
修正时只把Pod模板标签tier改回api,不同时改Service Selector、端口和其他配置(同时改两端会让人无法判断到底哪边错):
spec:template:metadata:labels:app:orderstier:api验证并保存修复后证据:
kubectl rollout status deployment/orders-api-n"$NS"--timeout=5m kubectl get endpointslices-n"$NS"-l"kubernetes.io/service-name=$SVC"\-ocustom-columns='NAME:.metadata.name,ADDRESSES:.endpoints[*].addresses,READY:.endpoints[*].conditions.ready,PORTS:.ports[*].port'机制一致的说明性输出:
deployment "orders-api" successfully rolled out NAME ADDRESSES READY PORTS orders-internal-abcde 10.244.1.7,10.244.2.9 true,true 8080端点恢复后,从集群内做一次连通性确认:
kubectl run netcheck--rm-it--restart=Never-n"$NS"\--image=<approved-debug-image>--\sh-c'wget -qO- --timeout=3 http://orders-internal.example-prod.svc/healthz'这条命令会临时创建一个调试Pod,属于主动操作,需使用经审批的调试镜像并在完成后清理。它用于确认链路已通,不是必需步骤。
这些输出分别证明不同范围的事实:
| 输出 | 能够支持 | 不能单独证明 |
|---|---|---|
| rollout成功 | Deployment滚动完成 | 所有调用方立即恢复 |
| EndpointSlice有就绪地址 | Selector重新匹配到就绪Pod | 端口和策略一定都正确 |
| 连通性探测成功 | 该路径当前可达 | 所有来源和端口都放行 |
5.7 根因闭环条件
- 修正版与失败版只有Pod标签这一项主要差异。
- 修正后EndpointSlice重新出现就绪地址。
- Service Selector未改动,说明是Pod侧标签错。
- 调用方连接恢复。
- 观察窗口内端点稳定,没有再次变空。
如果修正标签后仍无端点,就要停止把标签当作唯一原因,重新检查就绪探针、命名空间和Selector两端。
六、修复方案要分六层
| 层次 | 本文场景中的动作 | 关键边界 |
|---|---|---|
| 应急止损 | 回滚到已知可用版本或修正标签 | 先查可用副本、PDB、兼容性和回退版本 |
| 现场取证 | 保存Service、EndpointSlice、Pod标签、端口和NetworkPolicy | 端点和标签随发布变化,尽快留存 |
| 根因验证 | 只改一端的一个变量并观察端点恢复 | 不同时改Pod标签和Service Selector |
| 永久修复 | 修正Git或Helm源配置的标签或Selector | 防止下次发布再次改错 |
| 监控预防 | 监控Service端点数、就绪比例和连接失败 | 端点为0应触发告警 |
| 运行治理 | 标签与Selector一致性校验、端口约定、发布校验 | 标签契约纳入模板校验 |
临时恢复不等于根因确认。端点恢复只说明变更与故障相关,仍需配置差异、端点变化和单变量验证共同支撑。
不要在没看端点前就抓包或重启kube-proxy。没有端点时问题在关联层,重启网络组件无效还可能扩大影响。
七、可直接使用的只读Service采集脚本
脚本只读取对象,不修改Service、不改标签、不重启组件、不创建调试Pod。
#!/usr/bin/env bashset-uset-opipefailNS=${1:?用法:$0 <namespace> <service-name>}SVC=${2:?用法:$0 <namespace> <service-name>}STAMP=$(date+%Y%m%d-%H%M%S)OUT="svc-evidence-${NS}-${SVC}-${STAMP}"mkdir-p"$OUT"umask077capture(){localfile=$1shiftprintf'采集 %s\n'"$file"if!"$@">"$OUT/$file"2>"$OUT/$file.err";thenprintf'失败: %s,查看 %s.err\n'"$file""$file">&2fi}capture context.txt kubectl config current-context capture version.txt kubectl version capture service.yaml kubectl get svc"$SVC"-n"$NS"-oyaml capture service-describe.txt kubectl describe svc"$SVC"-n"$NS"capture selector.txt kubectl get svc"$SVC"-n"$NS"\-ojsonpath='{.spec.selector}{"\n"}'capture ports.txt kubectl get svc"$SVC"-n"$NS"\-ojsonpath='{range .spec.ports[*]}{.name}{" port="}{.port}{" targetPort="}{.targetPort}{" protocol="}{.protocol}{"\n"}{end}'capture endpointslices.txt kubectl get endpointslices-n"$NS"\-l"kubernetes.io/service-name=$SVC"-owide capture endpointslices.yaml kubectl get endpointslices-n"$NS"\-l"kubernetes.io/service-name=$SVC"-oyaml capture pods.txt kubectl get pods-n"$NS"--show-labels-owide capture networkpolicy.yaml kubectl get networkpolicy-n"$NS"-oyamlprintf'采集完成: %s\n'"$OUT"printf'如需连通性测试,请另用经审批镜像的临时Pod,属于主动操作\n'printf'分享前请检查地址、标签、镜像和策略中的敏感信息\n'使用方式:
bashcollect-svc-evidence.sh'<namespace>''<service-name>'脚本边界:
- 只做只读采集,连通性测试需另行创建临时Pod,属于主动操作
- endpointslices受版本和权限影响,旧集群可补充
kubectl get endpoints - NetworkPolicy是否生效取决于CNI,采集到策略不代表一定被执行
- 脚本用于保存首轮现场,不能替代按端点结果选择下一步
八、监控、就绪与治理
8.1 监控什么
- 每个Service的就绪端点数量,端点为0应告警
- 就绪Pod比例与Readiness探针失败
- 服务间连接失败率和超时率
- NetworkPolicy变更与拒绝计数(若CNI支持)
- 发布前后端点数量对比
告警不能只监控Pod是否Running。Pod Running但未就绪、标签改错、端口不符都会让端点为空或不通,需要以端点数量和连接成功率为准。
8.2 发布前校验
- Pod模板标签与Service Selector保持一致
- Service的targetPort与容器实际监听端口一致
- 命名空间内外访问用正确的Service名或FQDN
- NetworkPolicy放行必要的来源和端口
- 变更标签或Selector时同时检查两端
8.3 契约与治理
- 把标签和端口作为服务契约的一部分,纳入模板校验
- Selector和Pod标签由同一模板生成,避免两端漂移
- 关键服务配置端点数量告警,快速发现失去后端
- 建立Service排障Runbook,先看端点再看网络
九、常见误区
误区1:一访问不通就抓包或重启kube-proxy
先看端点。没有端点时问题在关联层,抓包和重启网络组件无效。
误区2:Pod是Running就以为一定是后端
只有就绪的Pod才会成为端点。Running不等于Ready。
误区3:只改一端标签或Selector
改错的一端和排查时改动的一端要一致,同时改两端会掩盖真正问题。
误区4:忽略targetPort与实际监听端口
端口对不上会连接被拒,即使有端点也不通。
误区5:忘了NetworkPolicy
有端点但超时可能是策略拦截,尤其是默认拒绝的命名空间。
误区6:跨命名空间用短名
跨命名空间要用带命名空间的名字或FQDN,否则解析不到。
误区7:把连接被拒和连接超时混为一谈
被拒是端口层面,超时更多是策略或网络,处理路径不同。
误区8:只看Service不看EndpointSlice
Service配置正确但端点为空时,问题在Selector或就绪,不看端点会漏判。
十、面试怎么说
60秒版本
Service不通我不会先抓包。先确认要访问的Service和端口,再看有没有Endpoint。没有端点几乎一定是Selector不匹配或Pod未就绪,我用Service的Selector去筛Pod,筛不到就是标签错,筛得到但没就绪就查Readiness。有端点还不通,才往targetPort、NetworkPolicy和kube-proxy查,区分连接被拒和连接超时。修复只改一端一个变量并观察端点恢复,最后补端点数量和连接成功率监控。
3分钟场景版本
假设发布后调用方连接失败,被调Pod都Running。我先看这个Service的EndpointSlice,发现没有任何地址,这就把断点定位到Service与Pod的关联,不用去抓包。我用Service的Selector精确筛Pod,筛不到;再看Pod标签,发现这次发布把tier从api改成了backend,而Service Selector还是tier=api,所以端点变空。我确认Pod都Ready,排除就绪问题,然后回到Helm和Git确认是Pod模板标签改错。我只把Pod标签改回api,不动Service Selector和端口,滚动完成后EndpointSlice重新出现就绪地址,调用恢复。这样我能区分是标签契约问题而不是网络故障,也不会用重启kube-proxy这种无关动作去掩盖配置错误。
十一、延伸问答
1. Service没有Endpoint一定是Selector错吗
不一定。也可能是匹配到的Pod未就绪,或Pod不在同一命名空间。要先用Selector筛Pod再看就绪状态。
2. Pod是Running为什么不在端点里
只有通过Readiness的Pod才会作为可用端点。Running不等于Ready。
3. 连接被拒和连接超时怎么区分处理
被拒通常是端口层面,查targetPort和监听端口;超时更多是NetworkPolicy或网络,查策略和CNI。
4. 一个Service会有多个EndpointSlice吗
会。端点较多时会分片,需要按service-name标签合并看全部端点。
5. 改了Selector后要注意什么
Selector和Pod标签是两端契约,改一端就要同步另一端,否则端点会变空。
6. NetworkPolicy会导致有端点但不通吗
会。默认拒绝的命名空间里,没有放行规则的流量会被拦截,表现为超时。
7. 跨命名空间访问要注意什么
要用带命名空间的Service名或FQDN,并确认NetworkPolicy放行了对方命名空间。
8. 什么时候才需要抓包和查kube-proxy
在确认有就绪端点、端口正确、策略放行之后仍不通时,才深入节点网络、kube-proxy和CNI。
小结
- Service只是抽象,请求要经过DNS、ClusterIP、EndpointSlice、kube-proxy、端口和策略多段。
- 排查先看有没有Endpoint,能把范围直接减半。
- 没有端点几乎一定在Selector或Pod就绪,不在下层网络。
- 有端点不通才查targetPort、NetworkPolicy和kube-proxy。
- 连接被拒和连接超时断点不同,处理不同。
- 标签和Selector是两端契约,改一端要同步另一端。
- 修复只改一端一个变量并观察端点恢复。
- 长期治理覆盖标签契约、端口约定、端点监控和排障Runbook。
下一篇预告
下一篇进入CoreDNS与域名解析故障。我们会沿着DNS链路、解析超时、缓存、上游DNS和ndots配置,区分解析失败、解析慢和解析到错误地址,并整理一份DNS排查清单。
参考资料
- Kubernetes官方文档:Service
- Kubernetes官方文档:EndpointSlices
- Kubernetes官方文档:Debug Services
- Kubernetes官方文档:Connecting Applications with Services
- Kubernetes官方文档:Virtual IPs and Service Proxies
- Kubernetes官方文档:Network Policies
- Kubernetes官方文档:Liveness, Readiness and Startup Probes
- Kubernetes官方文档:DNS for Services and Pods
- Kubernetes官方文档:Service Internal Traffic Policy
- Kubernetes官方文档:Debug Running Pods