Skip to content

K8s 集群部署排错 ​

本文基于一次真实的 KubeKey 部署 KubeSphere 环境排错过程整理,涵盖 NFS 服务启动失败、NFS Provisioner 调度失败、Worker 节点反复 NotReady、noexec 挂载导致脚本无法执行、sudo 权限配置缺失、节点标签缺失等 6 类问题的完整排查路径与解决方案。

问题一:nfs-server 服务启动失败(unit is masked) ​

现象 ​

执行启动 NFS 服务时报错:

text
failed to restart nfs-server.service
unit nfs-server.service is masked

原因 ​

masked 是比 disable 更彻底的状态,表示该服务被系统管理员主动屏蔽,目的是阻止它被任何方式启动。屏蔽通常是通过创建指向 /dev/null 的符号链接实现的。

解决方案 ​

bash
# 1. 解除屏蔽
sudo systemctl unmask nfs-server.service

# 2. 启动服务
sudo systemctl restart nfs-server.service

# 3. 设置开机自启(可选)
sudo systemctl enable nfs-server.service

补充排查 ​

如果解除屏蔽后仍然启动失败,检查是否有其他依赖服务也被屏蔽:

bash
# 查看是否有指向 /dev/null 的符号链接
ls -l /etc/systemd/system/ | grep nfs

# 检查相关依赖
systemctl status nfs-mountd rpc-statd

NFS 服务依赖于 nfs-mountd、rpc-statd 等组件,如果有类似的 masked 状态,同样用 unmask 解除。

问题二:NFS Provisioner 调度失败 ​

现象 ​

bash
kubectl describe pod -n kube-system nfs-subdir-external-provisioner-xxx

输出显示:

text
Conditions:
  PodScheduled  False

Events:
  Warning  FailedScheduling
  0/2 nodes are available:
  1 node(s) had untolerated taint {node-role.kubernetes.io/control-plane: },
  1 node(s) had untolerated taint {node.kubernetes.io/unreachable: }
  preemption: 0/2 nodes are available

Node-Selectors:  rise.io/manager=true

原因 ​

有两个独立原因叠加:

  • master 节点有 control-plane 污点,Pod 没有对应容忍,无法调度。
  • worker 节点有 unreachable 污点(因节点 NotReady 产生),Pod 同样无法调度。
  • Pod 的 Node-Selector 要求 rise.io/manager=true 标签,但节点上缺少该标签。

解决方案 ​

第一步:修复 worker 节点 NotReady(见问题三)

节点恢复 Ready 后,unreachable 污点会自动消失。

第二步:给节点打标签

bash
# 在 master 上执行
kubectl label node 110236p.corp.scaniagroup.cn rise.io/manager=true

# 验证标签
kubectl get node 110236p.corp.scaniagroup.cn --show-labels

# 检查 Pod 是否正常调度
kubectl get pod -n kube-system | grep nfs

补充说明 ​

rise.io/manager=true 是 KubeKey 或存储组件用来指定管理节点的标签,让 NFS Provisioner 固定跑在特定节点上。

问题三:Worker 节点因 8081 端口占用导致反复 NotReady ​

现象 ​

Worker 节点 110236p.corp.scaniagroup.cn 状态在 Ready 与 NotReady 之间反复横跳。

kubectl describe node 显示:

text
Conditions:
  Type              Status   Reason
  Ready             Unknown  NodeStatusUnknown
  MemoryPressure    Unknown  Kubelet stopped posting node status
  DiskPressure      Unknown  Kubelet stopped posting node status
  PIDPressure       Unknown  Kubelet stopped posting node status

Taints:
  node.kubernetes.io/unreachable:NoExecute
  node.kubernetes.io/unreachable:NoSchedule

Lease:
  Failed to get lease: leases.coordination.k8s.io "..." not found

kubelet 日志关键报错:

text
E0914 ... kubelet_node_status.go] "Unable to register node with API server"
  err="Post \"https://127.0.0.1:6443/api/v1/nodes\": dial tcp 127.0.0.1:6443: connect: connection refused"

E0914 ... controller.go:145] "Failed to ensure lease exists, will retry"
  err="Get \"https://127.0.0.1:6443/apis/coordination.k8s.io/v1/...\":
  dial tcp 127.0.0.1:6443: connect: connection refused"

根因分析 ​

故障链如下:

text
McAfee 端点安全软件占用 8081 端口
        ↓
HAProxy 静态 Pod 无法绑定 8081,启动即崩
        ↓
本地 127.0.0.1:6443 代理不可用
        ↓
