NodePort 连不上这种问题,凡是维护过 k8s 集群的人多多少少都遇到过。明明 Service 配置看起来没问题,Pod 也 Running,但拿浏览器或 curl 一测节点 IP 加端口就是不通,有时候连本机都通不了,有时候换个节点又好了。这类问题排查起来说难不难,说简单也不简单,核心问题在于链路太长——中间任何一环断了,表现都是“连接超时”或者“拒绝连接”,根本看不出是哪一层出的问题。
所以写这篇文章前,我想按照自己平时排查的真实顺序,把 NodePort 不可连接这个问题从头到尾拆一遍:从客户端到节点、从节点到 kube-proxy、从 kube-proxy 到 Pod,每一层怎么验证、常见坑在哪、用什么命令快速定位。这不是纯理论,凡是我踩过的坑会单独标出来,后面还附了几个真实场景的排查案例。不管是刚接触 k8s 的新手,还是已经维护过一段时间集群的同行,照着这个思路走一遍,大概率能省下半天乱试的时间。
1. 先把 NodePort 的通信链路和原理理清楚
排查之前最忌讳的就是拿着命令瞎敲。我见过不少同事一上来就telnet IP port,通了就认为没问题,不通就一脸懵。其实要想快速定位,第一步需要搞清楚一个 NodePort 请求从外面进来,到最终到达 Pod 里,中间到底经过哪些环节。
1.1 一个请求从客户端到 Pod 要过几道门
假设客户端要访问http://192.168.1.10:30080,其中 192.168.1.10 是某台工作节点 IP,30080 是 NodePort 端口。那么这个请求大致要经过这么几层:
- 客户端到节点的网络路径:包括物理网络、路由、防火墙、安全组等。
- 节点上监听的服务端口:kube-proxy 会把 NodePort 端口上的流量通过 iptables/ipvs 规则接收下来。
- 内核转发规则:SNAT/DNAT 处理,决定数据包最终怎么转到 Pod IP。
- Pod 网络:通过 CNI 插件(比如 flannel、calico)把数据包送到 Pod。
- Pod 内的容器进程:监听的是 Service 的 targetPort,不是 30080。
只要这五层里有任何一层出问题,表现就是 NodePort 不通。所以排查思路就是一层层往内收,先确认是哪一层断了,再针对那层深挖。这个思路听起来简单,但实际工作中至少能过滤掉一半的无效操作。
1.2 默认端口范围和配置项先确认
NodePort 默认分配的端口范围是 30000~32767,这个区间可以通过 kube-apiserver 的--service-node-port-range参数修改。如果在创建 Service 时手动指定了nodePort: 30080,这个值必须落在范围内,否则创建都会报错。
另外一个容易被忽略的点是:Service 的type必须是NodePort或LoadBalancer。如果你定义的是ClusterIP,那节点上根本不会监听 30080 端口。有些公司内部封装了模板,模板里 Service 类型写死为ClusterIP,但同事以为nodePort字段填了就一定能通,这种错误其实不少见。
要快速确认 Service 的配置,一条命令就够:
kubectl get svc -n <namespace> <service-name> -o yaml重点看四个字段:type、spec.ports[].port、spec.ports[].nodePort、spec.ports[].targetPort。特别是targetPort,它决定了 kube-proxy 会把流量转发到容器的哪个真实端口。如果容器里的进程监听的是 8080,而 targetPort 写成了 80,那数据到了 Pod 里也会连接失败。
注意:NodePort 分配后,如果 Service 被删除重建,同一个 nodePort 端口可能被重新分配,也可能直接用于别的 Service,这在排查的时候很容易造成混淆。所以一旦发现问题,先看当前 Service 的实时配置,别凭记忆判断。
2. 第一层排查:客户端到节点的网络通不通
很多人习惯一遇到 NodePort 不通就立刻怀疑 k8s 本身的配置,其实第一步应该先确认最基础的网络问题:节点 IP 到底能不能访问、端口是不是被防火墙拦了。
2.1 先验证节点 IP 的可达性
这一步听起来好像多余,但我在实际工作中经常发现,所谓“NodePort 连不上”其实是整个节点都不通。可能是节点宕了、网线松了、或者客户端路由表压根没有指向这个 IP 的路径。
基础的三板斧:
ping <node-ip> traceroute <node-ip> nc -vz <node-ip> <nodePort>ping通不代表端口就通,但如果ping都不通,那问题大概率在物理网络或路由层面,没必要继续查 k8s。如果ping通、nc不通,那就要分两种情况:
- 返回
Connection refused:说明有东西收到了请求,但主动拒绝了,这是端口没监听或防火墙 DROP 规则的问题。 - 返回
No route to host或Connection timed out:说明数据包在路上丢了,或回包路径有问题,重点查路由、防火墙、安全组。
nc -vz只能判断端口通不通,没法判断转发是否正常。所以这个阶段不要因为端口不通就急着下结论说 NodePort 配置错了,先把它当成“节点服务端口不可达”的普通网络问题来排查。
2.2 防火墙、安全组这些拦路虎怎么查
如果是云环境,安全组(security group)是头号嫌疑。NodePort 端口范围是 30000-32767,很多云厂商默认安全组根本没放通这个范围。你需要确认:
- 安全组入方向是否放行了目标 NodePort 端口;
- 放行的 CIDR 是否包含了客户端的源 IP;
- 出方向是否需要放行回包流量(大多数云厂商默认放行,但有的限流策略会直接丢弃)。
如果是自建机房,那就是 iptables 和 firewalld 的事。在节点上执行:
iptables -L INPUT -n --line-numbers iptables -L FORWARD -n --line-numbers特别关注有没有 DROP 或 REJECT 的规则把 30000-32767 的流量拦了。很多公司会部署统一的主机安全软件,它们喜欢在 INPUT 链上加一条很靠前的 DROP 规则,导致所有非标准端口全部无法访问。这种问题你光看 k8s 配置永远查不出来,必须到主机上亲自看规则链。
另外,注意rp_filter(反向路径过滤)也是一个隐性杀手。如果数据包的源 IP 无法通过该节点的路由表回溯,内核会直接丢弃数据包。检查:
sysctl net.ipv4.conf.all.rp_filter sysctl net.ipv4.conf.default.rp_filter如果值为 1,且网络环境比较复杂,考虑针对特定接口设置成 0 或者 2(松散模式)。这个问题在有多块网卡、多个网段的节点上尤其常见。
3. 第二层排查:Service 和 Endpoint 状态是否正常
如果节点网络层没问题,节点 IP 能 ping 通,NodePort 也确认没有被防火墙拦截,那就要开始把注意力转到 k8s 内部——先查 Service 和 Endpoint。这一层的问题是最常见的,排查起来也最快。
3.1 先看 Endpoint 有没有内容
Service 的负载均衡目标是 Pod,而 Pod 的 IP 列表是通过 Endpoints(或者更现代的 EndpointSlice)来保存的。如果 Pod 都正常 Running,但 Endpoint 列表为空,那流量根本没地方可转,NodePort 表面上看是通的,但实际连接会被拒绝或一直超时。
查看命令:
kubectl get endpoints -n <namespace> <service-name> kubectl get endpointslice -n <namespace> -l kubernetes.io/service-name=<service-name>如果 Endpoint 为空,常见原因有:
- 部署(Deployment/StatefulSet)的 Pod 没有达到 Ready 状态,比如存活探针失败、镜像拉取失败、容器启动后持续 CrashLoopBackOff;
- Service 的
selector和 Pod 的 label 对不上,这个是最容易犯的错。比如 Pod 打的是app: my-web,但 Service 写的是app: myweb,差一个横杠就完全匹配不上。
排查 selector 是否匹配的小技巧:
kubectl get pods -n <namespace> --show-labels kubectl get svc -n <namespace> <service-name> -o jsonpath='{.spec.selector}'两边对比一下就知道有没有问题了。如果只是少数副本没就绪,但至少有一个 Endpoint 存在,那 NodePort 大概率是能通的,不通的原因会在后面转发层里面找。
3.2 Service 类型、端口映射和会话保持的确认
Service 配置里还有一个容易踩的坑:port和nodePort混用导致理解偏差。spec.ports[].port是 ClusterIP 上监听的端口,spec.ports[].nodePort才是节点上的端口。如果你在 Service YAML 里只写了port: 80,忘记写nodePort,kube-apiserver 会自动分配一个随机端口,你再用自己预想的 30080 去连,当然不通。
还有一种情况是 Service 配置正确,但业务 Pod 的targetPort写成了字符串。比如:
ports: - port: 80 targetPort: http但容器里并没有定义名为http的端口,这种写法会导致 Endpoint 里虽然有 IP,但端口映射错误,连接同样会失败。所以无论 YAML 多简单,我都建议用下面这条命令把端口映射关系完整看一遍:
kubectl get svc -n <namespace> <service-name> -o jsonpath='{range .spec.ports[*]}{.name}: {.port} -> {.targetPort} (nodePort: {.nodePort}){"\n"}{end}'如果发现端口映射有出入,直接 edit Service 改掉,或者删了重建。
关于会话保持(sessionAffinity),虽然它不影响连通性,但会影响你测试时的体验。如果把sessionAffinity设置成了ClientIP并且timeout比较长,你连续测试时会一直访问同一个 Pod,偶尔会让人误以为“流量固定只走某个节点/Pod,是不是负载均衡失效了”。这种情况不算故障,但容易干扰排查方向,知道就好。
4. 第三层排查:kube-proxy 和规则链有没有生效
如果 Service 和 Endpoint 都正常,但还是连不上,那就轮到 kube-proxy 背锅了。它是把所有 NodePort 流量转进 Pod 的关键组件,工作模式通常有两种:iptables和ipvs。不同模式排查方式略有不同,但有一个共同点:先确认 kube-proxy 本身还活着。
4.1 kube-proxy 运行状态和日志怎么看
kube-proxy 一般以 DaemonSet 方式部署。先查 Pod 状态:
kubectl get pods -n kube-system -l k8s-app=kube-proxy -o wide如果 Pod 是 CrashLoopBackOff 或者长时间处于 ContainerCreating,自然不会有规则被写入。查看日志:
kubectl logs -n kube-system <kube-proxy-pod-name> --tail=100常见的日志错误有两种。一种是error looking for path/failed to write iptables,多半是缺少权限或者新版 k8s 不再要求却仍存在的kube-proxy特权问题。另一种是反复提示can't initialize ipvs,那就是内核模块缺失,需要确认:
ls /lib/modules/$(uname -r)/kernel/net/netfilter/ipvs/如果路径里找不到ip_vs.ko,说明当前系统没装内核模块,kube-proxy 就无法以 ipvs 模式工作。你当然可以改成 iptables 模式,但更推荐先把内核模块补上。
4.2 iptables 模式下看四条关键链
kube-proxy 在 iptables 模式下,会在 nat 表中写入规则。我们通过 iptables 查到的规则链名称和 Service 一一对应。假设 Service 名称是web-svc,命名空间是default,端口 30080,则该 Service 相关的链大致有:
iptables -t nat -L KUBE-SVC-<hash> -n iptables -t nat -L KUBE-SEP-<hash> -n不过 hash 一串看半天也看不出哪个是哪个,更实用的做法是通过 nodePort 去找规则:
iptables -t nat -L -n --line-numbers | grep 30080你会看到一个类似这样的输出:
DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:30080 to:KUBE-SVC-XXXX看到这一条,说明 kube-proxy 确实给 NodePort 写了 DNAT 规则。然后顺着这条链继续查下去,确认它跳转到哪个KUBE-SEP,最终转发到哪个 Pod IP。
如果 nodePort 端口在iptables的 OUTPUT 链或 PREROUTING 链中完全搜不到,有两种可能:一是 kube-proxy 没同步规则(监听有问题或者缓存没刷新),二是 Service 类型压根不是 NodePort。这时候可以尝试删除 kube-proxy Pod 让其重建,强制重新同步一次。
4.3 ipvs 模式下要核对虚拟服务器列表
如果 kube-proxy 运行在 ipvs 模式,查看方式变了:
ipvsadm -L -n每个 NodePort 对应一个虚拟服务,比如:
TCP 192.168.1.10:30080 rr -> 10.244.1.5:8080 Masq 1 0 0 -> 10.244.2.7:8080 Masq 1 0 0如果列表里没有*:30080或节点 IP 对应 30080 的记录,说明 ipvs 规则没写入。先查看 kube-proxy 的模式配置:
kubectl get configmap -n kube-system kube-proxy -o yaml | grep -i mode如果mode是ipvs但列表为空,大概率就是 kube-proxy 没和 apiserver 正常通信,或者没有拿到 Service 更新的 watch 事件。此时重启 kube-proxy Pod 一般能恢复,但如果频繁出现,就要检查 kube-proxy 的 RBAC 权限或 apiserver 连接地址。
实测下来,ipvs 模式出问题时的日志往往不像 iptables 模式那么直观,因为没有规则链这种东西可查。我的习惯是出了问题先
ipvsadm -L -n看有没有条目,没有就往 kube-proxy 日志里翻有没有报错,首选关键字是 watch/event/endpoint,其次看 service/sync 相关错误。
5. 第四层排查:节点转发、CNI 和 Pod 内部
规则链有了,请求也进到了 NAT 层,但最后能不能到 Pod 手里还不一定。上面四层查完如果还没找到原因,基本就轮到节点内核转发能力和容器网络了。
5.1 检查 IP 转发开关和 conntrack 状态
NodePort 流量需要节点把数据包从物理网卡转发到 Pod 网段,这一动作依赖内核的ip_forward。如果它被关了,数据包到节点之后就会原地消失,NodePort 表现为连不上或者一直卡在握手阶段。
sysctl net.ipv4.ip_forward如果输出是 0,在节点上执行:
sysctl -w net.ipv4.ip_forward=1 echo "net.ipv4.ip_forward=1" >> /etc/sysctl.conf这一步做完之后,NodePort 往往会立刻恢复正常,因为很多网络故障排查到 kube-proxy 就停了,根本没人想到转发开关会被改掉。
conntrack 表满也是老问题了。当转发流量非常大时,conntrack 保存的连接条目可能耗尽,新连接无法建立。查看方式:
cat /proc/sys/net/netfilter/nf_conntrack_max cat /proc/sys/net/netfilter/nf_conntrack_count如果两者接近甚至相等,说明表已满,需要增大上限或者排查是否有异常的大量短连接。比较实际的操作是把nf_conntrack_max调大(例如从默认的 131072 调到 1048576),同时配合nf_conntrack_buckets调整。
5.2 CNI 插件和相关网络策略造成的问题
Flannel、Calico、Cilium 这类 CNI 插件负责给 Pod 分配 IP 和打通网络。NodePort 的流量最终要通过 CNI 的转发路径进到 Pod 网卡里。常见的问题有:
- Node 上的 CNI 插件崩溃或者版本和 k8s 不兼容,导致 Pod 启动正常但网络不通;
- Calico 的 BGP route 或者 overlay 网络没有正确建立;
- NetworkPolicy 限制了入站流量,尤其很多公司默认拒绝所有非白名单流量,如果业务命名空间下的 NetworkPolicy 不包含 Node 的 IP,就会导致 NodePort 能到节点但到不了 Pod。
查看 CNI Pod 状态:
kubectl get pods -n kube-system | grep -E 'flannel|calico|cilium'再查看节点上的路由表,确认 Pod 网段的路由是否存在:
ip route | grep <pod-cidr>如果 Pod 网段路由缺失,最直接的办法是重启该节点上的 CNI Pod,让插件重新注册和下发路由规则。注意,如果集群用的 BGP 模式,还要检查 BGP peer 是不是正常连接,bird或calico-node的状态是否 Established,这些对新手来说很容易忽略。
5.3 Pod 内的服务监听端口有没有对齐
最后一步,咱们还是得回到业务本身。Pod 起来了,进程也在跑,但它到底监听在哪个端口?这个问题看着弱智,但真的出现过很多次。有的二进制默认监听 8080,运维按 80 端口配了 targetPort,结果到容器里一看,进程压根没监听 80。
进容器检查:
kubectl exec -it <pod-name> -n <namespace> -- netstat -tlnp # 或者容器里没有 netstat 的话,用 ss kubectl exec -it <pod-name> -n <namespace> -- ss -tlnp重点看监听地址是0.0.0.0还是127.0.0.1。有些开发框架默认只回环监听,容器里的服务如果只绑了127.0.0.1,那 NodePort 转发永远不会成功,这属于业务侧问题,不是什么集群问题。一个经验:碰到 NodePort 不通,我都会顺带查一下目标 Pod 的 IP 能不能从节点上直接 ping 通:
ping <pod-ip>Pod IP 通不通是一个非常重要的分水岭。如果 Pod IP 都不通,说明 CNI 网络有问题;如果 Pod IP 通、但 targetPort 的 TCP 连不上,那就是 Pod 内进程监听的问题。这两个结果能直接把问题切成两个完全不同的方向。
6. 我实际踩过的几个 NodePort 排查案例
理论说完,拿几个真实碰到的案例来复盘。这些案例都不复杂,但每个都耗费了至少半小时以上才定位到根因,写出来给大家做个引子。
6.1 安全组默认不信任,导致只有本机通
某次在云环境里搭测试集群,Service 配置完全正常,Pod 也 Ready,但 NodePort 就是不通。我在节点上用curl 127.0.0.1:30080能通,在节点上curl <node-ip>:30080也能通,但从本机(外网)访问就不通。
一开始怀疑是安全组规则被某个同事改了,开了控制台检查发现安全组只放通了 80、443、22、3389 这些常规端口,30000-32767 整个范围全没放。加上 30080 这条策略后立即恢复。
这个案例的教训是:云环境排查 NodePort 一定要先看安全组,而且不要只看规则名,要看实际的 CIDR 和协议端口。很多公司用基础设施即代码管理安全组,如果你不是直接操作控制台的人,先问一句“30000-32767 放通了吗”,比什么都省时间。
6.2 kube-proxy 被删后没有自动重建,规则全丢了
有一次同事反馈某个节点上的 NodePort 访问时通时不通。查了半天,最后发现那个节点的 kube-proxy Pod 因为节点资源紧张被驱逐了,DaemonSet 没有自动把它调度回来,导致这个节点上完全没有任何转发规则。
本机 curl 127.0.0.1:30080 直接拒绝连接,但同一个 Service 在其他节点上是正常的。所以教训很明显:NodePort“某个节点不通而其他节点通”的时候,先对比节点间的 kube-proxy 状态和规则,别急着往网络层面广度排查。这种 DaemonSet Pod 被驱逐的问题在资源偏紧的集群里其实很多见,看kubectl get events -n kube-system就能找到线索。
6.3 Pod 内进程绑定了 127.0.0.1
一个内部模拟项目,开发把容器里的服务监听地址写成了127.0.0.1,测试的同学创建完 Service 就反馈 NodePort 不通。我第一轮查完 Service、Endpoint、kube-proxy 全都没问题,Pod IP 也能 ping 通,但nc -vz <pod-ip> <targetPort>就超时。后来kubectl exec进容器里一看,ss 输出明确显示进程监听在127.0.0.1:8080。
解决办法是让开发把监听地址改成0.0.0.0或者具体的 Pod IP 地址,重新发版。这类问题其实不算 k8s 本身的故障,但排查链路确实绕,因为表象和网络故障完全一样。以后只要碰到 targetPort 连接不通,我都习惯性先进容器看一眼监听地址,能省非常多时间。
6.4 本地 iptables 规则的优先级问题
有一次是节点上的安全软件自动添加了一条很靠前的 iptables INPUT DROP 规则,把 30080 端口给丢了。我上节点查看的时候用iptables -L INPUT -n --line-numbers发现 DROP 规则排在前面,而 kube-proxy 生成的规则链虽然在nat表正常,但是报文在进入之前就被 INPUT 链丢了,根本轮不到 NAT 转发。
这种问题的迷惑之处在于:iptables -t nat -L | grep 30080能看到所有规则都正常,感觉是健康的,但实际上 INPUT 链已经先把流量拒之门外。所以在节点网络排查这步,一定要把INPUT链和FORWARD链都看完整,不要只盯着nat表。
7. 排查工具和命令速查表
为了方便现场使用,我把常见排查动作整理成一份速查表。按照这个表格自上而下执行,绝大多数 NodePort 不通的问题都能把你带到正确的方向。
| 层级 | 检查对象 | 关键命令 | 期望结果 |
|---|---|---|---|
| 客户端网络 | 节点可达性 | ping <node-ip> | 通 |
| 客户端网络 | 端口可达性 | nc -vz <node-ip> <nodePort> | 显示 succeeded |
| 节点防火墙 | INPUT 规则 | iptables -L INPUT -n --line-numbers | 无拦截规则 |
| 节点转发 | 路由表 | ip route | 存在 pod 网段路由 |
| 节点转发 | 转发开关 | sysctl net.ipv4.ip_forward | 1 |
| k8s 对象 | Service 配置 | kubectl get svc <svc> -o yaml | type 为 NodePort/LoadBalancer |
| k8s 对象 | Endpoint | kubectl get endpoints <svc> | 有可用的 IP:Port |
| k8s 对象 | Selector 匹配 | kubectl get po --show-labels | 和 Service selector 一致 |
| 转发层 | iptables nat 规则 | `iptables -t nat -L -n | grep ` |
| 转发层 | ipvs 规则 | ipvsadm -L -n | 有对应端口和 RS |
| 转发层 | kube-proxy 状态 | kubectl get pod -n kube-system -l k8s-app=kube-proxy | Running |
| 容器网络 | CNI 插件状态 | `kubectl get pods -n kube-system | grep -E 'flannel |
| 容器网络 | Pod IP 连通 | ping <pod-ip>(在节点上) | 通 |
| 容器内部 | 进程监听 | kubectl exec <pod> -- ss -tlnp | 监听在 0.0.0.0 或 Pod IP |
| 系统状态 | conntrack | cat /proc/sys/net/netfilter/nf_conntrack_count | 远小于 max |
这个表格不是理论设计,而是我实际使用下来最稳定的一套顺序。正常情况下一路查下来,定位到根因的时间基本控制在十分钟以内。麻烦就麻烦在有些人跳过一两步直接查无关的位置,比如 Service 配置和 Endpoint 没问题就开始怀疑 CNI,这样最容易绕远路。
8. 一些容易让人头晕的特殊情况
NodePort 排查中还有一些特殊情况,乍看去完全不符合常规,但确实会频繁出现。把这几类单独拿出来说说,免得大家到时候怀疑人生。
8.1 多网卡节点的 NodePort 绑定问题
kube-proxy 的 NodePort 默认绑定在节点的所有 IP 上,但如果你给 kube-proxy 设置了nodePortAddresses(比如只允许绑定某个网段的 IP),那么其他网段的节点 IP 上就访问不到对应的 NodePort。这个配置写在 kube-proxy 的 ConfigMap 中:
nodePortAddresses: - 10.0.0.0/8如果节点有多个网卡,比如管理网段和业务网段,你从业务网段发起请求,但 kube-proxy 只监听管理网段,那就会出现“同一个节点,换个 IP 就通”的诡异现象。所以遇到这类情况,先看看节点上有几个 IP,再对比 kube-proxy 的nodePortAddresses设置。
8.2 Dual-Stack(双栈)集群下的 IPv6 遗漏
双栈集群里如果只排查 IPv4,很可能漏掉 IPv6 侧的问题。虽然目前大多数业务还是 IPv4 为主,但某些云环境里节点 IPv6 地址也被分配了,如果 Service 配置了ipFamilyPolicy: RequireDualStack,那么 NodePort 会同时监听 IPv4 和 IPv6。客户端用 IPv6 访问失败时,要先确认 kube-proxy 是否对这个 Service 生成了对应的 IPv6 规则,其次确认节点的 IPv6 转发和防火墙是否放行。
我个人建议在双栈环境排查 NodePort 时,直接用ip6tables -t nat -L -n查一下有没有对应条目。很多同行习惯性只查 IPv4,最后发现是 IPv6 规则缺失,搞得一头雾水。
8.3 硬件 LB 与 NodePort 混用时的额外注意点
如果你前面挂的是云负载均衡或者硬件 LB,那“NodePort 不通”可能是负载均衡自身的问题。比如健康检查的端口和协议写错了,目标组里的节点 IP 有误,或者后端权重设置成 0。这种情况不要一头扎进 k8s 内部排查,先绕过分层,直接从某个节点 IP 访问 NodePort,如果节点上访问没问题,问题就在负载均衡到节点这一段。
我习惯的做法是优先绕开所有中间层,直接用curl从某个节点本机访问 NodePort。如果本机通、外部不通,那问题一定在中间的某一环,不用动 k8s 的任何配置。
我的常用排障命令清单(按顺序执行)
数量不在多,关键是顺序。以下是我固定在服务器上跑的一串命令,遇到 NodePort 问题就按这个顺序执行一遍:
- 看 Service:
kubectl get svc -A | grep -i <service-name> kubectl get endpoints -A | grep -i <service-name>- 看 Pod 状态:
kubectl get pods -A -o wide | grep -i <service-name>- 看 kube-proxy:
kubectl get pods -n kube-system -o wide | grep kube-proxy- 看节点本机监听的端口:
ss -tlnp | grep <nodePort>这条命令特别有用。如果ss里能看到节点上已经监听了 30080,说明 kube-proxy 的 iptables/ipvs 规则已经建立,问题大概率在后面的转发或业务容器;如果看不到,直接去查 kube-proxy。
- 看 iptables 转发链:
iptables -t nat -L -n | grep <nodePort> ipvsadm -L -n | grep <nodePort>- 看节点到 Pod IP 连通性:
ping <pod-ip>- 最后一招,直接进容器验证:
kubectl exec -it <pod-name> -- curl 127.0.0.1:<targetPort>这条命令的意义在于,它可以区分是容器内部问题还是链路问题。如果容器内部curl 127.0.0.1:<targetPort>都不通,那问题100%在业务进程或容器启动参数;如果通,那外面不通的原因还需要继续往上排查。
9. 排查完之后,顺便做好这四件事
NodePort 问题一旦解决了,我建议顺手把集群的几个配置项检查一遍,能避免后面反复踩坑。这不是必须的运维流程,但对我来说每次修完都做一遍,整体稳定性提升非常明显。
第一件事是确认 kube-proxy 的 mode 是想要的模式。如果是 iptables 且 Service 数量超过几百个,规则链会非常长,节点上的 CPU 消耗也会定期突增。如果条件允许,把 mode 改成 ipvs 会省很多事。改完记得把所有节点上的 kube-proxy 都滚动重启一遍,确认ipvsadm -L -n能看到条目。
第二件事是检查各节点的 conntrack 参数是否一致。很多故障发生在集群扩容的时候,新节点 sysctl 参数没同步,流量一上来就现场翻车。把这些参数统一写进初始化脚本,至少检查nf_conntrack_max和ip_forward,两个就够了。
第三件事是确认 CNI 插件的更新策略。建议把 flannel/calico 的 DaemonSet 和 Deployment 都设为滚动更新,并且保证版本升级时节点逐个进行,避免出现某些节点 CNI 不工作导致的 NodePort 局部异常。
第四件事是在监控系统里加上 NodePort 探测,直接用 nodePort 端口做 TCP 探测。这样以后再出现这类问题,监控会在业务反馈之前先跳告警,而不是等到用户找你的时候才匆忙排查。
这几件事看着简单,真想维护好一个集群,还真得靠这些日常积累。
最后再多说一句
NodePort 排查本质上是个链路问题排查。只要心里有那条完整链路,手里的命令是固定的,答案也基本是确定的。怕就怕每次都是零散地乱试命令,没有框架,那样哪怕这次侥幸解决了,下次换个环境照样抓瞎。
我自己在实际排查中还有一个习惯值得分享:答完一个 NodePort 故障之后,我会把当时的 Service YAML、kube-proxy 模式、CNI 类型、节点的 sysctl 配置全部记录下来。下一次碰到类似现象,直接对照旧记录过滤一遍,能省下大量重复排查时间。如果你也在经常维护多个集群,强烈建议把这个习惯保持下去。