两个 Service 用了同一个端口号——K8s 的沉默最可怕
场景:新部署一个有 NodePort 30080 的 Service,kubectl apply 报错"port is already allocated",但查当前 namespace 的 Service 列表根本看不到 30080。另一个团队占用了端口——在另一个 namespace,你完全不知道 路径:坐标(创建失败但本地看不到占用)→ 分层(namespace → 集群级 NodePort 分配 → kube-proxy iptables 残留)→ 路径(3 条命令找到端口归属)→ 定位("换个 namespace 就没事了"的错觉)→ 标点(预防 + Check-list) K8s 版本:v1.28,kube-proxy iptables 模式
上篇讲了 NetworkPolicy——一条规则没配 5672,两个服务同时连不上。这篇我们看另一种"沉默拒绝"——两个 Service 用了同一个 NodePort,这次连错误信息都不给你。
部署完一个 NodePort: 30080 的 Service(节点端口类型 Service,在每个集群节点上开放一个固定端口,外部流量通过 NodeIP:NodePort 直达 Pod)。kubectl apply 返回了错误。
你查了一遍当前 namespace——没有 Service 用 30080。 又翻了一遍——还是没有。 "K8s 抽风了吧?换个端口。"
30081,apply 通过了。问题解决了?没有——你只是绕过了问题,不是找到了根因。
你不是端口冲突——你是看不到端口。
【坐标】NodePort 创建失败,Error 说"已分配"
结论前置:kubectl apply 报错了,但 kubectl get svc 找不到
$ cat web-frontend.yaml
apiVersion: v1
kind: Service
metadata:
name: web-frontend
namespace: frontend
spec:
type: NodePort
selector:
app: web-frontend
ports:
- port: 80
targetPort: 80
nodePort: 30080
$ kubectl apply -f web-frontend.yaml
The Service "web-frontend" is invalid:
spec.ports[0].nodePort: Invalid value: 30080: provided port is already allocated

马上查自己 namespace 的所有 Service:
$ kubectl get svc -n frontend -o wide
NAME TYPE CLUSTER-IP PORT(S) AGE
web-frontend ClusterIP 10.96.100.1 80/TCP 5m
等等——连 web-frontend 都没列出来(因为创建失败了),更别说看到 30080。你翻一遍所有 Service,确信没人用 30080。"K8s 的问题吧?换一个端口试试。"
换 nodePort: 30081,apply 通过。问题解决了?不是——根因没找到,30081 明天也会被另一个团队撞上。
证据展开:--all-namespaces 才看到真相
试一下 kubectl get svc --all-namespaces -o wide ——输出几十行,PORT(S) 列长这样:
9090:30900/TCP
NodePort 信息(上面说的节点端口)藏在 PORT(S) 列末尾,以 containerPort:NodePort 的格式混在端口列表里。一整页输出翻下来,你根本不会注意到 30080 出现了两次。
要用 grep 才能精准定位:
$ kubectl get svc --all-namespaces -o wide | grep 30080
backend api-gateway NodePort 10.96.150.1 8080:30080/TCP 20d
30080 被 backend namespace 的 api-gateway Service 占了。20 天前创建的,你完全不知道。
衔接:那如果两个 Service 同时用 30080 会怎样?
上述场景 kube-apiserver(Kubernetes API 服务器,集群的控制面核心,所有资源操作都通过它)阻止了冲突——第二个 Service 创建失败。但有一个更隐蔽的场景:两个 Service 并存了——kube-proxy(K8s 网络代理组件,运行在每个节点上,负责将 Service 流量转发到后端 Pod)没清理干净旧规则,新 Service 上来后 iptables 层出现两条竞争规则。
【分层】从端口占用查看,到 iptables 规则冲突

排查顺序
| 层级 | 排查对象 | 关键命令 | 异常信号 |
|---|---|---|---|
| ① namespace 层 | 当前 namespace Service | kubectl get svc -n <ns> -o wide |
看不到占用 |
| ② 集群层 | 全集群 Service NodePort | kubectl get svc --all-namespaces \| grep <port> |
另一个 namespace 占着 |
| ③ iptables 层 | kube-proxy NodePort 规则 | iptables -t nat -L KUBE-NODEPORTS -n |
同端口多条规则 |
| ④ kube-proxy 层 | kube-proxy 同步状态 | kubectl logs -n kube-system kube-proxy-xxx |
残留规则未清理 |
① namespace 层 — 查自己的 Service
第一直觉是查当前 namespace,结果没有任何 Service 用 30080。这就走入了第一个陷阱:NodePort 是集群级资源,但 kubectl get svc 不加 --all-namespaces 只查自己 namespace。
② 集群层 — --all-namespaces 找到真凶

