深入理解 Karpenter NodeClaim:节点生命周期编排与容量请求的核心对象
【免费下载链接】karpenter-provider-awsKarpenter is a Kubernetes Node Autoscaler built for flexibility, performance, and simplicity.项目地址: https://gitcode.com/GitHub_Trending/ka/karpenter-provider-aws
Karpenter(AWS 版,即 karpenter-provider-aws)通过NodeClaim这一自定义资源统一管理 Kubernetes Node 与底层云厂商实例之间的生命周期映射。本文将基于官方 v1.0 文档,结合仓库源码与 CRD 定义,完整讲解 NodeClaim 的概念、节点创建六步流程、字段级 API 参考与状态条件,帮助你在排障与二次开发时快速定位问题。
NodeClaim 是什么:Kubernetes Node 与云厂商实例的"一对一契约"
Karpenter 使用 NodeClaim 来管理与底层云厂商之间的 Kubernetes 节点生命周期。Karpenter 会响应集群中 Pod 的需求,创建和删除 NodeClaim。它通过评估待调度 Pod 的需求,找到兼容的 NodePool 与 NodeClass 配对,并创建一个同时满足两者需求的 NodeClaim。
需要特别强调的是:NodeClaim 是不可变的(immutable)、由 Karpenter 管理的集群级(cluster-scoped)资源,与云厂商实例和 Kubernetes Node 一一对应。虽然不可修改,但你可以通过监控 NodeClaim 来跟踪节点的状态。
除了跟踪节点生命周期外,NodeClaim 还充当容量请求(requests for capacity)。Karpenter 在响应供应(provisioning)与干扰(disruption)需求时会预创建(pre-spin)NodeClaim。每当 Karpenter 创建 NodeClaim 时,它都会要求云厂商:
- launch:创建实例;
- registration:将创建的节点注册并与 NodeClaim 关联;
- initialization:等待节点及其资源就绪。
从仓库中的 CRD 定义(pkg/apis/crds/karpenter.sh_nodeclaims.yaml)可以看到nodeclaims.karpenter.sh的scope: Cluster,即它是集群作用域资源,且kind: NodeClaim、复数形式nodeclaims,归属于karpenter类别(categories),方便kubectl get karpenter一类的聚合查询。
每个由 Karpenter 创建的 NodeClaim 都归属于单个 NodePool(owner reference),并引用单个 NodeClass。你也可以手动创建 NodeClaim,以绕过标准供应工作流直接请求容量。
观察 NodeClaim 与 Node 的命令
如果想了解 Karpenter 管理的节点情况,可以直接查看 NodeClaim,也可以查看与之关联的 Node:
- 查看 NodeClaim:如果节点创建过程出现问题,可以通过 NodeClaim 定位创建过程在哪里失败。
kubectl get nodeclaims会显示集群中的 NodeClaim 及其关联的 Node;kubectl describe nodeclaim <nodeclaim>会显示某个 NodeClaim 的状态。例如,如果节点是 NotReady,你可能会看到表明 NodeClaim 未能 launch、register 或 initialize 的状态。Karpenter 控制器也会输出相应日志。 - 查看 Node:使用
kubectl get node和kubectl describe node <nodename>查看节点实际拥有的资源、标签和其他属性。
kubectl get nodeclaim的列信息来自 CRD 的additionalPrinterColumns定义(pkg/apis/crds/karpenter.sh_nodeclaims.yaml),其中包括 Type(实例类型)、Capacity(容量类型)、Zone(可用区)、Node(关联 Node)、Ready、Age,以及优先级为 1 的 ImageID、ID(ProviderID)、NodePool、NodeClass 和 Drifted 列。
跟随日志观察:节点创建过程中 NodeClaim 的六步角色
NodeClaim 在 Karpenter 的容量供应工作流与节点干扰(disruption)流程中扮演关键角色。下面的示意图(见文首)展示了 Karpenter 驱动的节点创建过程中,NodeClaim 与其他组件的交互方式。
注意:将
KARPENTER_NAMESPACE环境变量配置为你安装 Karpenter 的命名空间(默认为kube-system),然后跟随集群中的 Karpenter 日志进行如下操作:export KARPENTER_NAMESPACE="kube-system" kubectl logs -f -n "${KARPENTER_NAMESPACE}" \ -l app.kubernetes.io/name=karpenter在另一个终端中启动一些需要 Karpenter 创建节点来承载的 Pod,例如按照 Scale up deployment 一节启动 inflate Pod(仓库中的示例清单见 examples/workloads/inflate.yaml,它会请求
cpu: "1"、memory: 256M并支持副本扩容)。
如文首示意图所示,创建节点时 Karpenter 与 NodeClaim 及相关组件的交互分为六步:
第 1 步:监听 Pod,并监控 NodePool 与 NodeClass
- 检查 Pod 的调度约束与资源请求;
- 将需求与现有的 NodePool 和 NodeClass 交叉比对(例如 zone、arch、os)。
示例日志:
{ "level": "INFO", "time": "2024-06-22T02:24:16.114Z", "message": "found provisionable pod(s)", "commit": "490ef94", "Pods": "default/inflate-66fb68585c-xvs86, default/inflate-66fb68585c-hpcdz, default/inflate-66fb68585c-8xztf,01234567adb205c7e default/inflate-66fb68585c-t29d8, default/inflate-66fb68585c-nxflz", "duration": "100.761702ms" }第 2 步:计算 NodeClaim 的形状与大小
计算要在集群中创建以容纳第 1 步 Pod 集合的 NodeClaim(或一批 NodeClaim)的形状与大小。
示例日志:
{ "level": "INFO", "time": "2024-06-22T02:24:16.114Z", "message": "computed new nodeclaim(s) to fit pod(s)", "controller": "provisioner", "nodeclaims": 1, "pods": 5 }第 3 步:在集群中创建 NodeClaim 对象
示例日志:
{ "level": "INFO", "time": "2024-06-22T02:24:16.128Z", "message": "created nodeclaim", "controller": "provisioner", "NodePool": { "name":"default" }, "NodeClaim": { "name":"default-sfpsl" }, "requests": { "cpu":"5150m", "pods":"8" }, "instance-types": "c3.2xlarge, c4.2xlarge, c4.4xlarge, c5.2xlarge, c5.4xlarge and 55 other(s)" }可以看到 NodeClaim 名称以 NodePool 名称default为前缀自动生成,日志中还输出了该 NodeClaim 的聚合资源请求(cpu: 5150m、pods: 8)以及候选实例类型集合。
第 4 步:将 NodeClaim 翻译为创建云厂商实例的 API 调用
找到新的 NodeClaim 后,Karpenter 将其翻译成创建云厂商实例的 API 调用,并记录 API 调用的响应。
如果 API 响应是不可恢复的错误(例如Insufficient Capacity Error),Karpenter 会删除该 NodeClaim,将该实例类型标记为暂时不可用,并在必要时创建另一个 NodeClaim。
示例日志:
{ "level": "INFO", "time": "2024-06-22T02:24:19.028Z", "message": "launched nodeclaim", "controller": "nodeclaim.lifecycle", "NodeClaim": { "name": "default-sfpsl" }, "provider-id": "aws:///us-west-2b/i-01234567adb205c7e", "instance-type": "c3.2xlarge", "zone": "us-west-2b", "capacity-type": "spot", "allocatable": { "cpu": "7910m", "ephemeral-storage": "17Gi", "memory": "13215Mi", "pods": "58" } }第 5 步:等待实例注册为节点,并同步节点元数据
Karpenter 等待实例将自身注册为集群中的节点,然后更新节点的 labels、annotations、taints、owner refs 与 finalizer,使其与 NodePool 和 NodeClaim 中定义的内容一致。此步骤完成后,Karpenter 会移除 Node 上的karpenter.sh/unregisteredtaint。
如果该步骤在15 分钟内未成功,Karpenter 会从集群中移除该 NodeClaim 并删除底层实例,必要时再创建另一个 NodeClaim。
示例日志:
{ "level": "INFO", "time": "2024-06-22T02:26:19.028Z", "message": "registered nodeclaim", "controller": "nodeclaim.lifecycle", "NodeClaim": { "name": "default-sfpsl" }, "provider-id": "aws:///us-west-2b/i-01234567adb205c7e", "Node": { "name": "ip-xxx-xxx-xx-xxx.us-west-2.compute.internal" } }第 6 步:等待节点就绪、启动 taint 移除、资源注册完成
Karpenter 持续观察节点,直到节点变为 Ready、所有启动 taint 被移除、且所有请求的资源都注册到节点上。
示例日志:
{ "level": "INFO", "time": "2024-06-22T02:24:52.642Z", "message": "initialized nodeclaim", "controller": "nodeclaim.lifecycle", "NodeClaim": { "name": "default-sfpsl" }, "provider-id": "aws:///us-west-2b/i-01234567adb205c7e", "Node": { "name": "ip-xxx-xxx-xx-xxx.us-west-2.compute.internal" }, "allocatable": { "cpu": "7910m", "ephemeral-storage": "18242267924", "hugepages-2Mi": "0", "memory": "14320468Ki", "pods": "58" } }从日志中的controller字段可以看出,第 1~3 步由provisioner控制器完成,而第 4~6 步(launch / register / initialize)由nodeclaim.lifecycle控制器完成——这正是 NodeClaim 生命周期状态机在控制器层面的分工。
NodeClaim 实例解读:一次完整的kubectl describe
NodeClaim 创建后不可修改。要查看其内容,先获取 NodeClaim 名称,再运行kubectl describe:
kubectl get nodeclaim NAME TYPE ZONE NODE READY AGE default-m6pzn c7i-flex.2xlarge us-west-1a ip-xxx-xxx-xx-xxx.us-west-1.compute.internal True 7m50s kubectl describe nodeclaim default-m6pzn从下往上解读这个示例,NodeClaim 包含以下几个要点:
- Node 名称(
ip-xxx-xxx-xx-xxx.us-west-1.compute.internal)和Provider ID(aws:///us-west-1a/i-xxxxxxxxxxxxxxxxx)标识了履行该 NodeClaim 的实例; - Image ID(
ami-0ccbbed159cce4e37)代表节点上运行的操作系统镜像; - Status显示节点上可用的资源(CPU、内存等)以及与之关联的 conditions。conditions 显示节点的状态,包括节点是否已 launched、registered、initialized。当 Pod 无法部署到节点时,这对定位原因尤其有用;
- Spec包含 Karpenter 启动和管理实例所需的元数据,包括调度需求、资源需求、NodeClass 引用、taints,以及不可变的 disruption 字段(
expireAfter与terminationGracePeriod); - 附加信息包括应同步到 Node 的 annotations 与 labels、创建元数据、终止 finalizer 以及 owner reference。
以下是完整的kubectl describe输出示例:
Name: default-x9wxq Namespace: Labels: karpenter.k8s.aws/instance-category=c karpenter.k8s.aws/instance-cpu=8 karpenter.k8s.aws/instance-cpu-manufacturer=amd karpenter.k8s.aws/instance-ebs-bandwidth=3170 karpenter.k8s.aws/instance-encryption-in-transit-supported=true karpenter.k8s.aws/instance-family=c5a karpenter.k8s.aws/instance-generation=5 karpenter.k8s.aws/instance-hypervisor=nitro karpenter.k8s.aws/instance-memory=16384 karpenter.k8s.aws/instance-network-bandwidth=2500 karpenter.k8s.aws/instance-size=2xlarge karpenter.sh/capacity-type=spot karpenter.sh/nodepool=default kubernetes.io/arch=amd64 kubernetes.io/os=linux node.kubernetes.io/instance-type=c5a.2xlarge topology.k8s.aws/zone-id=usw2-az3 topology.kubernetes.io/region=us-west-2 topology.kubernetes.io/zone=us-west-2c Annotations: compatibility.karpenter.k8s.aws/cluster-name-tagged: true compatibility.karpenter.k8s.aws/kubelet-drift-hash: 15379597991425564585 karpenter.k8s.aws/ec2nodeclass-hash: 5763643673275251833 karpenter.k8s.aws/ec2nodeclass-hash-version: v3 karpenter.k8s.aws/tagged: true karpenter.sh/nodepool-hash: 377058807571762610 karpenter.sh/nodepool-hash-version: v3 API Version: karpenter.sh/v1 Kind: NodeClaim Metadata: Creation Timestamp: 2024-08-07T05:37:30Z Finalizers: karpenter.sh/termination Generate Name: default- Generation: 1 Owner References: API Version: karpenter.sh/v1 Block Owner Deletion: true Kind: NodePool Name: default UID: 6b9c6781-ac05-4a4c-ad6a-7551a07b2ce7 Resource Version: 19600526 UID: 98a2ba32-232d-45c4-b7c0-b183cfb13d93 Spec: Expire After: 720h0m0s Node Class Ref: Group: Kind: EC2NodeClass Name: default Requirements: Key: kubernetes.io/arch Operator: In Values: amd64 Key: kubernetes.io/os Operator: In Values: linux Key: karpenter.sh/capacity-type Operator: In Values: spot Key: karpenter.k8s.aws/instance-category Operator: In Values: c m r Key: karpenter.k8s.aws/instance-generation Operator: Gt Values: 2 Key: karpenter.sh/nodepool Operator: In Values: default Key: node.kubernetes.io/instance-type Operator: In Values: c3.xlarge c4.xlarge c5.2xlarge c5.xlarge c5a.xlarge c5ad.2xlarge c5ad.xlarge c5d.2xlarge Resources: Requests: Cpu: 3150m Pods: 6 Startup Taints: Effect: NoSchedule Key: app.dev/example-startup Taints: Effect: NoSchedule Key: app.dev/example Termination Grace Period: 1h0m0s Status: Allocatable: Cpu: 7910m Ephemeral - Storage: 17Gi Memory: 14162Mi Pods: 58 vpc.amazonaws.com/pod-eni: 38 Capacity: Cpu: 8 Ephemeral - Storage: 20Gi Memory: 15155Mi Pods: 58 vpc.amazonaws.com/pod-eni: 38 Conditions: Last Transition Time: 2024-08-07T05:38:08Z Message: Reason: Consolidatable Status: True Type: Consolidatable Last Transition Time: 2024-08-07T05:38:07Z Message: Reason: Initialized Status: True Type: Initialized Last Transition Time: 2024-08-07T05:37:33Z Message: Reason: Launched Status: True Type: Launched Last Transition Time: 2024-08-07T05:38:07Z Message: Reason: Ready Status: True Type: Ready Last Transition Time: 2024-08-07T05:37:55Z Message: Reason: Registered Status: True Type: Registered Image ID: ami-08946d4d49fc3f27b Node Name: ip-xxx-xxx-xxx-xxx.us-west-2.compute.internal Provider ID: aws:///us-west-2c/i-01234567890123 Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal Launched 70s karpenter Status condition transitioned, Type: Launched, Status: Unknown -> True, Reason: Launched Normal DisruptionBlocked 70s karpenter Cannot disrupt NodeClaim: state node doesn't contain both a node and a nodeclaim Normal Registered 48s karpenter Status condition transitioned, Type: Registered, Status: Unknown -> True, Reason: Registered Normal Initialized 36s karpenter Status condition transitioned, Type: Initialized, Status: Unknown -> True, Reason: Initialized Normal Ready 36s karpenter Status condition transitioned, Type: Ready, Status: Unknown -> True, Reason: Ready值得注意的细节:
- Labels中的
karpenter.k8s.aws/instance-*系列标签由 AWS Provider 在调度时解析注入(实例族、CPU、内存、带宽、虚拟化类型等),而karpenter.sh/capacity-type=spot、karpenter.sh/nodepool=default则是 Karpenter 的通用标签; - Annotations中的
karpenter.sh/nodepool-hash、karpenter.k8s.aws/ec2nodeclass-hash用于 NodePool / EC2NodeClass 的 hash 版本追踪(-hash-version: v3),当 NodePool 或 EC2NodeClass 的配置变化导致 hash 不匹配时,NodeClaim 会发生 drift(漂移); - Events中的
DisruptionBlocked说明干扰控制器曾尝试对该 NodeClaim 执行操作,但因节点状态尚未同时包含 node 与 nodeclaim 而被阻止——这是注册过程中的正常现象。
NodeClaim API 参考:字段级完整注释
以下是带注释的 NodeClaim 资源示例(yaml 形式):
apiVersion: karpenter.sh/v1 kind: NodeClaim metadata: # NodeClaim names are auto-generated by Karpenter using the NodePool name as a prefix name: default-sfpsl # Labels are propagated from the NodePool's template and include well-known labels # resolved during scheduling (instance type, capacity type, zone, etc.) labels: billing-team: my-team karpenter.sh/capacity-type: spot karpenter.sh/nodepool: default node.kubernetes.io/instance-type: c5.2xlarge topology.kubernetes.io/zone: us-west-2b kubernetes.io/arch: amd64 kubernetes.io/os: linux # Annotations are propagated from the NodePool's template annotations: example.com/owner: "my-team" # The NodeClaim is owned by the NodePool that created it ownerReferences: - apiVersion: karpenter.sh/v1 kind: NodePool name: default blockOwnerDeletion: true # Karpenter adds a termination finalizer to ensure proper cleanup finalizers: - karpenter.sh/termination spec: # References the Cloud Provider's NodeClass resource nodeClassRef: group: karpenter.k8s.aws kind: EC2NodeClass name: default # Taints applied to the node, propagated from the NodePool template taints: - key: example.com/special-taint effect: NoSchedule # Startup taints applied to the node on creation, expected to be removed by an external system startupTaints: - key: example.com/another-taint effect: NoSchedule # The amount of time a Node can live on the cluster before being removed # Inherited from the NodePool's spec.template.spec.expireAfter expireAfter: 720h # The maximum duration the controller will wait before forcefully deleting pods on the node # Inherited from the NodePool's spec.template.spec.terminationGracePeriod terminationGracePeriod: 48h # Requirements resolved during scheduling, combining NodePool requirements with pod constraints # These are the final, resolved requirements that were used to launch the instance requirements: - key: "kubernetes.io/arch" operator: In values: ["amd64"] - key: "kubernetes.io/os" operator: In values: ["linux"] - key: "karpenter.sh/capacity-type" operator: In values: ["spot"] - key: "karpenter.k8s.aws/instance-category" operator: In values: ["c", "m", "r"] - key: "karpenter.k8s.aws/instance-generation" operator: Gt values: ["2"] - key: "node.kubernetes.io/instance-type" operator: In values: ["c3.2xlarge", "c4.2xlarge", "c5.2xlarge", "c5.4xlarge", "m5.2xlarge"] - key: "topology.kubernetes.io/zone" operator: In values: ["us-west-2a", "us-west-2b"] # Resource requests represent the minimum resources needed to schedule the pods # that triggered this NodeClaim resources: requests: cpu: "5150m" pods: "8" status: # The name of the Kubernetes Node object linked to this NodeClaim nodeName: ip-xxx-xxx-xx-xxx.us-west-2.compute.internal # The cloud provider identifier for the instance providerID: "aws:///us-west-2b/i-01234567adb205c7e" # The image (AMI) running on the instance imageID: ami-0ccbbed159cce4e37 # Full capacity of the node capacity: cpu: "8" memory: "15155Mi" ephemeral-storage: "20Gi" pods: "58" # Allocatable resources available for pod scheduling allocatable: cpu: "7910m" memory: "14162Mi" ephemeral-storage: "17Gi" pods: "58" # Conditions track the lifecycle state of the NodeClaim conditions: - type: Launched status: "True" reason: Launched - type: Registered status: "True" reason: Registered - type: Initialized status: "True" reason: Initialized - type: Ready status: "True" reason: Ready - type: Consolidatable status: "True" reason: Consolidatable下面逐字段说明。
metadata.labels
NodeClaim 上的 Labels 来自两个来源:NodePool 的spec.template.metadata.labels中定义的标签,以及调度期间解析出的 well-known 标签(如node.kubernetes.io/instance-type、topology.kubernetes.io/zone、karpenter.sh/capacity-type、karpenter.sh/nodepool)。这些标签会被同步到对应的 Kubernetes Node 上。
注意:目前 NodePool 与 NodeClaim 上的 requirements 总数存在100 个的上限。NodePool 中
spec.template.metadata.labels定义的标签会作为 requirements 传播到 NodeClaim,这意味着你的 NodePool 上设置的 requirements 与 labels 合计不能超过 100 个。
metadata.annotations
NodeClaim 上的 Annotations 从 NodePool 的spec.template.metadata.annotations传播而来。Karpenter 还会添加用于追踪 hash 版本和云厂商特定元数据的内部 annotations(如前面示例中的karpenter.sh/nodepool-hash、karpenter.k8s.aws/ec2nodeclass-hash)。这些 annotations 会被同步到对应的 Kubernetes Node 上。
metadata.ownerReferences
每个由 Karpenter 创建的 NodeClaim 都有一个指向创建它的 NodePool 的 owner reference,且blockOwnerDeletion: true。这意味着删除 NodePool 会级联删除其所有 NodeClaim。
metadata.finalizers
Karpenter 会给每个 NodeClaim 添加karpenter.sh/terminationfinalizer。该 finalizer 确保 NodeClaim 被删除时,Karpenter 能正确地 cordon 节点、排空 Pod、终止云厂商实例并清理 Node 对象,然后才移除 finalizer。
spec.nodeClassRef
该字段引用云厂商的 NodeClass 资源,该资源定义了实例的云厂商特定配置。详见 EC2NodeClasses。
引用包含三个部分:
group:NodeClass 的 API 组(如karpenter.k8s.aws);kind:NodeClass 的类型(如EC2NodeClass);name:NodeClass 资源的名称。
spec.taints
应用到已供应节点上的 taints,继承自 NodePool 的spec.template.spec.taints。不容忍这些 taints 的 Pod 不会被调度到该节点上。关于 taint 与 toleration 的机制,可参考 Kubernetes 官方的 Taints and Tolerations 文档。
spec.startupTaints
启动 taints 在节点创建时被应用,但预期由外部系统(例如 DaemonSet)在初始化完成后移除,继承自 NodePool 的spec.template.spec.startupTaints。Pod 不需要容忍启动 taints 才能被纳入供应考虑——Karpenter 假定它们是临时的。
spec.expireAfter
节点在被 Karpenter 删除前可以存活于集群中的时长,继承自 NodePool 的spec.template.spec.expireAfter。一旦达到过期时间,节点开始排空(draining)。默认值为720h(30 天),设置为Never可禁用过期。
在 NodePool 上修改expireAfter会导致现有 NodeClaim 发生 drift。
spec.terminationGracePeriod
Karpenter 在排空节点时,在强制删除 Pod 之前愿意等待的最大时长,继承自 NodePool 的spec.template.spec.terminationGracePeriod。一旦达到该时长,Karpenter 将无视 PodDisruptionBudgets 或karpenter.sh/do-not-disrupt注解强制删除 Pod。
Karpenter 会提前删除 Pod,使其terminationGracePeriodSeconds与节点的terminationGracePeriod对齐。如果未定义,控制器将无限期等待 Pod 排空。
在 NodePool 上修改terminationGracePeriod会导致现有 NodeClaim 发生 drift。
spec.requirements
NodeClaim 的已解析调度需求。它结合了 NodePool 的spec.template.spec.requirements与触发供应的 Pod 的调度约束(nodeSelector、nodeAffinity 等)。支持与 NodePool 相同的操作符:In、NotIn、Exists、DoesNotExist、Gt和Lt。
这些 requirements 表示用于选择实例类型并启动节点的最终约束,包含node.kubernetes.io/instance-type、topology.kubernetes.io/zone、kubernetes.io/arch、karpenter.sh/capacity-type等 well-known 标签。
spec.resources
资源请求代表调度触发该 NodeClaim 的 Pod 所需的最低资源。Karpenter 使用这些值来正确选择实例大小(right-size)。
spec.resources.requests:将要调度到该 NodeClaim 上的 Pod 的聚合资源请求(如cpu、memory、pods)。
status.nodeName
与该 NodeClaim 关联的 Kubernetes Node 对象的名称。实例注册到集群后填充。
status.providerID
实例的云厂商标识符(如aws:///us-west-2b/i-01234567adb205c7e)。实例启动后填充。
status.imageID
实例上运行的镜像标识符(例如 AMI ID,如ami-0ccbbed159cce4e37)。
status.capacity
节点的预估完整容量,包括系统保留的资源。包含cpu、memory、ephemeral-storage、pods以及任何扩展资源(例如vpc.amazonaws.com/pod-eni)。
status.allocatable
节点的预估可分配容量——扣除系统保留后实际可用于 Pod 调度的资源。通常小于status.capacity。
status.conditions
Conditions 跟踪 NodeClaim 的生命周期状态。每个 condition 都有type、status(True、False或Unknown)、reason、message和lastTransitionTime。
| Condition Type | Description |
|---|---|
| Launched | 云厂商实例已成功创建 |
| Registered | 实例已作为 Kubernetes Node 加入集群,且 Karpenter 已同步 labels、taints 与 owner refs |
| Initialized | 节点已就绪,启动 taints 已移除,所有请求的资源已注册 |
| Ready | 顶层条件,表示 NodeClaim 完全可用。仅当 Launched、Registered 与 Initialized 均为 True 时为 True |
| Consolidatable | 该节点是合并(consolidation)的候选者(空节点或利用率不足) |
| Drifted | NodeClaim 已偏离其期望规格(例如 NodePool 或 NodeClass 发生变化) |
| InstanceTerminating | 云厂商实例正在被终止 |
| ConsistentStateFound | Karpenter 已验证实例状态与 NodeClaim 一致 |
这些条件类型同样体现在 CRD 的 printer columns 中(如 Ready、Drifted 列),使kubectl get nodeclaim的输出能够一目了然地反映生命周期状态。
结合源码:NodeClaim 与 AWS Provider 的实现印证
从仓库源码可以进一步印证 NodeClaim 的设计与实现:
- CRD 定义:pkg/apis/crds/karpenter.sh_nodeclaims.yaml 是 NodeClaim 的权威 schema,定义了上述全部字段、校验规则与 printer columns。它是 Helm chart 安装时通过 charts/karpenter/crds/karpenter.sh_nodeclaims.yaml 下发到集群的。
- NodeClass 引用:NodeClaim 的
spec.nodeClassRef在 AWS Provider 中对应EC2NodeClass,其 Spec 定义于 pkg/apis/v1/ec2nodeclass.go,包含子网/安全组/AMI 选择器、实例角色、用户数据等字段——这些正是 Karpenter 在 launch 阶段构建实例请求时读取的配置。 - 生命周期控制器:从日志中的
controller: nodeclaim.lifecycle可以推断,launch/register/initialize 状态机由 NodeClaim 生命周期控制器驱动;供应侧的controller: provisioner则负责将 Pod 需求转换为 NodeClaim 规格。这两个控制器共同实现了前文描述的六步流程。 - 标签来源:示例 NodeClaim 中的
karpenter.k8s.aws/instance-*标签由 AWS Provider 的实例类型信息解析而来,与 pkg/providers/instancetype 目录下的实例类型数据(带宽、vCPU、内存等)相对应。 - 手动创建 NodeClaim:NodeClaim 支持手动创建以在标准供应流程之外请求容量,只需提供
spec.nodeClassRef与必要的 requirements 即可,Karpenter 会像处理自动创建的 NodeClaim 一样完成 launch、register 与 initialize。
常见问题排查速查
- 节点 NotReady:运行
kubectl describe nodeclaim <name>查看 conditions。若Launched为 False,说明实例创建失败;若Registered为 False,说明实例未能注册为节点或 Karpenter 未能同步元数据;若Initialized为 False,说明节点尚未就绪或资源未完全注册。同时可以配合kubectl logs -f -n "${KARPENTER_NAMESPACE}" -l app.kubernetes.io/name=karpenter查看控制器日志。 - 容量不足(Insufficient Capacity):Karpenter 会自动删除受影响的 NodeClaim、将对应实例类型标记为暂时不可用,并尝试创建新的 NodeClaim(见第 4 步说明)。
- 注册超时:实例在 15 分钟内未完成注册,Karpenter 会删除 NodeClaim 与底层实例并重试(见第 5 步说明)。
- NodeClaim 意外被删除:检查 NodePool 是否被删除(owner reference 级联删除),或 NodePool 的
expireAfter/terminationGracePeriod是否被修改(会导致 drift)。 - Pod 无法调度到节点:检查 NodeClaim 的
spec.taints与 Pod 的 tolerations 是否匹配,以及spec.requirements中是否包含了不满足 Pod 约束的条件。
结语
NodeClaim 是理解 Karpenter 节点编排的钥匙:它既是"容量请求",也是"生命周期状态机"。掌握其不可变语义、六步创建流程、字段级含义与条件状态机,无论是排查节点创建失败、理解 consolidation 与 drift 行为,还是进行手动容量请求,都能事半功倍。相关概念的延伸阅读可继续查看 NodePools(NodeClaim 的规格来源)与 NodeClasses(云厂商配置来源)。
【免费下载链接】karpenter-provider-awsKarpenter is a Kubernetes Node Autoscaler built for flexibility, performance, and simplicity.项目地址: https://gitcode.com/GitHub_Trending/ka/karpenter-provider-aws
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考