节点防火墙收严之后,kubectl 直连 6443 全断了

业务页面正常,Pod 全是 Running,kubectl 却全部 timeout。查下来是节点策略把管理面挡在外面;端口扫描报 open 也只是上游设备的 SYN 代答,不能当判据。

节点加固那天,我做的是"把不需要的端口关掉"。规则很简单:对外只留 SSH、80、443,加一个 VPN 的 UDP 端口,其余全部拒绝。

变更当时验证是通的:业务页面正常,Pod 全部 Running,SSH 也能上。第二天早上我在管理机上敲 kubectl get nodes,全部 i/o timeout。第一反应是集群挂了,赶紧 SSH 上节点看,crictl ps 里容器一个不少,apiserver 进程也在。

一、先分清"集群坏了"和"我连不上"

这两件事的现象高度相似:kubectl 报错,看起来都一样。区别在于错误文本。

kubectl get nodes
# Unable to connect to the server: dial tcp <node>:6443: i/o timeout

ssh "$NODE" 'sudo ss -lntp | grep -E ":(6443|2379|10250)\b"'
# LISTEN 0 4096 *:6443 *:* users:(("kube-apiserver",pid=2314,fd=7))
# LISTEN 0 4096 127.0.0.1:2379 *:* users:(("etcd",pid=1888,fd=8))

一个 timeout 加一个"进程在、端口在监听",基本就定性了:服务没死,是包到不了。这时不要再去查 Pod、查 etcd、重启组件,先查链路。

二、端口扫描的结果不能当判据

排查过程中我犯过一个错:拿端口扫描去确认"哪些端口还开着",扫出来 25、110、143 都是 open。这三个端口我们根本没跑服务,看起来像"策略没生效",于是差点把刚写的规则全部推翻。

实际上那是上游网络设备的 SYN 代答:它替不存在的服务回应握手,扫描器就报 open。真正的判据是"能不能完成一次应用层交互",不是"TCP 握手成不成功"。

# 用应用层协议判断,而不是 TCP 握手
curl -sk -o /dev/null -w 'api=%{http_code}\n' --max-time 3 https://$NODE:6443/healthz
curl -s  -o /dev/null -w 'web=%{http_code}\n' --max-time 3 http://$NODE/

api=000(超时/拒绝)而 web=200,两条一对比,范围立刻缩小到"6443 这一个端口被挡"。

三、策略是怎么写的,为什么 6443 被挡

节点级策略分两组:一组管主机网络(SSH、kubelet、控制面端口),一组管 NodePort 范围。结构都是"先放行内部,再放行指定端口,最后一条兜底"。

# 结构示意(节选)
- action: Allow
  source:
    nets: ["127.0.0.0/8", "$POD_CIDR", "$SERVICE_CIDR", "$NODE1/32", "$NODE2/32"]
  destination: {}
- action: Allow
  protocol: TCP
  destination:
    ports: [22, 80, 443]
- action: Allow
  protocol: UDP
  destination:
    ports: [1194]
- action: Allow          # 末尾这条必须保留
  source:
    selector: all()

第一段是内部全放行:回环、Pod 网段、Service 网段、两台节点互相之间。集群自身要用的东西都在这段里——etcd 的 2379、kubelet 的 10250、apiserver 的 6443,节点之间互访不受影响。

第二段是对外只留三个 TCP 端口。我的管理机在另一个网段,不在第一段的名单里,所以 6443 被这条规则挡在外面。这个结果是符合预期的:加固的目标本来就是"管理面不出内网"。

真正的问题不是规则写错,而是我的操作习惯没跟着变——我一直是直连节点跑 kubectl,而这个动作在加固后已经不成立了。

四、failsafe 里不要补 6443

有些方案会在紧急通道(failsafe)里把常用端口都列上,我一开始也想把 6443 加进去,"万一 SSH 也断了还能用 API 救"。

这个想法是错的,原因有两条:

  • 紧急通道的意义是"最小可恢复集合"。SSH 加上 DHCP 客户端(22/TCP、68/UDP)就够救机器了;把 6443 放进去,等于把管理面重新对全网开放,加固白做。
  • 更危险的是"以为有后路"。加了 6443 之后,日常操作仍然能连,团队就不会去建跳板,等哪天策略再收紧一次,故障会以更突然的方式出现。