$ kubectl get svc --all-namespaces -o wide | grep 30080
backend api-gateway NodePort 10.96.150.1 8080:30080/TCP 20d
NodePort 的分配由 kube-apiserver 维护:已分配的端口记录在 kube-apiserver 的内存中,同端口跨 namespace 也被阻止。但问题是:你看不到分配表。没有 kubectl get nodeport-allocations 这类命令。你只能通过 grep 全集群的 Service 输出反向推断。
这是 NodePort 冲突最核心的设计问题:端口分配是内置的、自动的,但查询接口是缺失的。
③ iptables 层 — kube-proxy 的残留规则
NodePort 冲突不仅在创建时发生。还有一种更隐蔽的情况:一个 Service 被删除后,kube-proxy 的 iptables 规则没有立即清理干净。新 Service 使用相同端口后,iptables 中出现两条指向不同后端的 DNAT 规则。

$ iptables -t nat -L KUBE-NODEPORTS -n
Chain KUBE-NODEPORTS (1 references)
target prot opt source destination
DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:30080 to:10.244.1.5:8080
DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:30080 to:10.244.2.10:8080
同一个端口 30080 两条 DNAT 规则——kube-proxy 没清理干净的残留和新规则同时存在。 外部请求打到 30080,iptables 会匹配命中第一条规则,导致流量全部去到一个旧的 Pod IP。
这种现象的原因是 kube-proxy 的同步延迟或部分节点上的 kube-proxy Pod 重启丢失了状态:
$ kubectl logs -n kube-system kube-proxy-j2k9s --tail=20 | grep -i "sync"
I0711 10:15:23.456789 1 iptables.go:178] "Syncing iptables rules"
W0711 10:15:25.123456 1 iptables.go:205] "Received invalid service update, retrying"

当一个 Service 被删除,kube-proxy 需要触发一次全量同步来清理相关 iptables 规则。如果同步失败(API Server 连接中断、kube-proxy 重启),旧规则就留在了节点上。
衔接:找到了问题根源,那怎么预防下一次?
NodePort 冲突的根本原因不是技术问题——是可见性问题。集群里 50 个 namespace,谁都不知道端口 30080 已被占用。等创建时报错,已经是事后了。
【路径】🔍 3 条命令找到端口归属
结论前置:三板斧确定冲突

异常判断标准
| 命令 | 正常 | 异常 |
|---|---|---|
kubectl get svc -A \| grep 30080 |
唯一匹配 | 多条匹配(同端口多个 Service 残留) |
kubectl describe svc NodePort 行 |
显示 NodePort: <port> <setNodePort> |
NodePort 为空 — 自动分配未完成 |
iptables -L KUBE-NODEPORTS |
每个端口一条 DNAT 规则 | 同端口多条 DNAT — 规则冲突 |
衔接:3 条命令顺序即排查思路
先查集群 Service 看谁占着端口 → 再查 iptables 看 kube-proxy 有没有残留规则 → 两条都确认了再接下一步修复。中间跳一步都可能误判。
【定位】最常见的误判
❌ 错误方向:"换个端口就好了"
kubectl apply 报错 "port is already allocated"
→ "哦被占用了,换个端口 30081"
→ kubectl apply 成功
→ "好了,问题不大"
为什么这个路径是错的:
换端口看似解决了眼前问题,但: - 你没有确定 30080 被谁占着,下一次部署到另一个 namespace 还会撞 - 30081 可能也被部分占用了(自动分配范围中的端口),明天另一个团队又撞上 - 更危险的是:如果旧 Service 被删除后 iptables 规则残留,你用了它的旧端口就可能在流量转发层面出现冲突
一个端口撞了换一个——这不是排查,是掩耳盗铃。
✅ 正确方向:"找到端口归属 + 建立预防机制"
kubectl apply 报错 "port is already allocated"
→ kubectl get svc -A | grep 30080 → 找到 backend/api-gateway
→ 确认是否真的是两个团队需要同一个端口
→ 方案 A:团队协商,谁用不同的端口
→ 方案 B:建立 NodePort 分配表/静态分配策略
→ 方案 C:NodePort 范围扩容 + 团队分区
两者的本质差异:错误方向把端口冲突看作偶发事件绕过,正确方向把它当作集群资源管理问题系统性解决。
检查 iptables 规则是防止残留规则导致问题的最后一道防线。iptables -t nat -L KUBE-NODEPORTS | grep 30080 的输出是一条还是多条——这个判断决定了"修好了"还是"以为修好了"。
【标点】修复 + Check-list
修复方案 A:静态 NodePort 分配 + 团队分区