kubelet 无法连接 API Server → 节点 NotReady
        ↓
节点被打上 unreachable 污点 → 其他 Pod 无法调度

此外还发现一个附带问题:

  • HAProxy 配置文件权限问题:宿主机配置文件属主为 LDAP 用户 gqu1vx,权限 640,容器内 HAProxy 进程以非 root 运行,读取报 Permission denied。

排查步骤 ​

步骤 1:从集群侧观察

bash
kubectl get nodes
kubectl describe node 110236p.corp.scaniagroup.cn

步骤 2:登录节点检查 kubelet

bash
systemctl status kubelet
journalctl -u kubelet --no-pager -n 100

步骤 3:定位 API Server 连接问题

bash
grep server /etc/kubernetes/kubelet.conf

预期结果:KubeKey 高可用部署中应为 https://127.0.0.1:6443。

步骤 4:检查本地 HAProxy 状态

bash
crictl ps -a | grep haproxy
crictl logs <haproxy-container-id>

发现 1:配置文件权限问题

text
[ALERT] (1) : config : Could not open configuration file
  /usr/local/etc/haproxy/haproxy.cfg : Permission denied
bash
ls -l /etc/haproxy/haproxy.cfg
# -rwxr-xr-x. 1 gqulvx ot010005-scan iausers 1096 Sep 14 15:06 haproxy.cfg

发现 2:端口冲突

text
[ALERT] (1) : Binding [/usr/local/etc/haproxy/haproxy.cfg:24]
  for frontend healthz: cannot bind socket (Address in use) for [0.0.0.0:8081]
[ALERT] (1) : [haproxy.main()] Some protocols failed to start their listeners! Exiting.
bash
netstat -anput | grep 8081
# tcp6  0  0 :::8081  :::*  LISTEN  3384/macmnsvc

解决方案 ​

修复 1:配置文件权限

bash
chown root:root /etc/haproxy/haproxy.cfg
chmod 644 /etc/haproxy/haproxy.cfg

注意:不要修改 /etc/kubernetes/manifests/haproxy.yaml 的权限,那是 kubelet 读取的静态 Pod 清单,与容器内进程无关。

修复 2:解决端口冲突

修改宿主机配置文件中的健康检查端口:

bash
sed -i 's/bind :8081/bind :8082/' /etc/haproxy/haproxy.cfg

同时修改 /etc/kubernetes/manifests/haproxy.yaml 中的探针端口:

yaml
containers:
- name: haproxy
  image: ...
  livenessProbe:
    httpGet:
      path: /healthz
      port: 8082   # 与 haproxy.cfg 中保持一致
  readinessProbe:
    httpGet:
      path: /healthz
      port: 8082   # 与 haproxy.cfg 中保持一致

关键原则:HAProxy 的 healthz 绑定端口必须与 kubelet 探针的目标端口一致,改一处必须同步改另一处。

验证修复效果 ​

bash
# 1. 检查端口监听
ss -tlnp | grep -E '8082|6443'

# 2. 模拟健康检查
curl -s -o /dev/null -w "%{http_code}\n" http://127.0.0.1:8082/healthz
# 预期输出:200

# 3. 验证 6443 代理
curl -k https://127.0.0.1:6443/healthz
# 预期输出:ok

# 4. 检查容器状态
crictl ps | grep haproxy
# 预期:Running

# 5. 在 master 上观察节点状态
kubectl get nodes -w

KubeKey 高可用架构说明 ​

KubeKey 部署高可用集群时,每个节点都会运行一个本地 HAProxy 静态 Pod:

text
kubelet → 127.0.0.1:6443 (本地 HAProxy) → master:6443 (真实 API Server)
  • 设计意图:kubelet 不需要知道 master 的真实 IP,统一走本地回环地址,master 变更时只需改 HAProxy 配置。
  • 副作用:本地 HAProxy 一旦故障,该节点的 kubelet 立即失联,节点变为 NotReady。

问题四:/tmp 分区 noexec 导致脚本无法执行 ​

现象 ​

以 root 权限执行 ./run.sh 提示:

text
bash: ./run.sh: Permission denied

但用 bash ./run.sh 就没问题。

原因 ​

./run.sh 与 bash ./run.sh 的本质区别:

方式需要 x 权限需要 r 权限是否用 shebang
./run.sh✅ 必须视情况✅ 需要
bash ./run.sh❌ 不需要✅ 必须❌ 不需要

