本文永久链接: https://www.xtplayer.cn/rancher/either-cluster-is-not-ready-for-registering/

问题背景

某天一客户突然反馈 rancher local 集群节点 32G 内存资源耗尽,local 集群中只运行了 rancher 相关业务。

经过一系列排查,发现耗尽内存的是 k8s apiserver 进程。在 apiserver 容器日志中发现了大量以下日志:

{"log":"I0507 08:24:37.492998       1 pathrecorder.go:253] kube-apiserver: \"/apis/management.cattle.io/v3/namespaces/c-dql6k/nodes/machine-h46dc\" satisfied by NotFoundHandler\n","stream":"stderr","time":"2022-05-07T08:24:37.495887119Z"}
{"log":"I0507 08:24:37.493004 1 handler.go:153] apiextensions-apiserver: PUT \"/apis/management.cattle.io/v3/namespaces/c-dql6k/nodes/machine-h46dc\" satisfied by nonGoRestful\n","stream":"stderr","time":"2022-05-07T08:24:37.495891177Z"}
{"log":"I0507 08:24:37.493010 1 pathrecorder.go:247] apiextensions-apiserver: \"/apis/management.cattle.io/v3/namespaces/c-dql6k/nodes/machine-h46dc\" satisfied by prefix /apis/\n","stream":"stderr","time":"2022-05-07T08:24:37.495894009Z"}
{"log":"I0507 08:24:37.493103 1 handler.go:153] kube-aggregator: PUT \"/apis/management.cattle.io/v3/namespaces/c-dql6k/nodes/machine-h46dc\" satisfied by nonGoRestful\n","stream":"stderr","time":"2022-05-07T08:24:37.495896746Z"}
{"log":"I0507 08:24:37.493113 1 pathrecorder.go:247] kube-aggregator: \"/apis/management.cattle.io/v3/namespaces/c-dql6k/nodes/machine-h46dc\" satisfied by prefix /apis/management.cattle.io/v3/\n","stream":"stderr","time":"2022-05-07T08:24:37.495899592Z"}
{"log":"I0507 08:24:37.493121 1 handler.go:153] kube-apiserver: PUT \"/apis/management.cattle.io/v3/namespaces/c-dql6k/nodes/machine-h46dc\" satisfied by nonGoRestful\n","stream":"stderr","time":"2022-05-07T08:24:37.495902397Z"}

问题排查

rancher 中保存的数据,均是以 CRD(CustomResourceDefinition)(https://kubernetes.io/zh/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/)资源的形式存储在 K8S 中,rancher 会通过 kubernetes svc 调用 local k8s apiserver 来读写 CRD 资源。

management.cattle.io 是 rancher 存储数据的 API Group,所有与 rancher 相关的 CRD 资源均在这个 API Group中。

在 rancher 中有一个叫做 nodes.management.cattle.io 的 CRD 资源,这个 CRD 资源用于保存所有集群的 node 配置信息。比如在 local 集群中执行 kubectl get nodes.management.cattle.io -A 可以看到以 CLUSTER-ID 为 NAMESPACE 的 node 列表。

查看 local apiserver 的日志,发现频繁的刷新着 /apis/management.cattle.io/v3/namespaces/c-dql6k/nodes/machine-h46dc 相关字样的日志信息,通过这些信息可以初步判断集群 c-dql6k 的 machine-h46dc 节点可能存在异常。在 rancher 架构中,每个业务集群的节点上均运行着一个 node-agent pod ,这个 pod 主要负责 node 注册以及节点配置更新,如果 rancher 频繁的读写 nodes.management.cattle.io CRD,那么也说明了对应节点的 node-agent pod 很有可能运行异常。

根据 local apiserver 的日志知道了 cluster-id 和 node-id, 通过在 local 集群中执行以下命令快速定位集群名称以及节点 IP。

kubectl get clusters.management.cattle.io <cluster-id> -ojson|jq .status.appliedSpec.displayName
kubectl get nodes.management.cattle.io -n <cluster-id> <node-id> -ojson|jq .status.internalNodeStatus.addresses

通过以上命令快速定位到具体集群和具体的 node 名称,切换到 system 项目下,点击 cattle-system 命名空间下的 cattle-node-agent 服务,根据查询到的 node ip 可以快速的定位到具体的 node-agent pod 名称。

查询 node-agent pod 日志

根据以上操作定位到具体集群以及具体 node agent pod 后,通过 rancher ui 查看 node agent pod 日志。

问题分析

Rancher 中有一个 nodessyncer controller,它会根据下游集群的 node 情况,把 K8S node 同步到 local 的nodes.management.cattle.io CRD中。

  • 如果是 import 集群,因为 node 已存在,那么在 nodes.management.cattle.io CRD中将会以 machine- 命名方式创建 nodes.management.cattle.io 资源。
  • 如果自定义集群,正常自定义创建集群时,在 local 集群的 c-xxx ns中创建了 m-xxx node,此时nodessyncer会判断已经存在了这个资源,就不会创建 machine- node。

基于以上逻辑,出现 node-agent 报以上错误有以下两种可能:

  1. 在 rancher ui 删除节点时,可能某些原因导致 rancher nodes.management.cattle.io CRD中的 node 资源被删除了,但是下游 k8s 集群却未能正常的把节点从 k8s 中剔除。当再次通过 rancher ui 复制添加节点的命令执行后,因为这个时候 node 已经存在 k8s 集群中,因此 node-agent 不会再次注册,所以抛出了错误日志。
  2. 在清理下游集群节点时,如果先删除的 local m-xxx node,nodessyncer 可能瞬间同步了下游的 native node 并创建machine-xxx node。因为当时下游集群的native node在这个时间窗口,还没有被清理,从程序逻辑执行角度,会出现这种情况。

解决方法

  1. 如果集群运行踢出节点,那么最简单的方法就是把异常的节点从集群中中踢出,执行 kubectl get node 命令确认节点已经删除后,再把节点初始化之后重新添加到集群中。

  2. 如果节点不能踢出,那么只能通过手动更新配置了。通过执行 kubectl get nodes.management.cattle.io -n <cluster-id> <node-id> -oyaml 导出一个正常节点和异常节点的 YAML ,然后进行对比。

可以发现主要的差异在 status.rkeNodespec.customConfig

rkeNode:
address: <当前节点 ip>
hostnameOverride: < spec.requestedHostname 的值 >
nodeName: <cluster_id>:<node_id>
port: "22"
role:
- worker
user: root

spec.customConfig 配置是做的自定义配置,根据实际清楚修改。

最后执行 kubectl edit nodes.management.cattle.io -n <cluster-id> <node-id> -oyaml ,将以上配置更新即可。