Karpenter NodePool 完全指南:从节点模板、调度约束到中断与资源限额
【免费下载链接】karpenter-provider-awsKarpenter is a Kubernetes Node Autoscaler built for flexibility, performance, and simplicity.项目地址: https://gitcode.com/GitHub_Trending/ka/karpenter-provider-aws
导读
NodePool 是 Karpenter 集群节点自动扩缩容的核心配置对象:它定义了 Karpenter 可以创建哪些节点(实例类型、可用区、架构、容量类型等)以及哪些 Pod 可以运行在这些节点上。本文基于 karpenter-provider-aws 仓库 v1.0 版本文档,系统讲解 NodePool 的完整配置体系,包括节点模板(template)、调度需求(requirements)、中断策略(disruption)、资源限额(limits)与权重(weight),并结合仓库内 CRD 定义与示例文件给出可复制、可运行的实战配置。
什么是 NodePool
安装 Karpenter 时,你会同时创建(或随 Helm Chart 一并部署)一个默认的 NodePool。NodePool 用来对 Karpenter 可以创建的节点,以及可以运行在这些节点上的 Pod 设置约束。一个 NodePool 可以做的事包括:
- 通过taints限制可以调度到 Karpenter 所建节点上的 Pod;
- 通过startupTaints告知 Karpenter 在节点启动时打上临时污点(该污点预期会被其他系统移除);
- 将节点创建限制在特定可用区、实例类型、CPU 架构范围内;
- 为节点设置过期时间(expiration)等默认值。
你可以修改现有 NodePool,也可以为 Karpenter 添加更多 NodePool。关于 NodePool,需要理解以下核心工作机制:
- Karpenter 只有在至少配置了一个 NodePool 时才会工作;没有任何 NodePool,Karpenter 不会做任何事。
- 每个已配置的 NodePool 都会被 Karpenter循环遍历,以寻找能满足待调度 Pod 的候选池。
- 如果 NodePool 中存在某个taint 不被 Pod 容忍,Karpenter 就不会用该 NodePool 为这个 Pod 供给节点。
- 如果 NodePool 配置了startupTaints,这些污点会被打到新建节点上,但Pod 无需容忍这些污点——Karpenter 假设污点是临时的,会由其他系统(通常是 DaemonSet)移除。
- 推荐创建互斥的 NodePool,即任意 Pod 不应同时匹配多个 NodePool;若多个 NodePool 均匹配,Karpenter 会使用 weight 最高的那个。
另外需要特别说明:设置 Kubelet 特性的对象已从 NodePool spec 迁移到 EC2NodeClass spec,这样做的目的是不强制其他 Karpenter 云提供商实现这些特性。也就是说,maxPods、podsPerCore、systemReserved等 kubelet 参数统一在EC2NodeClass.spec.kubelet中配置(见 EC2NodeClasses 文档),而 NodePool 只负责跨云提供商的通用调度语义。
一个完整的 NodePool 配置示例
下面是一个完整的 NodePool 定义(对应仓库 v1.0 文档中的示例),后续小节将逐字段拆解:
apiVersion: karpenter.sh/v1 kind: NodePool metadata: name: default spec: # Template section that describes how to template out NodeClaim resources that Karpenter will provision # Karpenter will consider this template to be the minimum requirements needed to provision a Node using this NodePool # It will overlay this NodePool with Pods that need to schedule to further constrain the NodeClaims # Karpenter will provision to launch new Nodes for the cluster template: metadata: # Labels are arbitrary key-values that are applied to all nodes labels: billing-team: my-team # Annotations are arbitrary key-values that are applied to all nodes annotations: example.com/owner: "my-team" spec: # References the Cloud Provider's NodeClass resource, see your cloud provider specific documentation nodeClassRef: group: karpenter.k8s.aws # Updated since only a single version will be served kind: EC2NodeClass name: default # Provisioned nodes will have these taints # Taints may prevent pods from scheduling if they are not tolerated by the pod. taints: - key: example.com/special-taint effect: NoSchedule # Provisioned nodes will have these taints, but pods do not need to tolerate these taints to be provisioned by this # NodePool. These taints are expected to be temporary and some other entity (e.g. a DaemonSet) is responsible for # removing the taint after it has finished initializing the node. startupTaints: - key: example.com/another-taint effect: NoSchedule # The amount of time a Node can live on the cluster before being removed # Avoiding long-running Nodes helps to reduce security vulnerabilities as well as to reduce the chance of issues that can plague Nodes with long uptimes such as file fragmentation or memory leaks from system processes # You can choose to disable expiration entirely by setting the string value 'Never' here # Note: changing this value in the nodepool will drift the nodeclaims. expireAfter: 720h | Never # The amount of time that a node can be draining before it's forcibly deleted. A node begins draining when a delete call is made against it, starting # its finalization flow. Pods with TerminationGracePeriodSeconds will be deleted preemptively before this terminationGracePeriod ends to give as much time to cleanup as possible. # If your pod's terminationGracePeriodSeconds is larger than this terminationGracePeriod, Karpenter may forcibly delete the pod # before it has its full terminationGracePeriod to cleanup. # Note: changing this value in the nodepool will drift the nodeclaims. terminationGracePeriod: 48h # Requirements that constrain the parameters of provisioned nodes. # These requirements are combined with pod.spec.topologySpreadConstraints, pod.spec.affinity.nodeAffinity, pod.spec.affinity.podAffinity, and pod.spec.nodeSelector rules. # Operators { In, NotIn, Exists, DoesNotExist, Gt, and Lt } are supported. requirements: - key: "karpenter.k8s.aws/instance-category" operator: In values: ["c", "m", "r"] # minValues here enforces the scheduler to consider at least that number of unique instance-category to schedule the pods. # This field is ALPHA and can be dropped or replaced at any time minValues: 2 - key: "karpenter.k8s.aws/instance-family" operator: In values: ["m5","m5d","c5","c5d","c4","r4"] minValues: 5 - key: "karpenter.k8s.aws/instance-cpu" operator: In values: ["4", "8", "16", "32"] - key: "karpenter.k8s.aws/instance-hypervisor" operator: In values: ["nitro"] - key: "karpenter.k8s.aws/instance-generation" operator: Gt values: ["2"] - key: "topology.kubernetes.io/zone" operator: In values: ["us-west-2a", "us-west-2b"] - key: "kubernetes.io/arch" operator: In values: ["arm64", "amd64"] - key: "karpenter.sh/capacity-type" operator: In values: ["spot", "on-demand"] # Disruption section which describes the ways in which Karpenter can disrupt and replace Nodes # Configuration in this section constrains how aggressive Karpenter can be with performing operations # like rolling Nodes due to them hitting their maximum lifetime (expiry) or scaling down nodes to reduce cluster cost disruption: # Describes which types of Nodes Karpenter should consider for consolidation # If using 'WhenEmptyOrUnderutilized', Karpenter will consider all nodes for consolidation and attempt to remove or replace Nodes when it discovers that the Node is empty or underutilized and could be changed to reduce cost # If using `WhenEmpty`, Karpenter will only consider nodes for consolidation that contain no workload pods consolidationPolicy: WhenEmptyOrUnderutilized | WhenEmpty # The amount of time Karpenter should wait to consolidate a node after a pod has been added or removed from the node. # You can choose to disable consolidation entirely by setting the string value 'Never' here consolidateAfter: 1m | Never # Added to allow additional control over consolidation aggressiveness # Budgets control the speed Karpenter can scale down nodes. # Karpenter will respect the minimum of the currently active budgets, and will round up # when considering percentages. Duration and Schedule must be set together. budgets: - nodes: 10% # On Weekdays during business hours, don't do any deprovisioning. - schedule: "0 9 * * mon-fri" duration: 8h nodes: "0" # Resource limits constrain the total size of the pool. # Limits prevent Karpenter from creating new instances once the limit is exceeded. limits: cpu: "1000" memory: 1000Gi # Priority given to the NodePool when the scheduler considers which NodePool # to select. Higher weights indicate higher priority when comparing NodePools. # Specifying no weight is equivalent to specifying a weight of 0. weight: 10 status: conditions: - type: Initialized status: "False" observedGeneration: 1 lastTransitionTime: "2024-02-02T19:54:34Z" reason: NodeClaimNotLaunched message: "NodeClaim hasn't succeeded launch" resources: cpu: "20" memory: "8192Mi" ephemeral-storage: "100Gi"注意:
expireAfter: 720h | Never、consolidationPolicy: WhenEmptyOrUnderutilized | WhenEmpty、consolidateAfter: 1m | Never中的竖线是文档中表达"多选一"的记号,实际 YAML 中请填入其中一个值。
节点模板:spec.template
spec.template描述 Karpenter 如何渲染出 NodeClaim 资源。Karpenter 把该模板视为供给节点的最低要求,并在调度时将待调度 Pod 的需求叠加(overlay)到模板之上,进一步约束 NodeClaim,最终创建节点。
metadata.name
NodePool 的名称。
spec.template.metadata.labels
应用到所有节点上的任意键值对标签。注意:这些标签在创建 NodeClaim 时也会被传播为 NodeClaim 上的 requirements,因此在计算需求总数限制时要一并计入(见下文"100 个限制")。
spec.template.metadata.annotations
应用到所有节点上的任意键值对注解。
spec.template.spec.nodeClassRef
该字段指向云提供商的 NodeClass 资源。在 AWS 上即EC2NodeClass,格式为:
nodeClassRef: group: karpenter.k8s.aws kind: EC2NodeClass name: defaultEC2NodeClass 负责所有 AWS 专属配置(AMI 选择、子网/安全组发现、IAM 角色、kubelet 参数、块设备映射、UserData 等)。一个 EC2NodeClass 可以被多个 NodePool 引用。详见 EC2NodeClasses 文档。仓库中的示例(如 general-purpose.yaml)展示了 NodePool 与 EC2NodeClass 配套部署的完整形态:
apiVersion: karpenter.sh/v1 kind: NodePool metadata: name: general-purpose spec: template: spec: requirements: - key: kubernetes.io/arch operator: In values: ["amd64"] - key: karpenter.sh/capacity-type operator: In values: ["on-demand"] nodeClassRef: group: karpenter.k8s.aws kind: EC2NodeClass name: default --- apiVersion: karpenter.k8s.aws/v1 kind: EC2NodeClass metadata: name: default spec: role: "KarpenterNodeRole-${CLUSTER_NAME}" subnetSelectorTerms: - tags: karpenter.sh/discovery: "${CLUSTER_NAME}" securityGroupSelectorTerms: - tags: karpenter.sh/discovery: "${CLUSTER_NAME}" amiSelectorTerms: - alias: al2023@latestspec.template.spec.taints
添加到被供给节点上的污点。不容忍这些污点的 Pod 将无法调度到这些节点上。污点机制与 Kubernetes 原生 Taints and Tolerations 语义一致(key、value、effect三个字段,effect常见取值为NoSchedule、NoExecute、PreferNoSchedule)。
spec.template.spec.startupTaints
同样会被添加到节点上,用于指示"某个条件必须先满足(例如启动 agent、配置网络),节点才能被初始化"。与taints的关键区别是:Pod 不需要容忍 startupTaints 就能被该 NodePool 供给。这些污点必须在 Pod 部署到节点之前被清除(通常由容忍该污点的 DaemonSet 在完成初始化后移除)。
spec.template.spec.expireAfter
节点在集群中可存活的最长时间,到期后 Karpenter 会将其删除,节点随即开始排空(drain)。设置过期时间的价值在于:避免长期运行的节点积累安全漏洞,降低文件碎片化、系统进程内存泄漏等长期运行带来的问题。可以设置为字符串Never来完全禁用过期机制。
需要注意:修改 NodePool 中的该值会导致 NodeClaim 漂移(drift),Karpenter 会据此替换现有节点。从 CRD 定义(karpenter.sh_nodepools.yaml)可以看到expireAfter的默认值是720h(30 天),即节点从创建起最多存活 30 天。
spec.template.spec.terminationGracePeriod
节点可以处于排空(draining)状态的最长时间,超过后 Karpenter 会强制清理节点。排空从对节点发起删除调用、进入终结流程时开始。此期间:
- Pod 的
terminationGracePeriodSeconds会被提前删除,以便在terminationGracePeriod结束前留出尽可能多的清理时间; - 如果你的 Pod 的
terminationGracePeriodSeconds大于该terminationGracePeriod,Karpenter 可能在 Pod 用完完整的终止宽限期之前就强制删除 Pod; - PDB(PodDisruptionBudget)和 do-not-disrupt 等阻塞驱逐的机制在排空期间会被尊重,但一旦到达
terminationGracePeriod,这些 Pod 将被强制删除。
同样,修改该值会导致 NodeClaim 漂移。仓库示例 max-node-lifetime.yaml 给出了一个组合用法:expireAfter: 168h(7 天)+terminationGracePeriod: 24h,配合disruption.consolidationPolicy: WhenEmpty与consolidateAfter: 60s。
调度需求:spec.template.spec.requirements
Kubernetes 定义了一系列 Well-Known Labels)。
这些 well-known 标签可以出现在 NodePool 层级,也可以出现在工作负载定义中(例如 Pod spec 的nodeSelector)。节点的选择同时使用NodePool 的需求与Pod 的需求:
- 两者若无交集,则不会启动节点;
- 换句话说,Pod 的需求必须落在 NodePool 的需求范围之内;
- 如果某个 well-known 标签未定义任何需求,云提供商提供的任意取值都可以被选择。
例如,Pod 可以用nodeSelector指定实例类型;如果请求的实例类型不在 NodePool 的实例类型列表内(且 NodePool 定义了实例类型需求),Karpenter 既不会创建节点,也不会调度该 Pod。
Well-Known Labels 分类说明
实例类型(Instance Types)
| 标签键 | 说明 |
|---|---|
node.kubernetes.io/instance-type | 实例类型,如g4dn.8xlarge |
karpenter.k8s.aws/instance-family | 实例系列,如g4dn |
karpenter.k8s.aws/instance-category | 实例类别,通常是代数前的字符串,如g |
karpenter.k8s.aws/instance-generation | 实例代数,如4 |
一般情况下,实例类型应该是一个列表而不是单个值。建议不定义这些需求,以便 Karpenter 在为 Pod 高效装箱时有最多的选择空间。AWS 实例类型支持情况可参考 实例类型参考文档,大多数实例类型都受支持(非 HVM 虚拟化的除外)。
可用区(Availability Zones)
| 标签键 | 示例值 | 取值来源 |
|---|---|---|
topology.kubernetes.io/zone | us-east-1c | aws ec2 describe-availability-zones --region <region-name> |
Karpenter 可以被配置为只在特定可用区创建节点。注意:你 AWS 账号下的us-east-1a与其他账号的us-east-1a地理位置可能不同(可用区 ID 才是全局一致的,参见 AWS 的 AZ IDs 文档)。
架构(Architecture)
| 标签键 | 取值 |
|---|---|
kubernetes.io/arch | amd64、arm64 |
Karpenter 同时支持amd64与arm64节点。仓库示例 multiple-arch.yaml 展示了如何用多个 NodePool 分别承载不同架构的工作负载。
操作系统(Operating System)
| 标签键 | 取值 |
|---|---|
kubernetes.io/os | linux、windows |
Karpenter 支持linux与windows操作系统。
容量类型(Capacity Type)
| 标签键 | 取值 |
|---|---|
karpenter.sh/capacity-type | spot、on-demand |
Karpenter 支持指定容量类型,其语义与 EC2 购买选项 对应。容量类型的行为细节:
- 如果 NodePool 同时允许 Spot 与 on-demand,Karpenter 优先使用 Spot(注意:此场景下,价格高于最便宜 on-demand 实例的 Spot 实例会被临时移出候选集);
- 如果提供商 API(如 EC2 Fleet 的 API)指示某个实例类型在某可用区无 Spot 容量,Karpenter 会对该结果缓存 3 分钟,期间所有供给 EC2 容量的尝试都会复用该缓存;
- 如果 Spot 没有其他可用 offering,Karpenter 会尝试供给 on-demand 实例,通常在毫秒级内完成;
karpenter.sh/capacity-type还可以作为topology key用于拓扑分布约束(topology-spread)。
仓库示例 spot.yaml 就是一个将karpenter.sh/capacity-type固定为spot的完整 NodePool。
需求数量上限(100 个)
当前 NodePool 与 NodeClaim 上需求总数上限为 100。特别注意:spec.template.metadata.labels在创建 NodeClaim 时也会被传播为 requirements,因此NodePool 上的 requirements 与 labels 合起来不能超过 100 个。这一限制在调度文档(scheduling.md)与 CRD 校验中均有体现。
运算符支持
spec.requirements支持 Kubernetes 标准的六种运算符:In、NotIn、Exists、DoesNotExist、Gt、Lt。其中Gt/Lt用于数值型标签(如 CPU 核数、本地 NVMe 容量、网络带宽)的大小比较。
Min Values:最小灵活性约束(ALPHA)
在[key, operator, values]组合之外,Karpenter 还支持在 NodePool 的 requirements 块中声明minValues,让调度器感知用户指定的灵活性下限。如果 Karpenter 在为某个 Pod 调度时,无法为每个声明了minValues的 key 提供足够数量的唯一取值,就会让该 NodePool 的调度循环失败,转而回退到其他满足 Pod 需求的 NodePool,或直接调度失败。
例如下面的 spec 强制了多个 key 的minValues:调度 Pod 时至少需要 2 个唯一实例类别(来自[c, m, r])、5 个唯一实例系列(如m5、m5d、r4、c5、c5d、c4等)、10 个唯一实例类型(如c5.2xlarge、c4.xlarge等):
spec: template: spec: requirements: - key: kubernetes.io/arch operator: In values: ["amd64"] - key: kubernetes.io/os operator: In values: ["linux"] - key: karpenter.k8s.aws/instance-category operator: In values: ["c", "m", "r"] minValues: 2 - key: karpenter.k8s.aws/instance-family operator: Exists minValues: 5 - key: node.kubernetes.io/instance-type operator: Exists minValues: 10 - key: karpenter.k8s.aws/instance-generation operator: Gt values: ["2"]关于minValues的要点:
minValues可以与多种运算符、多个 requirements 组合使用;- 如果同一个 key 上以多个运算符分别声明了
minValues,调度器按其中最大值计算。例如下面的 spec 中,karpenter.k8s.aws/instance-family同时声明了Exists + minValues: 5与In + minValues: 3,调度器最终要求至少考虑 5 个实例系列:
spec: template: spec: requirements: - key: kubernetes.io/arch operator: In values: ["amd64"] - key: kubernetes.io/os operator: In values: ["linux"] - key: karpenter.k8s.aws/instance-category operator: In values: ["c", "m", "r"] minValues: 2 - key: karpenter.k8s.aws/instance-family operator: Exists minValues: 5 - key: karpenter.k8s.aws/instance-family operator: In values: ["m5","m5d","c5","c5d","c4","r4"] minValues: 3 - key: node.kubernetes.io/instance-type operator: Exists minValues: 10 - key: karpenter.k8s.aws/instance-generation operator: Gt values: ["2"]从 CRD 定义(karpenter.sh_nodepools.yaml)可以看到minValues的合法范围是1~50,并且有一条 CEL 校验规则:使用In运算符且声明了minValues时,values字段中的取值数量必须大于等于minValues。该字段目前处于ALPHA阶段,随时可能被移除或替换。仓库还提供了两个专门示例:min-values-family.yaml 与 min-values-multiple-keys.yaml。
推荐的 requirements 基线
Karpenter 允许你在 NodePool 上做非常灵活的约束,建议只对集群绝对必要的维度限制实例类型。默认情况下,Karpenter 会强制要求填写spec.template.spec.requirements字段,但不会强制要求字段内必须有具体条目。如果填写requirements: [],意味着你对云提供商支持的所有实例类型完全开放。
虽然 Karpenter 不强制这些默认值,但对大多数通用负载场景,官方推荐至少声明一些 requirements,以避免出现奇怪行为或极端实例类型。以下是适合大多数通用工作负载的高层推荐基线:
spec: template: spec: 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: ["on-demand"] - key: karpenter.k8s.aws/instance-category operator: In values: ["c", "m", "r"] - key: karpenter.k8s.aws/instance-generation operator: Gt values: ["2"]仓库示例 general-purpose.yaml 中的 NodePool 与此基线完全一致(amd64 + linux + on-demand + c/m/r 类别 + 代数大于 2),可作为通用生产配置的起点。
中断策略:spec.disruption
通过spec.disruption,你可以用多种方式配置 Karpenter 对节点的中断(disrupt)行为:
spec.disruption.consolidationPolicy:合并策略;spec.disruption.consolidateAfter:合并等待时间;spec.template.spec.expireAfter:节点过期时间(见上文);spec.disruption.budgets:对中断行为做速率限制。
consolidationPolicy决定 Karpenter 将哪些节点纳入合并(consolidation)考量:
WhenEmptyOrUnderutilized(默认):Karpenter 会考虑所有节点,当发现节点为空或利用率不足、且通过移除或替换节点能够降低成本时,尝试执行合并;WhenEmpty:Karpenter 只考虑不含工作负载 Pod的节点进行合并。
consolidateAfter指定在节点上添加或移除 Pod 之后,Karpenter 等待多久才对该节点执行合并。可以设置为字符串Never以完全禁用合并。从 CRD 定义(karpenter.sh_nodepools.yaml)可以看到consolidationPolicy的默认值是WhenEmptyOrUnderutilized,consolidateAfter的默认值是0s。
budgets控制 Karpenter 缩容节点的速度:
- Karpenter 会取当前生效预算中的最小值,百分比计算时向上取整;
duration与schedule必须成对设置;- 示例中第一个预算
nodes: 10%始终生效(无 schedule),表示任何时候最多同时中断 10% 的节点;第二个预算在工作日(mon-fri)9 点到 17 点("0 9 * * mon-fri",持续 8h)之间生效,将节点中断数限制为"0",即业务高峰完全不缩容。
disruption: budgets: - nodes: 10% - schedule: "0 9 * * mon-fri" duration: 8h nodes: "0"关于中断的更多细节(合并、过期、drift、预算的完整语义),参见 Disruption 文档。
资源限额:spec.limits
NodePool spec 中的spec.limits段用来约束该 NodePool 可以消耗的最大资源总量:
- 如果
spec.limits未指定,表示没有默认的资源分配限制,此时最大资源消耗由云提供商的配额决定; - 一旦限额被超过,Karpenter 将停止供给新节点,直到部分节点被终止释放资源。
apiVersion: karpenter.sh/v1 kind: NodePool metadata: name: default spec: template: spec: requirements: - key: karpenter.sh/capacity-type operator: In values: ["spot"] limits: cpu: 1000 memory: 1000Gi nvidia.com/gpu: 2关于限额的重要事实:
- CPU 限额使用
DecimalSI值(如1000表示 1000 核)。注意 Kubernetes API 会将其强制转换为字符串,因此不建议使用整数以避免 GitOps 漂移(例如 Helm 渲染与 kubectl apply 之间出现类型不一致); - 内存限额使用
BinarySI值(如1000Gi),语义同 Kubernetes 容器内存管理中的 memory 含义; - 除了 CPU 与内存,还可以对其他资源(如
nvidia.com/gpu)设置限额; - 由于Karpenter 的供给是高度并行的,限额检查是最终一致的,在快速扩容时可能出现限额超卖(overrun);
- 可以通过以下命令查看集群中当前 CPU 与内存的消耗情况:
kubectl get nodepool -o=jsonpath='{.items[0].status}'status输出中的resources段(如cpu: "20"、memory: "8192Mi"、ephemeral-storage: "100Gi")即当前已被该 NodePool 占用的资源量。关于resources的更多类型定义,可参考 Kubernetes 核心 API(k8s.io/api/core/v1)中ResourceList的说明。
优先级:spec.weight
Karpenter 通过weight机制描述 NodePool 的优先级偏好,语义与 Pod 与节点的亲和性权重 类似:
- 调度器在多个 NodePool 之间做选择时,权重越高优先级越高;
- 不设置 weight 等价于 weight 为 0。
weight 的典型应用场景(详见 调度文档的 Weighted NodePools 一节):
- Savings Plans / Reserved Instances 优先:如果你购买了 Savings Plan 或预留实例,可以创建一个限制为特定实例类型、并设置较高
weight的 NodePool(同时用spec.limits限制其可供给的最大容量),让 Karpenter 优先使用预留容量,超出部分再回退到通用实例类型; - 集群级默认配置(Fallback):对没有声明 nodeSelector / affinity 的 Pod,通过设置较高
weight并限定特定容量类型或架构的 NodePool,为其提供集群级的默认节点配置。
需要注意的是,基于 Karpenter 的 Pod 批处理(batching)与装箱(bin packing)方式,并不保证 Karpenter 总是选择权重最高的 NodePool。例如:当 Pod 无法用最高优先级 NodePool 调度时,会强制用较低优先级 NodePool 创建节点,同一批的其他 Pod 也可能随之调度到该节点;或者当已有容量可用时,kube-scheduler 会直接把 Pod 调度走而不触发 Karpenter 新建节点。
状态观测:status.conditions 与 status.resources
status.conditions
Conditions 对象为 Karpenter 增加可观测性,各字段含义如下:
status.conditions.type:反映节点/对象的状态类型,如Initialized、Available;status.conditions.status:指示条件是True还是False;status.conditions.observedGeneration:指示实例是否落后于.metadata.generation的当前状态;status.conditions.lastTransitionTime:上一次状态转换时间的程序化标识;status.conditions.reason:上一次状态转换的原因;status.conditions.message:关于上一次状态转换的人类可读细节。
NodePool 自身的 status conditions 包括:
| Condition Type | 描述 |
|---|---|
NodeClassReady | 底层 nodeClass(EC2NodeClass)就绪 |
ValidationSucceeded | NodePool CRD 校验成功 |
Ready | 顶层条件,指示 NodePool 是否就绪;只有其余所有条件都为 True 时该条件才为 True |
如果 NodePool 未就绪(Ready 为 False),它将不会被纳入调度考虑。
status.resources
status.resources下的对象提供资源状态信息,如cpu、memory、ephemeral-storage的当前占用(示例中为cpu: "20"、memory: "8192Mi"、ephemeral-storage: "100Gi"),与spec.limits配合可用于观察限额消耗进度。
实战示例
示例一:隔离昂贵硬件(GPU)
可以创建一个只供给特定处理器类型的 NodePool。下面的示例通过 taint 实现:只有带 Nvidia GPU toleration 的 Pod 才能调度上来:
apiVersion: karpenter.sh/v1 kind: NodePool metadata: name: gpu spec: disruption: consolidationPolicy: WhenEmptyOrUnderutilized template: spec: requirements: - key: node.kubernetes.io/instance-type operator: In values: ["p3.8xlarge", "p3.16xlarge"] taints: - key: nvidia.com/gpu value: "true" effect: NoSchedule要让 Pod 运行在该 NodePool 供给的节点上,Pod spec 必须容忍nvidia.com/gpu(并在资源中声明 GPU 需求,例如limits: { nvidia.com/gpu: 1 })。完整的工作负载示例可参考仓库 workloads/gpu-nvidia.yaml。需要注意的是:供给 GPU 节点时,集群中必须部署对应的 GPU device plugin DaemonSet,否则 Karpenter 不会将这些节点视为已初始化。
示例二:Cilium 启动污点(startupTaints)
按 Cilium 官方建议,应该在节点上放置node.cilium.io/agent-not-ready=true:NoExecute污点,让 Cilium 在其他 Pod 启动前先完成网络配置。这可以通过 Karpenter 的startupTaints实现——污点会被打到节点上,但Pod 不需要容忍这些污点就能被纳入供给考量:
apiVersion: karpenter.sh/v1 kind: NodePool metadata: name: cilium-startup spec: disruption: consolidationPolicy: WhenEmptyOrUnderutilized template: spec: startupTaints: - key: node.cilium.io/agent-not-ready value: "true" effect: NoExecute未正确配置startupTaints的风险:如果节点上有 Karpenter 不知道的 startup taint(例如 Cilium 在节点加入后自行添加了该污点),Karpenter 会认为待调度 Pod 无法调度到该节点,于是不断尝试供给新节点来调度这个 pending Pod,导致节点被持续创建。因此凡是通过外部系统(DaemonSet 等)在节点上打污点的场景,都必须把对应污点声明进startupTaints,让 Karpenter 提前感知。
更多仓库示例
仓库 examples/v1 目录提供了大量可直接参考的 NodePool 配置,包括:
- general-purpose.yaml:通用负载基线配置;
- spot.yaml:全部使用 Spot 容量;
- max-node-lifetime.yaml:7 天过期 + 空节点 60 秒回收;
- multiple-arch.yaml:多架构(amd64/arm64)NodePool 拆分;
- min-values-family.yaml 与 min-values-multiple-keys.yaml:
minValues用法演示; - 100-cpu-limit.yaml:资源限额用法;
- large-instances.yaml:大实例约束。
关键要点速查
- NodePool 是 Karpenter 的调度入口,至少配置一个 NodePool 后 Karpenter 才会工作;
- 节点模板
spec.template通过nodeClassRef关联云提供商专属的 EC2NodeClass(AWS 上 kubelet 参数已迁移至 EC2NodeClass.spec.kubelet); taints需要 Pod 容忍,startupTaints不需要——但要防止"未知污点导致反复建节点"的循环供给问题;requirements用In/NotIn/Exists/DoesNotExist/Gt/Lt六种运算符组合约束节点属性,Pod 需求必须落在 NodePool 需求范围内;requirements 与 labels 合计不得超过 100 个;minValues(ALPHA)可为调度器声明灵活性下限,同一 key 多运算符时取最大值,取值范围 1~50 且值数量须不小于 minValues;disruption控制合并/过期/缩容节奏,budgets可用 cron 表达式实现"高峰不缩容";spec.limits约束池子总资源上限,未设置时受云提供商配额约束;限额检查最终一致,快速扩容可能超卖;spec.weight决定多 NodePool 匹配时的优先级,但受批处理与装箱逻辑影响,不保证严格按权重执行;status.conditions中Ready为 False 的 NodePool 不会被调度考虑。
【免费下载链接】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),仅供参考