即使文件有执行权限、shebang 也正确(#!/usr/bin/env bash),如果文件位于 noexec 挂载的分区(如 /tmp),内核仍然拒绝执行。

排查步骤 ​

bash
# 1. 确认文件权限
ls -l ./run.sh

# 2. 确认文件所在分区的挂载选项
findmnt -T ./run.sh -o TARGET,SOURCE,FSTYPE,OPTIONS

# 3. 检查是否包含 noexec
mount | grep -E "$(df --output=target run.sh | tail -1)"

如果 OPTIONS 中出现 noexec,即为此原因。

解决方案 ​

方案一:临时重新挂载(立即生效,重启失效)

bash
sudo mount -o remount,exec /tmp

方案二:永久修改(编辑 /etc/fstab)

bash
# 先备份
sudo cp /etc/fstab /etc/fstab.bak

# 编辑 fstab,将 /tmp 行中的 noexec 删除或替换为 exec
sudo vi /etc/fstab

将:

text
/dev/vdb1  /tmp  ext4  rw,noexec,relatime  0 2

改为:

text
/dev/vdb1  /tmp  ext4  rw,exec,relatime  0 2

方案三:把脚本移出 /tmp(最安全)

bash
cp /tmp/run.sh /root/run.sh
cd /root && ./run.sh

如果安全合规要求 /tmp 必须保持 noexec,推荐使用此方案。

补充说明 ​

即使有 x 权限,如果脚本第一行缺少 shebang,./run.sh 会默认用 /bin/sh 执行,可能导致语法不兼容。

其他可能触发 Permission denied 的原因还包括:文件系统不支持执行权限(vfat/ntfs/exfat)、ACL 限制、SELinux/AppArmor 拦截、容器内 noexec 挂载等。

问题五:sudo 用户缺少 sudoers 配置导致执行报错 ​

现象 ​

用 KubeKey 部署时提示:

text
Sorry, user gqu1vx is not allowed to execute '/bin/bash -c echo greetings,kubekey' as root on master, process exited with status 1

原因 ​

该报错说明 sudo 本身能正常运行,但用户没有匹配到任何免密 sudo 规则。

对于 LDAP/AD 用户,sudoers 文件里的用户名必须与 id 或 getent passwd 返回的登录名完全一致。

如果 SSSD 配置了 use_fully_qualified_names = True,系统识别的用户名可能是 gqu1vx@your.domain.com 格式,而不是短名 gqu1vx。

排查步骤 ​

bash
# 1. 确认 sudo 实际识别的用户名
id
whoami

# 2. 确认 sudoers 规则是否匹配
sudo -l -U gqu1vx

# 3. 检查 sudoers 文件语法
sudo visudo -c -f /etc/sudoers.d/kubekey

解决方案 ​

方案一:按短名配置

bash
echo "gqu1vx ALL=(ALL) NOPASSWD: ALL" | sudo tee /etc/sudoers.d/kubekey
sudo chmod 440 /etc/sudoers.d/kubekey

方案二:按全限定名配置(如果系统用 FQDN 识别用户)

bash
echo "gqu1vx@your.domain.com ALL=(ALL) NOPASSWD: ALL" | sudo tee /etc/sudoers.d/kubekey

方案三:通过组授权(更稳妥)

bash
# 先确认用户所属组
id gqu1vx

# 假设属于 wheel 或 sudo 组
echo "%wheel ALL=(ALL) NOPASSWD: ALL" | sudo tee /etc/sudoers.d/kubekey

注意:组名前必须加 %。

验证 ​

bash
# 非交互模式测试,不需要密码即返回成功
sudo -n true && echo "免密 sudo 配置成功" || echo "配置未生效"

# 校验 sudoers 语法
sudo visudo -c -f /etc/sudoers.d/kubekey
# 应输出 parsed OK

补充排查 ​

检查 /etc/sudoers 是否包含了 /etc/sudoers.d/:

bash
grep includedir /etc/sudoers

应输出类似 #includedir /etc/sudoers.d。如果没有,需要改用 visudo 直接在主文件末尾追加规则。

检查 LDAP 用户解析:

bash
id gqu1vx
getent passwd gqu1vx

如果这两条命令返回空,说明问题在 LDAP 客户端配置(/etc/nsswitch.conf、/etc/sssd/sssd.conf 等),而不是 sudoers 本身。

为什么 KubeKey 要求免密 sudo ​

KubeKey 在执行远程命令时使用 sudo -E 方式,且通过 SSH 非交互式通道运行。当 sudo 需要密码时,密码提示无法正常传递,会导致 sudo: 3 incorrect password attempts 错误,即使密码完全正确。因此官方和社区的一致做法是:在目标节点的 /etc/sudoers 中为部署用户配置免密 sudo。

问题六:节点缺少 rise.io/manager=true 标签导致组件调度失败 ​

现象 ​

部分组件(如 NFS Provisioner)Pod 一直处于 Pending 状态,kubectl describe pod 显示:

text
Node-Selectors:  rise.io/manager=true
Events:
  Warning  FailedScheduling
  0/2 nodes are available: 1 node(s) didn't match Pod's node affinity/selector

原因 ​

该 Pod 的 Node-Selector 要求节点必须带有 rise.io/manager=true 标签,但当前集群中没有节点满足此条件。

这个标签通常由 KubeKey 或存储组件用来指定管理节点,让特定组件固定跑在某一台节点上。

解决方案 ​

在 master 节点上执行:

bash
# 给目标节点打标签
kubectl label node 110236p.corp.scaniagroup.cn rise.io/manager=true

# 如果标签已存在但值不同,加 --overwrite 覆盖
kubectl label node 110236p.corp.scaniagroup.cn rise.io/manager=true --overwrite

验证 ​

bash
# 查看节点全部标签
kubectl get node 110236p.corp.scaniagroup.cn --show-labels

# 只查看该标签的值
kubectl get node 110236p.corp.scaniagroup.cn -o jsonpath='{.metadata.labels.rise\.io/manager}'
# 预期输出:true

# 检查 Pod 是否正常调度
kubectl get pod -n kube-system | grep nfs
# 预期:从 Pending 变为 Running

补充说明 ​

如果后续还有其他 Pod 也要求这个标签,打一次就够了,所有需要它的 Pod 都能匹配上。如果该标签是由 KubeKey 在特定阶段自动添加的,也可以考虑排查为何没有自动添加(比如部署流程中断、某个 addon 未执行)。

附录 A:常见陷阱速查表 ​

陷阱说明
改错文件haproxy.yaml(静态 Pod 清单)和 haproxy.cfg(HAProxy 配置文件)是两个不同文件
端口改一处改 HAProxy 的 bind 端口后,必须同步改 YAML 探针端口
LDAP 用户权限LDAP 用户的 UID/GID 在容器内可能对不上,导致文件读取失败
安全软件干扰McAfee/Trellix 等安全软件常与 K8s 组件抢占端口、hook 系统调用
污点残留节点恢复 Ready 后,unreachable 污点不会立即消失,需等待控制平面处理
脚本执行权限./run.sh 需要 x 权限,bash ./run.sh 只需要 r 权限,noexec 挂载会拒绝执行
sudo 用户名匹配LDAP 用户的短名/全限定名必须与系统识别的一致
标签缺失Node-Selector 依赖的标签如果缺失,Pod 会一直 Pending

附录 B:Worker 节点 NotReady 快速检查清单 ​

当 Worker 节点出现 NotReady 时,按以下顺序快速排查:

  • [ ] kubectl describe node <node> — 查看 Conditions 和 Taints
  • [ ] systemctl status kubelet — 确认 kubelet 进程状态
  • [ ] journalctl -u kubelet --no-pager | grep -i error — 查看关键错误
  • [ ] grep server /etc/kubernetes/kubelet.conf — 确认 API Server 地址
  • [ ] crictl ps -a | grep haproxy — 检查本地 HAProxy 状态
  • [ ] crictl logs <haproxy-id> — 查看 HAProxy 日志
  • [ ] ls -l /etc/haproxy/haproxy.cfg — 检查配置文件权限
  • [ ] netstat -anput | grep <port> — 检查端口占用
  • [ ] ss -tlnp | grep 6443 — 确认本地代理监听
  • [ ] curl -k https://127.0.0.1:6443/healthz — 验证代理连通性

附录 C:完整故障链示意 ​

text
McAfee 占用 8081 端口
        ↓
HAProxy 无法绑定端口 → 启动失败
        ↓
本地 127.0.0.1:6443 代理不可用
        ↓
kubelet 连不上 API Server → 节点 NotReady
        ↓
节点被打上 unreachable 污点
        ↓
NFS Provisioner 等 Pod 无法调度
        ↓
同时:节点缺少 rise.io/manager=true 标签 → 调度进一步失败

文档信息 ​

项目内容
文档版本v2.0
适用环境KubeKey 部署的 KubeSphere / Kubernetes 集群
涵盖问题6 类
最后更新2026-09-15

基于 Rise 智算平台 release-2606.v2.2.x 生成