排查卡在 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 自己的 Cluster 和 Machine 基础设施 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=Running 但 NodeHealthy 条件为 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=Provisioned,KubeadmControlPlane.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 重新分配)。

另请参阅