资讯动态

Kubernetes端口telnet不通:五段链路排查与根因定位

发布时间:2026/10/23 1:20:29 来源:尧图企业网站定制
下午三点多联调群里出现一张截图终端里敲完telnet 10.20.3.15 30080光标闪了几秒抛出一行Connection refused。开发同学说服务明明在跑日志里都有请求运维同学说节点负载正常、网络没告警。两边各握着一半证据谁也不服谁。这种对话我经历过太多次问题往往不在于谁对谁错而在于双方验证的压根不是同一段链路。这篇东西就是把这根链路彻底拆开讲。主题词很明确Kubernetes 里的端口用 telnet 连不上怎么一步步定位到真正的断点。它适合三类人看——刚接手容器化项目、遇到服务在跑但连不上的开发负责集群运维、天天被叫去看下网络的运维以及想把这套排查思路固化进发布流程的技术负责人。不论你现在是只会kubectl get pod的新手还是能背出 iptables 规则链的老手下面这套拆法都能直接拿去用因为我不打算只给结论而是把每一跳的取证动作、判读标准、以及我踩过的坑都摊开写。1. telnet 不通这件事先别急着改配置1.1 telnet 验证的只是三次握手不是业务可用很多人把 telnet 当成服务健康检查这是第一个认知偏差。telnet 一个端口成功标志只有一个TCP 三次握手完成屏幕上出现Connected to ...或者Escape character is ^]。它压根不知道对面的应用是不是 HTTP、是不是 gRPC、有没有正常返回数据。反过来Connection refused这个报错也极其具体——它是收到了对端返回的 RST 包含义是这台机器活着内核也处理了这个包但没有任何进程在监听这个端口。而Connection timed out、No route to host、Connection reset by peer是完全不同的故事。我在实际排查里做过一个粗糙的统计真正因为端口没监听导致的 refused 大概只占三成剩下七成里一半是网络策略或转发规则把包丢了表现为超时而不是 refused另一半是 telnet 打到的地方本身就不对——比如打的是 NodePort 但externalTrafficPolicy设成了 Local而目标 Pod 恰好不在这个节点上。所以第一步永远不是翻 YAML而是先确认这个报错到底属于哪一类因为它直接决定了你该往哪个方向查。1.2 三个方向的断点症状长得很像但根因天差地别我把 Kubernetes 里端口连不上的断点归成三个方向第一个是流量根本没到达目标节点或 Pod第二个是到达了但被策略或规则拦掉第三个是到达了 Pod 但没人监听。这三者的外在表现有时几乎一样尤其是中间那一层因为丢包和拒绝在客户端看都是连不上。有个细节特别容易误导人如果你在节点宿主机上telnet 一个 ClusterIP走的是本机 kube-proxy 装的 iptables/IPVS 规则链路里没有物理网络这一段但如果你在集群外的笔记本上telnet 一个 NodePort中间隔着交换机、安全组、节点防火墙链路长了三倍。同样一个 Service两种测法的结论可能完全相反。所以我习惯在动手前先明确一件事我现在站在哪里、我要打到哪里、中间隔了几层。这个习惯帮我省掉的返工时间比任何工具都多。1.3 先画一张四层链路草图再动手我几乎每次排查都会先在纸上画一条横线从左到右标出几个节点客户端 → 节点网卡 → kube-proxy 规则 → Pod 网卡 → 应用监听。然后在这条线上逐个打点验证验证顺序是从右往左也就是从最靠近应用的 Pod 开始。为什么反着来因为越靠近应用信息越确定、成本越低。kubectl exec进容器看一眼监听状态几秒钟的事反过来先去抓交换机镜像流量那是最后手段。从右往左还有一个好处如果 Pod 内部监听正常那你就排除了应用代码没起来这个最常见也最容易被人推卸责任的选项后面所有讨论都能建立在服务是真的在跑这个共识上。提示如果你连 Pod 都进不去比如容器是 distroless 或者崩溃重启中那也先别急着抓包用kubectl debug挂一个临时容器进去看这个后面第 4 章会详细讲。2. 把一条访问链路拆成可验证的五段2.1 第一段客户端到节点先确认包有没有出去这一步最容易被跳过但坑不少。我遇到过一类情况开发同学的笔记本在公司办公网目标节点在隔离的测试网段两者之间压根不通telnet 出来是No route to host但他一直在查 Kubernetes 配置。所以第一个动作应该是用ping或者tracerouteWindows 上用tracert确认三层可达性再确认目标端口在防火墙上是否放行。如果是有安全组的环境这里还有个隐形陷阱安全组的入方向规则放行了 NodePort 范围默认 30000-32767但如果你在 Service 里把nodePort手动指定成了范围外的端口它会直接被 API Server 拒绝创建或者在某些旧版本上创建成功但转发行为异常。另外节点上如果跑了本地防火墙服务它的规则优先级往往高于 iptables 的 nat 表规则包在到达 kube-proxy 规则之前就被 REJECT 了。判断方法很简单在节点上iptables -t nat -nvL KUBE-SERVICES | grep 你的端口看规则是否存在再iptables -nvL INPUT | head -30看有没有位置更靠前的拒绝规则。这两个命令的输出顺序就是包的实际处理顺序。2.2 第二段节点上的转发规则Service 到底有没有生效确认包到了节点接下来看 kube-proxy 有没有为这个 Service 生成转发规则。这里要区分两种模式因为验证手法完全不同。iptables 模式下用iptables -t nat -nvL KUBE-SERVICES --line-numbers应该能看到一条目标为 ClusterIP:端口 的规则类似DNAT tcp -- 0.0.0.0/0 10.96.12.34 tcp dpt:8080 to:10.244.1.7:8080。如果这条规则的目标地址是:随机端口或者干脆没有这条链说明 Service 没被正确解析——大概率是 Endpoints 为空kube-proxy 就把这类 Service 的规则跳过了。IPVS 模式下用ipvsadm -Ln看虚拟服务列表应该能找到TCP 10.96.12.34:8080这一项下面挂着 RealServer 列表。如果虚拟服务存在但 RealServer 为空说明后端端点没注册如果连虚拟服务都没有那要么 kube-proxy 没同步要么这个节点上的 IPVS 模块加载有问题。我特别想强调一个容易忽略的细节IPVS 模式下每个 ClusterIP 都会在节点上挂一个虚拟网卡地址通常叫kube-ipvs0用ip addr show kube-ipvs0能看到一长串地址。有时候集群规模大、Service 数量多这张网卡上的地址数量会碰到上限表现出来就是部分 Service 突然不通。这种问题没有报错日志只能靠观察这张网卡的状态定位比较阴。2.3 第三段Service 到 Pod端点列表是空还是满这一段是所有排查里性价比最高的检查点一条命令就能定性kubectl get endpointslices -n 命名空间 -l kubernetes.io/service-name服务名。看ENDPOINTS那一列如果显示unset或者空那问题已经找到了往下就不用看了。端点为空的原因主要有三类。第一类是selector 不匹配Service 的spec.selector里的标签和 Pod 的实际标签对不上比如 Service 写的是app: web而 Pod 的标签是app: web-server一个字符的差别kubectl get pod --show-labels一眼就能看出来但很多人就是不看。第二类是Pod 没通过就绪探针只要 Pod 的 Ready 状态是 False它就不会被加进端点列表这就是为什么Pod 明明在 Running却连不上——Running 和 Ready 完全是两件事。第三类是命名空间搞错同名 Service 在多个命名空间里都有你查的和连的不是同一个。还有一种更隐蔽的多端口 Service 里每个port都必须有name而且 Pod 端的containerPort名称要和targetPort对得上。一旦名称对不上端口会被错误映射端点虽然非空但转到的是错误端口表现出来就是端点正常、telnet 不通。2.4 第四段Pod 内部监听地址才是真凶端点正常、规则正常那就要进容器看了。kubectl exec -it Pod名 -- ss -lntp有的精简镜像没有 ss用netstat -lntp再不行就直接读/proc/net/tcp看监听表。这里的关键是看本地地址那一列而不是端口。如果你看到的是127.0.0.1:8080而不是0.0.0.0:8080或*:8080那基本可以收工了——应用只监听在回环地址上集群里其他任何地方都连不进来包括同节点的其他 Pod。这种情况在本地开发时完全感觉不到问题因为你在本机 curl 一下是通的一上集群就不通很容易被误判成集群网络故障。/proc/net/tcp的长相比较劝退但我建议记一下第二列的格式是十六进制IP:十六进制端口0100007F就是 127.0.0.100000000就是 0.0.0.0。用awk NR1 {print $2} /proc/net/tcp | sort -u能快速看出这台机器上都有哪些监听组合比装工具快。2.5 第五段把五段的验证结果拼成一张判读表单独看每段信息容易晕我一般会边查边填一张表填完根因基本就锁定了。这张表我用了很久分享出来验证点正常表现异常表现对应根因方向客户端到节点连通ping 通、tracert 无黑洞超时、路由不可达网络链路、安全组节点 nat 规则存在对应 DNAT 或 IPVS 项规则不存在或 RealServer 为空kube-proxy、端点缺失端点列表有且地址与 Pod IP 一致unset或数量不对selector、就绪探针Pod 监听地址0.0.0.0/::127.0.0.1应用绑定配置Pod 监听端口与 targetPort 一致端口不一致端口映射三级关系这张表的价值不在于好看而在于它逼你把我以为变成我验证过。我见过太多排查绕远路的案例本质上是因为跳过了一行然后在后面的行里反复猜测。3. 六类高频根因的定位与修复3.1 containerPort、targetPort、port 的三角关系这是新人最容易绕晕、老手偶尔也会翻车的地方。三个端口各管一段port是 Service 自己对外暴露的端口targetPort是流量最终要打到 Pod 的哪个端口containerPort只是 Pod 模板里的一个声明。关键点是containerPort纯粹是文档性质的写错了、不写、写重复都不影响实际连通性。真正决定转发目的地的是targetPort而targetPort不写的时候默认等于port不是默认等于containerPort。所以有一种经典错误配置Service 写port: 80没写targetPortPod 里应用监听 8080containerPort写了 8080。看上去什么都写了实际上流量全被转到了 Pod 的 80 端口而那里没人监听客户端收到 RST报Connection refused。修起来很简单把targetPort: 8080补上就行。还有一种情况是端口对了但名称错了。多端口 Service 要求每个端口有唯一name此时targetPort可以用名称引用 Pod 端口的名字。一旦 Service 里的名字和 Pod 里的containerPort.name有一个字对不上转发就会落到错误端口。我现在的习惯是能用数字就用数字除非有明确的多端口语义需求因为数字不会拼错。3.2 端点为空Selector 与就绪探针的双重陷阱上一章讲过怎么查端点这里讲为什么会出现。除了标签不匹配这种低级错误最常见的是就绪探针配置不当。探针的initialDelaySeconds设得太短应用启动要 40 秒、探针 5 秒后就开查连续失败达到阈值后 Pod 一直处于 NotReady或者探针的path写错返回 404永远不 Ready。这两种情况 Pod 都是 Running 状态kubectl get pod看起来一切正常只有READY那一列是0/1不看就发现不了。修复方向不是把探针删掉而是调整参数。启动慢的应用应该用startupProbe单独兜住启动阶段把failureThreshold和periodSeconds的乘积放大到足够覆盖冷启动时间。我给的经验值是先用秒表实测三次冷启动耗时取最大值乘 1.5作为 startupProbe 的总容忍时间然后再把livenessProbe和readinessProbe的正常探测接上去。这么做的好处是 liveness 的判定不会在启动期误杀容器避免那种启动到一半被重启、重启又到一半又被重启的死循环。还有个细节readinessProbe探测的是 Pod 自身地址如果你的应用在启动早期只监听 127.0.0.1探针可能通过而外部访问不通——探针通过不代表对外可达这个认知要建立起来。3.3 NetworkPolicy 的白名单模型把流量吃掉NetworkPolicy 的语义是白名单这一点很多人理解反了。更麻烦的是它的一旦被选中就全拒特性只要某个 Pod 被任意一条带Ingress段的策略选中那么除了策略里from明确允许的来源其他所有入向流量默认全拒。也就是说你为了限制 A 服务只允许 B 访问写了一条策略结果 C、D、E 全被顺手禁掉了。定位方法比较直接kubectl get networkpolicy -n 命名空间看有没有策略再看kubectl describe networkpolicy 名字里的PodSelector是否选中了你的 Pod。如果集群用的 CNI 支持策略还可以在节点上查具体实现了哪些规则。判断是不是策略拦的有个小技巧策略拒绝通常表现为超时而不是 refused因为包被直接丢弃了没有 RST 回来。所以看到超时就该想到策略看到 refused 反而应该先怀疑监听。修复上我建议两条原则。第一默认拒绝策略只加在真正需要隔离的命名空间不要图省事全集群铺开。第二加策略时同步加一条allow from的显式规则给健康检查和监控来源否则探针和监控会一起挂掉排查时又会多一层干扰。3.4 监听地址绑成回环最隐蔽的一类这一类我在前面提过值得单独展开因为它太常见又太不容易被发现。表现是Pod 内部 curl 自己通同节点其他 Pod curl 不通kubectl port-forward却能用。最后这个现象特别有迷惑性因为 port-forward 是从 kubelet 侧直接连到 Pod 网络的它走的是另一条路径能通不代表服务对外可用。根因通常是应用配置里绑定了127.0.0.1或者绑定的是localhost被解析成了回环。有些框架的默认值就是回环本地开发图安全容器化之后就成了坑。修复就是改成0.0.0.0或者::注意 IPv6 环境下::和0.0.0.0行为不同双栈集群里建议显式绑::并开启双栈监听。还有一种衍生情况应用确实绑了0.0.0.0但容器里还有一层代理或 sidecar 抢占了同一个端口导致主应用绑不上、静默失败或者退到其他端口。这种情况看ss -lntp输出的进程名就能分辨如果监听端口的进程 PID 和主应用对不上那就是被劫持了。3.5 kube-proxy 模式差异带来的规则不同步iptables 和 IPVS 两种模式在超大规模下的表现差异很明显。iptables 模式下规则是线性匹配的Service 数量上千之后一次包处理要遍历成千上万条规则延迟和 CPU 都会上来IPVS 用哈希表规模不敏感但依赖内核模块模块加载异常时表现为部分节点转发失效。判断规则是否同步最直接的办法是对比期望状态和实际状态。期望状态来自kubectl get svc -o wide里的 ClusterIP实际状态来自节点上的ipvsadm -Ln或 iptables 链。如果某个 Service 在 API 里存在、在节点规则里缺失先看 kube-proxy 日志kubectl logs -n kube-system kube-proxy-pod --tail200重点找同步失败、连接 API Server 超时这类关键字。我遇到过一次比较典型的某个节点上的 kube-proxy 因为和 API Server 的长连接断了规则停留在几分钟前的快照新建的 Service 在这台节点上完全不可见其他节点正常。这种部分节点异常的问题最容易被误判成偶发网络抖动因为你在出问题的节点上测是不通换一台就通了。所以排查时一定要在多个节点上分别验证只测一台的结论不可靠。3.6 节点防火墙与 conntrack 表打满节点侧的问题有两个高频点。第一个是本地防火墙服务与容器网络规则的冲突某些发行版默认启用防火墙并拒绝未知入向NodePort 的包在到达 nat 规则前就被拦了。判断方法是临时停掉防火墙服务测一次如果能通就定位了但生产环境别直接停正确做法是把 Pod 网段和 Service 网段加入放行列表。第二个是 conntrack 表满这个更隐蔽。连接跟踪表有容量上限高并发短连接场景下很容易打满打满之后新连接直接被丢弃表现出来就是服务好好的突然大面积连不上过一会儿又自己好了。查法很简单cat /proc/sys/net/netfilter/nf_conntrack_count和nf_conntrack_max对比一下如果前者逼近后者基本确诊。修复分两步短期调大nf_conntrack_max长期要从应用侧减少短连接比如加连接池、调长 keepalive。注意调 conntrack 上限要同步评估内存占用每条跟踪表项都占内存盲目调到几十万在内存紧张的节点上会引发更严重的问题。我一般按当前峰值的 2 倍设并且加上监控告警。4. 现场取证用调试容器和抓包把问题钉死4.1 kubectl debug 挂临时容器不污染原镜像排查时最尴尬的情况是目标镜像是 distroless里面连sh都没有kubectl exec直接失败。这时候用kubectl debugkubectl debug -it Pod名 -n 命名空间 \ --imagebusybox:1.36 \ --target容器名 \ -- sh--target参数是关键它让临时容器共享目标容器的进程命名空间这样你在临时容器里ps能看到主应用的进程ss -lntp能看到主应用的监听排查才有意义。不加--target的话你看到的是一个干净的独立命名空间什么也查不到很多人第一次用就栽在这。临时容器有个限制它不支持端口映射和资源限制纯粹是调试用用完即弃。另外它需要有对应权限如果集群开了 Pod 安全策略可能会被拒绝这时候退而求其次用kubectl run起一个独立的调试 Pod指定--overrides让它调度到目标节点上。4.2 在节点上抓包判断 SYN 有没有进来抓包是终极手段但我建议把它放在倒数第二步因为它的信息量大、判读成本也高。命令本身不复杂tcpdump -i any -nn -c 50 tcp port 30080-i any能同时抓到多个网卡的流量避免漏掉经过网桥的包-c 50抓够 50 个包就停防止刷屏。然后在客户端发起 telnet观察输出。判读逻辑是只看到 SYN没有 SYN-ACK也没有 RST —— 包被静默丢弃优先怀疑 NetworkPolicy、防火墙 DROP 规则、路由问题看到 SYN 和 RST —— 对端明确拒绝说明包到了但没人监听重点查 Pod 监听看到完整的 SYN / SYN-ACK / ACK —— 三次握手成功网络层没问题问题在应用层协议或者后续数据交互连 SYN 都看不到 —— 包压根没到这台节点回到第 2 章第一段去查链路这套判读我用了很多年几乎没失手过。唯一要注意的是容器网络里包会经过多次 NAT抓到的地址可能是转换后的别被误导。4.3 反向验证从 Pod 里往外打正向查不通的时候反向打一次往往有奇效。在目标 Pod 里telnet或者nc -zv一下同命名空间的其他服务、ClusterIP、甚至外网地址看哪个方向不通。如果 Pod 能出去但外面进不来问题在入向如果 Pod 连 ClusterIP 都不通那可能是这个 Pod 所在的节点上 kube-proxy 规则有问题或者 Pod 的网络命名空间配置异常。如果 Pod 连外网都不通那要看 DNS 和出向 NAT跟端口本身没关系了。反向验证还有一个用途确认应用是不是真的能接受连接。有些应用监听正常但对特定来源做了 IP 白名单这时候从 Pod 内部打自己能通从外部打就不通排查方向完全不同。4.4 一次完整的取证实录说个我印象比较深的例子。某次测试环境报告某个服务 NodePort 连不上A 同学查了半天 YAML 没发现问题。我按流程走了一遍先kubectl get endpointslices看到端点是两个地址正常ss -lntp进容器看监听0.0.0.0:8080正常节点上iptables -t nat -nvL KUBE-SERVICES有对应规则正常。三条都正常那就只剩网络了。抓包一看只有 SYN 没有回包。这时候我想起 Service 配置里externalTrafficPolicy: Local而客户端打的那台节点上恰好没有这个服务的 Pod。Local 模式下NodePort 只在本节点有后端时才转发否则直接丢弃且不返回任何错误。换一台有 Pod 的节点测立刻通了。整个过程从抓包到定位不到十分钟但如果一开始就扎进 YAML 里翻可能要耗一下午。这个案例我的体会是排查的效率取决于你有没有一条固定顺序的验证路径而不是取决于你有多熟悉某个组件。路径对了问题自己会浮出来。5. 把这类问题挡在发布之前5.1 探针配置的三条硬规矩前面讲过探针导致端点为空的问题这里给三条我一直在用的规矩。第一启动慢的服务必须配 startupProbe用它的failureThreshold × periodSeconds覆盖冷启动别让 liveness 在启动期误杀。第二readinessProbe 的探测路径要探真实依赖比如数据库连接是否就绪而不是简单返回 200否则 Pod Ready 了但请求全 500等于把问题推给了上游。第三livenessProbe 不要探外部依赖数据库挂了不该导致所有 Pod 被重启那是雪崩。这三条背后的逻辑是一样的三种探针职责不同混用会把局部故障放大成全局故障。我在实际项目里见过 liveness 探数据库导致整个服务集体重启的案例恢复花了不少时间。5.2 Service 与工作负载的自检清单发布前花两分钟过一遍这几个点能挡掉大部分低级问题检查项期望值常见错误Service selector与 Pod labels 完全一致拼写差异、多余空格targetPort与 Pod 实际监听端口一致依赖默认值、写成 containerPort多端口 name每个端口唯一且有名字缺 name 导致校验失败端点数量与副本数一致探针未过、镜像启动失败网络策略显式放行监控与探针来源只写拒绝没写允许这份清单我贴在团队 wiki 上很久了新同学照着走一遍问为什么连不上的次数明显下降。5.3 一条命令做链路巡检手工逐段查太慢我写了个粗糙但好用的巡检脚本思路给定命名空间、Service 名、端口依次输出端点数量、Pod Ready 状态、Pod 监听地址最后在一台 Pod 里nc -zv目标 ClusterIP。四个输出并排看异常项一眼就能挑出来。核心就是把前面那张判读表自动化不追求多优雅能省时间就行。关键是要把巡检嵌进发布流水线作为发布后的验证步骤而不是等用户报障才去查。这一步做了之后上线之后连不上这类问题的平均恢复时间能压到很低。5.4 最后聊几句实际体会这套排查方法我用下来最大的感受是大部分网络问题最后都不是网络问题。真正涉及交换机、路由、丢包的占比很低绝大多数是配置对不上、探针没过、端点为空、监听绑错地址这几类。它们共同的特点是看起来一切正常就是连不上因为 Kubernetes 把很多信息分散在 API 对象里不主动去查就永远不知道。我现在的习惯是遇到 telnet 不通先不急着手先在纸上默写那条链路然后从最右边开始打点。这个动作熟练之后五分钟能走完比临时翻文档快得多。另外提醒一句kubectl port-forward能通千万别当成服务正常的证据它走的是 kubelet 那条路径跟 Service 转发完全是两回事这个坑我踩过也见过不少人踩。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