将 NodePort 范围分成区域,每个团队分配一段(截图已展示完整映射),Service YAML 中显式指定分区内的静态端口即可。
修复方案 B:扩容 NodePort 范围 + 清理残留规则
# 1. 修改 kube-apiserver 启动参数扩大范围
# 在 /etc/kubernetes/manifests/kube-apiserver.yaml 中添加:
# --service-node-port-range=30000-33767
# 2. 节点上清理残留 iptables 规则
iptables -t nat -D KUBE-NODEPORTS -p tcp --dport 30080 \
-j DNAT --to-destination 10.244.1.5:8080
# 3. 触发 kube-proxy 全量同步
kubectl rollout restart -n kube-system daemonset kube-proxy

修复方案 C:用 Ingress 替代 NodePort(生产环境推荐)
NodePort 本身是为开发测试设计的,生产环境建议使用 Ingress Controller(入口控制器,集群外部流量的统一入口,通过域名/路径规则将请求路由到内部 Service)暴露服务。Ingress 只需要一个 NodePort(供 Ingress Controller 使用),所有后端 Service 用 ClusterIP 即可。
NodePort 冲突排查 Check-list
□ kubectl get svc --all-namespaces -o wide | grep <port>
→ 列出全集群中使用了该 NodePort 的 Service
→ 确认是哪个 namespace 占用的
□ kubectl describe svc <name> -n <ns> | grep NodePort
→ 查看该 Service 的 NodePort 配置
→ 确认是显式指定的还是自动分配的
□ iptables -t nat -L KUBE-NODEPORTS -n | grep <port>
→ 查看节点上 kube-proxy 的 DNAT 规则
→ 同端口多条 → 残留规则未清理
□ kubectl logs -n kube-system -l k8s-app=kube-proxy | grep -i "sync"
→ 查看 kube-proxy 是否有同步异常
→ "Failed to sync" → kube-proxy 工作不正常
□ 检查集群 NodePort 范围配置
→ kubectl describe pod -n kube-system kube-apiserver-<node>
→ grep service-node-port-range 确认范围
附:完整命令清单
# === 1. NodePort 占用查找 ===
kubectl get svc --all-namespaces -o wide | grep <port>
kubectl describe svc <name> -n <namespace> | grep -A 2 "NodePort"
# === 2. 自定义列查询(只显示 Name + Namespace + NodePort) ===
kubectl get svc --all-namespaces -o custom-columns=\
'NAMESPACE:.metadata.namespace,NAME:.metadata.name,NODEPORT:.spec.ports[*].nodePort'
# === 3. iptables NodePort 规则检查 ===
iptables -t nat -L KUBE-NODEPORTS -n
iptables -t nat -L KUBE-NODEPORTS -n | grep <port>
# === 4. kube-proxy 状态 ===
kubectl get pods -n kube-system -l k8s-app=kube-proxy
kubectl logs -n kube-system -l k8s-app=kube-proxy --tail=50 | grep -i "sync\|error"
# === 5. NodePort 范围检查 ===
kubectl describe pod -n kube-system kube-apiserver-$(hostname) \
| grep service-node-port-range
# === 6. 修复 ===
# 触发 kube-proxy 全量同步
kubectl rollout restart -n kube-system daemonset kube-proxy
# 手动清理残留 iptables 规则
iptables -t nat -D KUBE-NODEPORTS -p tcp --dport <port> \
-j DNAT --to-destination <old-pod-ip>:<port>
NodePort 冲突的本质不是端口不够用——30000-32767 有 2767 个端口,远多于正常需求。是集群扩大了,但管理思维还停留在单 namespace 阶段。端口分配是集群级的,可见性却是 namespace 级的。
你现在集群的 NodePort 是谁在管?有没有一张分配表?如果没有——今天这篇文章就是你的第一张。
下篇我们聊 Pod 内无法访问集群外网络——所有 K8s 进程都正常,但 Pod 里 curl 外网超时。是 CNI 没配 NAT?还是节点路由少了一条?
🔗 个人博客:https://opencao.cn
📺 公众号:Ai拆代码的曹操
🌟 知识星球:Ai拆代码的曹操