排查卡在 Provisioned 状态的业务集群

当 Immutable Infrastructure 上的业务集群已达到 Cluster.status.phase = Provisioned,并且 Kubernetes 控制平面报告为就绪,但集群始终无法完全可用时,请使用本指南。

此诊断流程假定业务集群是通过将 provider Cluster API manifests 应用到 global 集群而创建的。

范围

本指南中的诊断流程针对所有 Immutable Infrastructure provider 共享的 global 集群导入控制器。在 健康导入集群的不变量 中的四项不变量——clusters.platform.tkestack.io 的存在、capi.cpaas.io/imported 标签、ClusterCredential 数量以及业务集群 sentry ServiceAccount——适用于每个 provider。

本指南中的日志字符串使用 reconciler 对象名称占位符,因为每个基础设施 provider 都使用自己的对象类型(例如,provider 自己的 ClusterMachine 基础设施 CRD)。阅读 provider 的日志时,请将占位符替换为 provider reconciler 输出中实际出现的对象名称。诊断模式本身不依赖于 provider,依赖的只是日志字符串中的对象名称。

如果你的环境是 Huawei Cloud Stack,并且下面的症状还包括某个节点上的 kubeadm init 卡住,而该节点的 pool config 带有带点的 hostname,请在完成本页的通用诊断流程后,参阅 排查 HCS 业务集群问题,了解 provider 特定的模式。

症状

集群会进入部分成功状态并停留在该状态。会同时出现以下指示:

查看位置你看到的内容
Cluster.status.phase(在 global 集群上)Provisioned
KubeadmControlPlane.status.readytrue
Machine.status.phaseRunning
Machine.status.conditions[?(@.type=="NodeHealthy")]status: False, reason: NodeConditionsFailed, message: Node condition Ready is False
MachineDeployment.status.conditions[?(@.type=="Available")]status: False, reason: NotAvailable, message: WorkersAvailable: 0 available replicas
在业务集群内:kubectl get nodes一个或多个节点报告 STATUS=NotReady,因为 CNI 未运行

global 集群上的基础设施 provider reconciler 日志中,通常还会出现一个次要特征:

ERROR  failed to reconcile <CNI-AppRelease-object>: ServiceAccount "sentry" not found
ERROR  failed to reconcile <Infra-Cluster-object>: ServiceAccount "sentry" not found

<CNI-AppRelease-object><Infra-Cluster-object> 是占位符,表示你的 provider 使用的实际对象类型。例如,CNI AppRelease 对象通常会以正在部署的 CNI 名称命名,而基础设施 cluster 对象名称则与你的 provider 的 CRD 相匹配(即 provider 自己的 *Cluster CRD)。

reconciler 会尝试通过 AppRelease 资源来应用 CNI,而该资源由业务集群自身的 sentry ServiceAccount 执行。如果该 ServiceAccount 不存在,则 CNI 永远不会被部署,业务集群节点也会一直保持 NotReady

健康导入集群的不变量

一旦业务集群达到健康的稳定状态,以下所有条件都必须成立。除非另有说明,否则请在 global 集群上下文中验证每一项不变量。

  1. 存在一个 clusters.platform.tkestack.io/<cluster-name> 对象。

    kubectl get clusters.platform.tkestack.io <cluster-name>
  2. Cluster API Cluster 资源带有标签 capi.cpaas.io/imported: ""

    在健康导入状态下,该标签值为空字符串 "",因此仅使用 jsonpath 查询无法区分“标签缺失”和“标签值为空”。请使用 jq 显式检查标签是否存在:

    kubectl get cluster <cluster-name> -n <namespace> -o json | \
      jq -r 'if (.metadata.labels | has("capi.cpaas.io/imported"))
             then "present (value=\"\(.metadata.labels["capi.cpaas.io/imported"])\")"
             else "missing"
             end'

    期望输出:present (value="")missing 表示导入尚未完成。

  3. 仅有一个带有该集群标签的 clustercredentials.platform.tkestack.io 对象。

    kubectl get clustercredentials.platform.tkestack.io -o json | \
      jq -r --arg c <cluster-name> \
        '.items[] | select(.metadata.labels["cluster.x-k8s.io/cluster-name"]==$c) | .metadata.name'

    应当仅打印出一个名称。

  4. 在业务集群内,cpaas-system 中存在 ServiceAccount sentry

    kubectl --kubeconfig <workload-kubeconfig> -n cpaas-system get serviceaccount sentry

