Kubelet节点注册Unauthorized报错全解析:从token到CSR认证链路排查 1. 节点注册失败背后的完整认证链路先说个真实场景。某次我给一套集群扩容新节点一切配置就绪kubeadm join命令执行完kubelet 服务刚拉起来日志里就刷出这么一行Unable to register node node-02 with API server: Unauthorized当时第一反应是 token 出了问题但重新kubeadm token create之后问题依旧。后来查了一晚上才发现问题不在 token 本身而在证书申请环节的权限配置。这个报错信息很短但背后牵扯到的认证链路一点也不简单。在真正动手排查之前必须把 kubelet 向 API server 注册节点的过程搞清楚。这一条链路理清楚了排错起来至少能少走一半弯路。1.1 kubelet 注册节点的两个阶段认证与授权节点要加入集群kubelet 进程会拿着自己的身份凭证去请求 API server 的node资源或者 CSR 资源。API server 收到请求后要做两件事第一件事是认证Authentication确认你是谁。kubelet 出示的凭证通常有两种——要么是 Join 时生成的 bootstrap token要么是已经签发的客户端证书。如果 API server 不认识这份凭证直接返回 401 Unauthorized。第二件事是授权Authorization确认你能干什么。即使认证通过API server 还要检查该身份是否有权限执行注册节点这个动作。如果认证没问题但权限不足会返回 403 Forbidden。比较坑的是kubelet 日志里有时不会严格区分 401 和 403经常统一表现为Unauthorized。所以这个报错的信息量其实很小必须自己往深处挖。1.2 bootstrap token 与证书模式两条注册路径新节点加入集群走的是bootstrap 流程节点用 bootstrap token 向 API server 发起首次请求。这个 token 对应的 RBAC 角色是system:node-bootstrapper它没有直接注册节点的权限但有权创建 CSRCertificateSigningRequest。kubelet 生成一对密钥把公钥放进 CSR 提交上去。集群的 controller-manager或管理员手动审批该 CSR。审批通过后kubelet 拿到签发的客户端证书后续再用证书身份去注册节点。如果集群配置了自动批准 bootstrap 节点的 CSR通常通过clusterrolebinding绑定了system:certificates.k8s.io:certificatesigningrequests:nodeclient这类角色流程可以全自动跑完。问题往往就出在中间某个环节——token 失效、CSR 没人批、RBAC 绑定被改动、证书 CN 不匹配全都会冒出Unauthorized报错。2. 收集三路证据日志、kubeconfig、集群状态看到Unauthorized后我的习惯是先别急着删节点重来而是先把现场信息收集齐。这一步看似简单实际上有不少人卡在这里——因为不知道该看哪份日志、哪个文件、哪个资源对象。2.1 从 kubelet 日志中定位更详细的原因kubelet 的系统日志是第一现场。不同的发行版日志位置不太一样# systemd 系 journalctl -u kubelet -f --since 10 minutes ago # 或直接查日志文件 tail -100 /var/log/messages tail -100 /var/log/syslog重点看Unable to register node这行日志的上下文尤其是紧挨着的前几十行。你会看到很多关键线索Unauthorized前面有没有伴随证书相关的错误比如x509: certificate signed by unknown authority有没有 CSR 相关的提示比如csr wait超时有没有明显的 URL比如请求了/api/v1/nodes还是/apis/certificates.k8s.io/v1/certificatesigningrequests有一次我遇到报错Unable to register node下面紧跟着一行failed to ensure that the node exists看起来像是在注册节点时出了问题但实际往前翻了几页发现 kubelet 一直在尝试提交 CSR却迟迟没被批准。这个细节如果只看后一行很容易误判成节点注册本身的问题。2.2 检查 bootstrap kubeconfig 文件是否过期或损坏新节点加入集群时用的凭证在/etc/kubernetes/bootstrap-kubeconfig里。这个文件里有一个关键字段——tokenclusters: - cluster: certificate-authority-data: ... server: https://apiserver-ip:6443 name: kubernetes contexts: - context: cluster: kubernetes user: tls-bootstrap-token-user name: tls-bootstrap-token-userkubernetes current-context: tls-bootstrap-token-userkubernetes users: - name: tls-bootstrap-token-user user: token: bootstrap-token这个 token 是 kubeadm join 时写入的。bootstrap token 有有效期限制默认 24 小时过期之后再用它发起认证API server 直接拒绝。如果怀疑 token 有问题可以直接在集群侧查一下这个 token 是否还存在kubectl get secrets -n kube-system | grep bootstrap-token kubectl get secret bootstrap-token-token-id -n kube-system -o yamltoken 的格式通常是token-id.token-secret在 secret 里拆成了token-id和token-secret两段存放。如果这个 secret 被删了或者和 kubeconfig 里的对不上那Unauthorized几乎是必然的。2.3 确认 API server 侧的审计日志是否开启很多节点接入问题只盯着 kubelet 日志是不够的还得去 API server 侧看看它到底拒绝在哪一步。默认情况下审计日志可能没开或者只记录了 metadata但这并不妨碍我们用最朴素的方式——直接看 API server 的进程日志。# API server 通常在 kube-system 里以静态 Pod 运行 kubectl logs -n kube-system kube-apiserver-master-node --tail200 | grep -i unauthorized如果能看到类似rbac.authorization.k8s.io字样说明请求已经过了认证阶段被卡在授权阶段如果报的是 TLS 握手错误或者 token 校验失败那就是认证阶段的问题。我自己更推荐在排错时临时开启审计日志。可以用--audit-log-path/var/log/kubernetes/audit.log加上--audit-log-maxsize参数重启 API server静态 Pod 直接改 manifest 文件即可。开启后把请求的user、verb、resource、code全流程看一遍问题定位就不是靠猜了。3. 从服务端视角定位 Unauthorized 的四种根因收集完证据把客户端和服务端的信息拼到一起基本上就能锁定问题根因。我梳理了实战中遇到最多的四种情况按出现频率排了个序。3.1 根因一bootstrap token 过期或被删除这个最常见。尤其是集群建了很久、节点在库房里吃灰了大半年才拿出来接入的场景token 早过期了。检查方法kubectl get secret -n kube-system | grep bootstrap-token没有输出或者 secret 里的expiration字段已经过了当前时间那就是它了。另外注意一种容易被忽略的情况有人在初始化集群后手动清理过 kube-system 里的 secret顺手把 bootstrap token 也删了。当时没影响但新节点加不进来查半天才发现是历史遗留问题。3.2 根因二CSR 未被批准导致注册悬空有 token 也不代表万事大吉。token 只是让你有资格申请证书最后一步还是得 CSR 审批通过。如果节点日志里反复出现 CSR 相关的等待先看集群侧kubectl get csr看到Pending状态说明没人批准。这时候可以手动批准kubectl certificate approve csr-name顺便检查一下自动批准机制是否正常。正常安装的集群通常会有下面这个 clusterrolebindingkubectl get clusterrolebinding -o wide | grep -E bootstrap|node-client|autoapprove如果这个 binding 被人动过或者后来装的插件把它覆盖了自动审批就不会生效节点永远停在 CSR 阶段。严格来说这个报错可能不是直接Unauthorized但实际现象很类似——kubelet 起不来系统提示节点加入失败。3.3 根因三证书 CN 或 O 字段不匹配节点拿到证书之后用它来执行注册节点的请求。API server 会对证书身份做校验客户端证书的CN必须是system:node:节点名格式O必须是system:nodes否则即使证书合法也会被授权机制拒绝。怎么查证书内容在节点上执行openssl x509 -in /var/lib/kubelet/pki/kubelet-client-current.pem -noout -subject -issuer如果看到 CN 不是system:node:xxx说明证书申请时的 CSR 设置有问题。常见原因包括Join 的时候节点名参数不对kubeadm join ... --node-name和实际节点 hostname 不一致。手动生成的 CSR 里 CN 写错。某些自动化脚本在证书签发后做了二次转发导致节点拿到的是别的身份的证书。3.4 根因四ClusterRoleBinding 被篡改或缺失这是最隐蔽的一种。system:node-bootstrapper对应的角色是system:node-bootstrapper而system:nodes对应的角色组有一系列 Node 相关的权限。如果集群管理员在配置 RBAC 时误操作删掉了关键的 ClusterRoleBinding或者给某些 namespace 加了奇怪的 deny 策略就会导致认证通过但授权被拒。排查时可以逐个核对kubectl get clusterrolebinding system:node-bootstrapper -o yaml kubectl get clusterrolebinding system:node -o yaml kubectl get clusterrole system:node-bootstrapper -o yaml kubectl get clusterrole system:node -o yaml拿全新集群的默认配置对比通常一眼就能看出差异。4. 修复方案的实操对比从快速恢复到底层修正根因不同修复手段也不同。下面这几种方案我按修复力从低到高排个序你可以根据自己的情况选择。4.1 方案一重新生成 bootstrap token 并重试加入适合 token 过期或被误删的场景。在集群侧任何一台 master 节点上kubeadm token create --print-join-command会输出一条新的kubeadm join命令里面带着全新的 token 和--discovery-token-ca-cert-hash。在目标节点上重新执行这条命令再重启 kubeletsystemctl restart kubelet这个方案有个注意点kubeadm join生成的 bootstrap kubeconfig 是写入/etc/kubernetes/bootstrap-kubeconfig的如果之前 join 失败遗留了文件最好先清掉再重新跑。rm -f /etc/kubernetes/bootstrap-kubeconfig再跑 join 命令避免旧文件里的过期 token 覆盖新 token。4.2 方案二手动审批 CSR 并观察节点是否完成注册如果 token 正常、错误在 CSR 挂起手动批准即可kubectl get csr kubectl certificate approve node-02批准后再看 kubelet 日志会发现它成功拿到了客户端证书随后开始正常上报节点心跳。需要注意CSR 审批通过后kubelet 还会用新证书做一次自我注册如果此时 CN 不对或者 RBAC 有问题依然会报 Unauthorized。所以要观察批准 CSR 后kubelet 日志是否变成正常的Node xxx needs to be registered或直接看到节点状态 Ready。4.3 方案三检查并修正证书身份信息对于 CN/O 不匹配的问题核实 kubelet 的启动参数。看/var/lib/kubelet/config.yaml或/etc/kubernetes/kubelet.conf中的clientConnection.kubeconfig路径确认实际使用的证书ls -l /var/lib/kubelet/pki/ openssl x509 -in /var/lib/kubelet/pki/kubelet-client-current.pem -noout -subject如果 CN 确实不是system:node:期望节点名最干脆的办法是重建该节点的加入流程在集群侧删除旧的 Node 对象和 CSR 记录。在节点侧做一次kubeadm reset清掉所有 pki 文件。用正确的--node-name重新 join。这里要特别提醒reset 前确认集群侧已经有别的节点在跑工作负载千万别在控制平面节点上乱 reset。4.4 方案四排查时钟偏差与证书轮换这个相对少见但一旦碰上很容易被忽略。如果节点和 API server 之间的系统时间偏差过大超过 5 分钟基于 TLS 的证书验证会失败kubelet 也会报出Unauthorized。检查方法timedatectl对比 master 节点时间偏差太大就同步一下。节点上可以临时用 NTP 服务或者直接systemctl start systemd-timesyncd systemctl enable systemd-timesyncd另外kubelet 客户端证书默认有效期一年证书轮换本身由RotateKubeletClientCertificate特性控制正常会自动续期。如果这个特性在节点上的 kubelet 配置里被关掉了到期后证书失效同样会报 Unauthorized。检查方式kubectl -n kube-system get cm kubelet-config -o yaml | grep feature-gates如果RotateKubeletClientCertificatefalse或者没配置建议显式开启。更稳妥的做法是直接保证集群版本和 kubeadm 初始化的版本一致避免新旧特性差异。5. 实际踩过的三个隐蔽坑与防御建议做运维久了你会发现Kubernetes 节点接入的报错往往都不是大问题而是小细节叠加。分享三个我实际踩过、也帮别人排过的小坑。5.1 坑一旧的 Node 对象未清理导致同名节点注册反复失败有一次节点系统盘损坏我直接把机器重装了系统然后重新 join。结果 kubelet 一直报 Unauthorized。排查半天发现集群里还留着旧 Node 对象旧对象绑定的证书和新节点对不上导致授权混乱。教训是节点重新加入集群前先在集群侧清理旧状态kubectl delete node 节点名 kubectl delete csr --all # 或者按 csr name 删然后再去节点上 join。这个步骤不费事但能省下大量排错时间。5.2 坑二bootstrap token 的 RBAC 绑定被其他插件覆盖某次装了一个网络插件它的安装脚本里自带了一套 RBAC 部署。装完插件后原本正常加入的新节点反而全部失败。一查原来是插件的安装脚本把system:node-bootstrapper的 ClusterRoleBinding 给覆盖了导致 bootstrap token 失去了创建 CSR 的权限。这类问题很难通过查 kubelet 日志发现必须对比 RBAC 对象的差异。防御办法是安装第三方组件之前搞清楚它的 RBAC 变更范围最好先备份一份集群 RBAC 清单kubectl get clusterrolebinding,clusterrole -o yaml /backup/rbac-before-$(date %F).yaml出问题的时候直接 diff几分钟就能定位。5.3 坑三手工写 kubeconfig 时踩中伪造身份误判也有一种情况不是真正的 Unauthorized而是 kubeconfig 里写的 user 字段和集群侧 CA 签发机制对不上。比如用户直接在节点上手工创建了一个kubelet.kubeconfig里面的 user 写成了system:master或自定义名字导致 API server 对证书链校验时报错。这种场景下最稳的做法不是手工鼓捣 kubeconfig而是回到 kubeadm 的 standard flowkubeadm join 控制平面地址:6443 \ --token token \ --discovery-token-ca-cert-hash sha256:hash \ --node-name 节点名让工具自己生成和签发少碰手工生成的凭证文件。5.4 防御建议把节点接入检查做成标准动作踩过几次之后我把节点接入失败排查整理成了标准动作清单在这里分享出来先在集群侧确认没有残留的 Node/CSR。在节点侧确认系统时间与集群一致。检查 bootstrap token secret 是否有效未过期。确认 ClusterRoleBinding 完整未被插件篡改。重新 join 前清空旧的 bootstrap kubeconfig 和 pki 目录。接入后观察kubectl get csr是否自动 Approvekubelet 日志是否出现 Ready。这六步做完绝大多数 Unauthorized 问题都能当场定位。如果还不行那就开启审计日志逐条对请求大概率能在五分钟内找到答案。6. 最后的经验之谈节点注册的 Unauthorized 报错看着就一行字实际上每一个词背后都有对应的机制token 决定你能不能进门RBAC 决定你能不能落座CSR 审批决定你能不能拿到钥匙证书身份决定钥匙能用在哪扇门上。这四个环节任何一个出错最终的报错可能都是同一个Unauthorized。这也是为什么我一直强调排错不要上来就盯着 token 改——先把整条链路在脑子里过一遍再按链路去收集证据最后动手改。越急越容易把问题改得更乱。等到好消息是一旦你把这类问题完整排过一次以后再遇到任何节点接入相关的报错都不会再慌乱。希望你用不上这些排错手段但你最好知道它们存在。