正确的用法是两条:SSH 到管控节点,在节点上跑 kubectl;或者从集群内发起(一个临时 Pod 或已有的运维 Job)。

# 在管控节点上本地跑,走的是 kubeconfig 里的 127.0.0.1
ssh "$NODE" 'sudo kubectl --kubeconfig /etc/kubernetes/admin.conf get nodes'

# 或者从集群内发起,用 Service 名而不是节点地址
kubectl -n ops run probe-$RANDOM --rm -it --restart=Never \
  --image=bitnami/kubectl:1.31 -- get --raw /healthz

五、验收清单:端口、来源、期望

改完之后我列了一张矩阵,逐条验证,而不是只看"页面能开"。

端口/协议 来源 期望 说明
22/TCP 管理网段 通 唯一的人工入口
80/443/TCP 任意 通 业务入口,由前置代理回源
1194/UDP 指定地址 通 VPN 备用通道
6443/TCP 管理网段 不通 控制面只对集群内部
6443/TCP 节点之间 通 集群自身需要
2379/TCP 节点之间 通 etcd 只监听本机与对端
10250/TCP 节点之间 通 kubelet
30000-32767/TCP 集群外 不通 NodePort 不对公网暴露
# 从管理机逐条打,用应用层判断
for t in 22 80 443 6443; do
  if curl -sk -o /dev/null --max-time 2 "https://$NODE:$t/" 2>/dev/null || \
     timeout 2 bash -c "</dev/tcp/$NODE/$t" 2>/dev/null; then
    echo "$t reachable"
  else
    echo "$t blocked"
  fi
done

从节点内部再验一遍,确认"内部互访没被误伤":

ssh "$NODE" 'for t in 6443 2379 10250; do
  timeout 2 bash -c "</dev/tcp/127.0.0.1/$t" && echo "$t local-ok" || echo "$t local-FAIL"
done'

这一步比对外验证更重要。对外挡错了,症状很明显;对内挡错了,表现是"偶发的 502、etcd 慢、日志断流",要查很久才能联想到防火墙。

六、回滚文件要能用一条命令生效

这套策略的备份是变更前的两个完整对象,回滚就是一次 apply:

kubectl apply -f bak/host-guard-before.yaml
kubectl apply -f bak/nodeport-guard-before.yaml

要点是备份必须是完整对象,不能是"我改的那几行"。策略对象是整份替换的,只备份差异等于没有备份。

另外,回滚之后要重新跑一次上面的矩阵——回滚不一定回到你想要的状态,尤其当变更分了几轮做。

七、把"连不上"变成可观测项

这次排查花了一个多小时,主要时间不是修,而是确认"到底是集群问题还是我的问题"。后来加了两条监控:

  • 集群内每分钟请求一次 /healthz,失败就告警。这条从内部探,不受节点策略影响,能区分"apiserver 挂了"和"外面进不来"。
  • 管理机的 kubectl 定时探活,失败只记日志不告警——它现在本来就该失败,记下来是为了观察有没有人还在用旧习惯。

第二点看起来多余,但它反映了一个真实问题:加固之后,团队里每个人的操作习惯都要改,不改就会反复撞墙。

小结

  • kubectl 报 i/o timeout 时,先用 ss -lntp 确认进程和监听,再判断是服务问题还是链路问题。
  • 端口扫描的 open 可能是上游设备的 SYN 代答,判据要用应用层请求,不是 TCP 握手。
  • 节点策略要分"内部全放行"和"对外白名单"两段,内部那段漏一个网段,症状会以偶发故障的形式出现。
  • 末尾那条兜底 Allow 不能删,删了会把内部流量也一起拒掉,连恢复通道都可能断。
  • 紧急通道只留恢复所需的最小集合(22/TCP、68/UDP),不要为了顺手把 6443 加回去。
  • 管理面入口走跳板或集群内发起,要写进文档;回滚备份必须是完整对象,回滚后重跑验收矩阵。
查看「容器与编排」全部文章 →

同分类文章

查看 0 评论 0

评论

正在加载评论…

留言提交后自动审核:正常内容立即显示,含敏感信息的会转人工复核。