如果任一不变量失败,则说明该业务集群尚未被 global 集群的导入控制器导入,上述症状模式就是预期表现。

诊断步骤

步骤 1 — 确认 Cluster API 表面正常

kubectl get cluster <cluster-name> -n <namespace> -o wide
kubectl get kubeadmcontrolplane -n <namespace>
kubectl get machinedeployment -n <namespace>

如果此处失败,则与导入模式无关;请直接检查基础设施 provider。

步骤 2 — 检查 Machine 和 Node 状态

kubectl get machine -n <namespace> \
  -l cluster.x-k8s.io/cluster-name=<cluster-name> \
  -o jsonpath='{range .items[*]}{.metadata.name}{" phase="}{.status.phase}{" "}{range .status.conditions[*]}{.type}={.status} {end}{"\n"}{end}'

如果 phase=RunningNodeHealthy 条件为 False,则底层业务节点处于 NotReady。继续执行步骤 3。

步骤 3 — 检查基础设施 provider reconciler 日志

kubectl logs -n cpaas-system <infra-provider-manager-pod> --tail=200 | \
  grep -E 'ServiceAccount|sentry|<cluster-name>'

如果你看到反复出现的 ServiceAccount "sentry" not found 错误,并且同时伴随对 CNI AppRelease 对象的调谐失败,则说明业务集群的 CNI 没有被部署,因为导入流程尚未完成。继续执行步骤 4。

步骤 4 — 验证 global 集群是否已导入该业务集群

运行上一节中的不变量检查。导入失败时的组合诊断特征为:

  • clusters.platform.tkestack.io/<cluster-name> 返回 NotFound
  • Cluster.metadata.labels 不包含 capi.cpaas.io/imported

步骤 5 — 验证 ClusterCredential 数量不变量

kubectl get clustercredentials.platform.tkestack.io -o json | \
  jq -r --arg c <cluster-name> \
    '[.items[] | select(.metadata.labels["cluster.x-k8s.io/cluster-name"]==$c)] | length'

健康集群会打印 1。如果值大于 1(例如 8),则表示导入流程曾多次尝试并失败,留下了孤立的凭证。

表明导入失败的模式

当以下所有条件同时成立时,诊断结果表明导入失败:

  • Cluster API 表面正常(Cluster.status.phase=ProvisionedKubeadmControlPlane.status.ready=true)。
  • Machine.condition.NodeHealthy=False,并且至少有一个业务节点处于 NotReady
  • 基础设施 provider 日志反复报告 ServiceAccount "sentry" not found
  • clusters.platform.tkestack.io/<cluster-name> 不存在。
  • Cluster 资源不带有 capi.cpaas.io/imported 标签。
  • 为该集群标记了多个 clustercredentials.platform.tkestack.io 对象。

当该模式成立时,说明业务集群在 global 集群上的初始导入失败,并且导入控制器当前不会从头重试。

这种模式无法解决的问题

WARNING

global 集群上重启 cluster-transformer pod,不会重新导入一个已按照此模式初次导入失败的业务集群。在手动重启 pod 之后,不会有日志条目引用受影响的集群,而且缺失的 clusters.platform.tkestack.io 条目也不会被创建。

如果同一个集群存在多个孤立的 ClusterCredential,不要认为控制器最终会自行恢复。

下一步

请向平台支持提供以下信息:

  • 集群名称和命名空间。
  • 步骤 1 到步骤 5 中诊断命令的输出。
  • 与受影响集群具有相同 cluster.x-k8s.io/cluster-name 标签的每个 clustercredentials.platform.tkestack.io 对象的名称和创建时间戳。

当前的恢复方式是在清理孤立的 ClusterCredential 条目后,删除并重新创建业务集群。不要在没有平台支持指导的情况下在生产集群上执行此操作:重新创建路径会触发 IaaS 平台上底层虚拟机的删除,并且在应用重新创建之前,必须先审查 provider 特定细节(例如,DCS IP-pool 和 hostname 规划,或 HCS HCSMachineConfigPool 重新分配)。

另请参阅