news 2026/9/17 7:18:07

Karpenter 调度机制完全指南:从 Pod 约束到 NodePool 分层调度实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Karpenter 调度机制完全指南:从 Pod 约束到 NodePool 分层调度实战

Karpenter 调度机制完全指南:从 Pod 约束到 NodePool 分层调度实战

【免费下载链接】karpenter-provider-awsKarpenter is a Kubernetes Node Autoscaler built for flexibility, performance, and simplicity.项目地址: https://gitcode.com/GitHub_Trending/ka/karpenter-provider-aws

本指南以 Karpenter(karpenter-provider-aws 仓库)v1.0 版本文档为核心,系统讲解 Karpenter 的调度模型:如何通过"云提供商约束 → NodePool 约束 → Pod 约束"三层叠加,实现精准的节点选择;如何利用资源请求、nodeSelector、节点亲和性、拓扑分布、污点与容忍、持久卷拓扑等 Kubernetes 标准调度能力;以及如何通过加权 NodePool、限制(limits)与高级调度技巧(Exists操作符、Spot/On-Demand 比例拆分)满足生产环境的精细化需求。读完本文,你将掌握用声明式 YAML 配置 Karpenter 调度策略的完整方法,并能结合源码理解其底层调度决策逻辑。

分层约束模型:理解 Karpenter 如何决策

Karpenter 的调度哲学建立在一个核心概念上:分层的约束叠加(layered constraints)。如果你的 Pod 对运行位置和运行方式没有任何要求,Karpenter 可以从云提供商提供的全部资源中自由选择节点。但借助这一分层约束模型,你可以确保 Pod 获得精确的类型与数量的资源。

产生约束需求的典型场景包括:

  • 需要运行在依赖应用或存储所在的可用区(Zone)
  • 需要特定种类的处理器或其他硬件(如 GPU、NVMe 本地存储)
  • 希望使用拓扑分布(Topology Spread)等技术保障高可用

约束由三个层次依次叠加而成:

  1. 云提供商层:定义其云环境可用的全部实例类型、架构、可用区与购买方式(如 Spot、On-Demand)。这是最宽泛的一层。
  2. 集群管理员层:通过创建一个或多个 NodePool 增加约束。NodePool 定义了节点的最小规格模板。
  3. 应用开发者层:在 Pod/Deployment 的 spec 中添加调度约束。

关键原则是:Pod 的调度约束必须落在某个 NodePool 的约束范围之内,否则 Pod 无法被部署。例如,如果 NodePool 限制了只能使用某个特定可用区,而 Pod 请求了另一个可用区,那么该 Pod 将无法被调度。从调度实现的角度看,NodePool 中的spec.template.spec.requirements会被作为 NodeClaim 的创建模板,Karpenter 在调度 Pod 时会将 Pod 的约束与 NodePool 的约束做交集(intersection)计算,只有交集非空才会启动节点。

Pod 侧可以请求的约束类型包括:

  • 资源请求(Resource requests):请求一定量的内存或 CPU。
  • 节点选择(Node selection):通过nodeSelector选择带有特定标签的节点。
  • 节点亲和性(Node affinity):将 Pod 吸引到具有特定属性的节点上。
  • 拓扑分布(Topology spread):利用拓扑分布保障应用的可用性。
  • Pod 亲和/反亲和(Pod affinity/anti-affinity):根据其他 Pod 的调度情况,将 Pod 吸引或排斥到某些拓扑域。

Karpenter 完整支持 Kubernetes 标准调度约束,这意味着你可以定义一套同时适用于既有容量(existing capacity,由 kube-scheduler 调度)与 Karpenter 新置备容量(provisioned capacity)的规则。Karpenter 还支持 Kubernetes 官方的 Well-Known Labels, Annotations and Taints,这些标签对调度非常有用。

资源请求:实例类型选择的输入

在 Pod spec 中,你可以同时为 Pod 所需的资源(如 CPU 与内存)设置请求(requests)与上限(limits):

apiVersion: v1 kind: Pod metadata: name: myapp spec: containers: - name: app image: myimage resources: requests: memory: "128Mi" cpu: "500m" limits: memory: "256Mi" cpu: "1000m"

在上例中,容器请求了 128MiB 内存与 0.5 CPU,上限设置为 256MiB 内存与 1 CPU。实例类型的选择计算只使用requests,而limits可以用于启用资源超卖(oversubscription)。这是 Karpenter 与常规调度器的一个重要区别:它基于请求量来做装箱(bin packing)决策,因此合理设置 requests 直接影响节点选型与成本。

有关 Kubernetes 支持的资源类型细节,可参考 Managing Resources for Containers。

加速器/GPU 资源

加速器(如 GPU)的取值包括:

  • nvidia.com/gpu
  • amd.com/gpu
  • aws.amazon.com/neuron
  • habana.ai/gaudi

Karpenter 支持 GPU 等加速器。在工作负载清单中附加资源需求,即可将依赖 GPU 的 Pod 调度到合适的节点上。以下是一个工作负载清单(如 Pod)中的加速器资源示例:

spec: template: spec: containers: - resources: limits: nvidia.com/gpu: "1"

注意:如果置备 GPU 节点,你需要为这些节点部署相应的 GPU 设备插件 DaemonSet。没有 DaemonSet 运行,Karpenter 将看不到这些节点完成初始化(节点会一直处于未就绪状态)。相关文档:

  • nvidia.com/gpu:NVIDIA device plugin for Kubernetes
  • amd.com/gpu:AMD GPU device plugin for Kubernetes
  • aws.amazon.com/neuron:Kubernetes environment setup for Neuron
  • habana.ai/gaudi:Habana device plugin for Kubernetes

Pod ENI 资源(Security Groups for Pods)

Pod ENI 是 AWS VPC CNI 插件的一项功能,允许将弹性网络接口(ENI)直接分配给 Pod。启用后,vpc.amazonaws.com/pod-eni扩展资源会被添加到受支持的节点上。Pod ENI 可以独立使用,但最常与 Security Groups for Pods 配合使用。

注意:在 Karpenter 中启用 Pod ENI 之前,必须先启用 AWS VPC CNI 插件中的 Pod ENI 支持,请参考 Security Groups for Pods 文档。

注意:如果你启用了 Security Groups per Pod)。

以下是在 Deployment 清单中定义 pod-eni 资源的示例:

spec: template: spec: containers: - resources: limits: vpc.amazonaws.com/pod-eni: "1"

Windows 支持提醒:Security Groups for Pods 目前不支持 Windows 节点(见 Security Groups for Pods)。

选择节点:标签、nodeSelector 与亲和性

通过nodeSelector你可以请求一个匹配指定键值对的节点,这些键值对既可以是 well-known 标签,也可以是你自定义的标签。

affinity可用于定义更复杂的约束,完整的规范见 Node Affinity。

标签

Well-known 标签既可以作为 NodePool 的 requirements,也可以作为 Pod 的调度约束。你也可以通过在 NodePool 上指定requirementslabels来定义自己的自定义标签,然后在 Pod 上通过nodeAffinitynodeSelector选择它们。

警告:务必确保标签域(domain)正确。像karpenter.k8s.aws/instance-family这样的 well-known 标签会强制执行节点属性,但可能与node.kubernetes.io/instance-family混淆——后者对 Karpenter 来说是未知的,会被当作自定义标签处理,不会强制执行节点属性。

Well-Known 标签一览
标签示例描述
topology.kubernetes.io/zoneus-east-2a可用区由云提供商定义(aws)
node.kubernetes.io/instance-typeg4dn.8xlarge实例类型由云提供商定义(aws)
node.kubernetes.io/windows-build10.0.17763Windows OS 构建版本,格式为 "MajorVersion.MinorVersion.BuildNumber",WS2019 为10.0.17763,WS2022 为10.0.20348
kubernetes.io/oslinux操作系统由实例上的 GOOS 值 定义
kubernetes.io/archamd64架构由实例上的 GOARCH 值 定义
karpenter.sh/nodepooldefault用于置备该节点的 NodePool 名称
karpenter.sh/capacity-typespot容量类型包括spoton-demand
karpenter.k8s.aws/ec2nodeclassdefault[AWS 专用] 用于置备该节点的 EC2NodeClass 名称
karpenter.k8s.aws/instance-hypervisornitro[AWS 专用] 使用特定 Hypervisor 的实例类型
karpenter.k8s.aws/instance-encryption-in-transit-supportedtrue[AWS 专用] 支持(或不支持)传输中加密的实例类型
karpenter.k8s.aws/instance-categoryg[AWS 专用] 相同类别的实例类型,通常为代数前的字符串
karpenter.k8s.aws/instance-generation4[AWS 专用] 实例类别内的实例类型代数
karpenter.k8s.aws/instance-familyg4dn[AWS 专用] 属性相似但资源数量不同的实例类型
karpenter.k8s.aws/instance-size8xlarge[AWS 专用] 资源数量相似但属性不同的实例类型
karpenter.k8s.aws/instance-cpu32[AWS 专用] 实例的 CPU 数量
karpenter.k8s.aws/instance-cpu-manufactureraws[AWS 专用] CPU 制造商名称
karpenter.k8s.aws/instance-memory131072[AWS 专用] 实例内存的 MiB 数
karpenter.k8s.aws/instance-ebs-bandwidth9500[AWS 专用] 实例可用的 EBS 最大带宽(Mbps)
karpenter.k8s.aws/instance-network-bandwidth131072[AWS 专用] 实例可用的基线带宽(Mbps)
karpenter.k8s.aws/instance-pods110[AWS 专用] 实例支持的 Pod 数量
karpenter.k8s.aws/instance-gpu-namet4[AWS 专用] 实例上的 GPU 名称(如果有)
karpenter.k8s.aws/instance-gpu-manufacturernvidia[AWS 专用] GPU 制造商名称
karpenter.k8s.aws/instance-gpu-count1[AWS 专用] 实例上的 GPU 数量
karpenter.k8s.aws/instance-gpu-memory16384[AWS 专用] GPU 内存的 MiB 数
karpenter.k8s.aws/instance-local-nvme900[AWS 专用] 实例本地 NVMe 存储的 GiB 数
topology.k8s.aws/zone-iduse1-az1[AWS 专用] 全局一致的可用区 ID

这些 AWS 专用标签在源码中有明确定义与注册,见 pkg/apis/v1/labels.go:例如karpenter.k8s.aws/instance-familyLabelInstanceFamily = apis.Group + "/instance-family")、topology.k8s.aws/zone-idLabelTopologyZoneID = "topology.k8s.aws/zone-id")等都被插入到WellKnownLabels集合中,成为调度器可感知的约束键。

注意:Karpenter 会将以下废弃标签翻译为稳定等价标签:failure-domain.beta.kubernetes.io/zonefailure-domain.beta.kubernetes.io/regionbeta.kubernetes.io/archbeta.kubernetes.io/osbeta.kubernetes.io/instance-type

用户自定义标签

Karpenter 感知多个 well-known 标签,这些标签由实例类型详情推导而来。如果你使用一个 Karpenter 不认识的标签指定nodeSelector或必需的nodeAffinity,Karpenter 不会启动带这些标签的节点,Pod 将保持 Pending 状态。要让 Karpenter 知道它可以为这些标签调度,必须在 NodePool 的 requirements 中使用Exists操作符声明该标签:

requirements: - key: user.defined.label/type operator: Exists

注意:目前 NodePool 和 NodeClaim 上的 requirements 总数限制为 100。需要注意,spec.template.metadata.labels在创建 NodeClaim 时也会被传播为 requirements,因此 NodePool 上设置的 requirements 与 labels 合计不能超过 100。

Node selectors

以下是一个用于选择节点的nodeSelector示例:

nodeSelector: topology.kubernetes.io/zone: us-west-2a karpenter.sh/capacity-type: spot

该示例同时使用了 well-known 标签(topology.kubernetes.io/zone)和对 Karpenter 而言的 well-known 标签(karpenter.sh/capacity-type)。

如果你想创建自定义标签,应该在 NodePool 层级定义,然后 Pod 声明该自定义标签。关于nodeSelector的细节参见 Kubernetes 文档。

Preferences:偏好如何被处理

Karpenter 感知偏好(preferences),包括节点亲和性、Pod 亲和性、Pod 反亲和性与 Pod 拓扑分布,并在大多数情况下将它们当作 requirements 处理。Karpenter 在以下两种场景使用这些偏好:

  • 判断 Pod 能否调度到某个节点(无拓扑要求时)
  • 判断 Pod 能否被迁移(shift)到新节点

Karpenter 在构造 Pod 的 requirements 时,会先把偏好亲和性(preferred affinities)当作必需亲和性(required affinities)处理。当这些 requirements 无法满足时,Pod 的偏好会按权重升序逐个放宽(权重最低的最先放宽),然后用剩余的 requirements 再次尝试。

警告:在构造调度到节点的拓扑 requirements 时,Karpenter不会把偏好亲和性解释为必需。如果这些偏好是必需的,应使用必需亲和性(见下文 Node affinity 文档)。

Node affinity

以下示例演示如何使用 Node affinity 来包含(In)或排除(NotIn)对象。设置规则时,以下两种 Node affinity 类型决定了每条规则的硬/软程度:

  • requiredDuringSchedulingIgnoredDuringExecution:必须满足的硬规则。
  • preferredDuringSchedulingIgnoredDuringExecution:偏好,但 Pod 可以在不保证满足的节点上运行。

注意:Pod 上的偏好亲和性可能导致创建的节点多于预期,因为 Karpenter 倾向于创建新节点来满足偏好(详见上文 Preferences 文档)。

每种规则的IgnoredDuringExecution部分告诉 Pod:即使节点上的条件发生变化、规则不再匹配,Pod 也继续运行。你可以将这两个概念理解为requiredpreferred(Kubernetes 从未实现这些规则的其他变体)。

下面所有示例都假设 NodePool 没有阻止使用这些可用区的约束。第一个约束表示可以使用us-west-2aus-west-2b,第二个约束使得只能使用us-west-2b

affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: "topology.kubernetes.io/zone" operator: "In" values: ["us-west-2a", "us-west-2b"] - key: "topology.kubernetes.io/zone" operator: "In" values: ["us-west-2b"]

把第二个操作符改为NotIn,Pod 将只能运行在us-west-2a

- key: "topology.kubernetes.io/zone" operator: "In" values: ["us-west-2a", "us-west-2b"] - key: "topology.kubernetes.io/zone" operator: "NotIn" values: ["us-west-2b"]

继续扩展示例:nodeAffinity允许你定义多个 term,如果第一个 term 不满足就尝试下一个。下面这个例子中,如果us-west-2a不可用,第二个 term 会让 Pod 运行在us-west-2d的 Spot 实例上:

affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: # OR - key: "topology.kubernetes.io/zone" # AND operator: "In" values: ["us-west-2a", "us-west-2b"] - key: "topology.kubernetes.io/zone" # AND operator: "NotIn" values: ["us-west-2b"] - matchExpressions: # OR - key: "karpenter.sh/capacity-type" # AND operator: "In" values: ["spot"] - key: "topology.kubernetes.io/zone" # AND operator: "In" values: ["us-west-2d"]

一般而言,Karpenter 会按顺序遍历每个nodeSelectorTerms,采用第一个可用的。如果第一个nodeSelectorTerms置备失败,它会用第二个重试;如果全部失败,则 Pod 置备失败。Karpenter 会随时间退避(backoff)并重试,因此当容量变为可用时,无需用户干预即可调度该 Pod。

污点与容忍

污点(Taints)与亲和性相反:在节点上设置污点,是告诉调度器除非 Pod 明确声明可以容忍该污点,否则不要在该节点上运行 Pod。以下示例展示了一个设置了污点的 NodePool,只运行需要 GPU 的 Pod:

apiVersion: karpenter.sh/v1 kind: NodePool metadata: name: gpu spec: template: spec: requirements: - key: karpenter.k8s.aws/instance-family operator: In values: - p3 taints: - key: nvidia.com/gpu value: "true" effect: "NoSchedule"

要让 Pod 请求运行在由此 NodePool 创建的节点上,可以在 Pod 上设置如下容忍(toleration):

apiVersion: v1 kind: Pod metadata: name: mygpupod spec: containers: - name: gpuapp resources: requests: nvidia.com/gpu: 1 limits: nvidia.com/gpu: 1 image: mygpucontainer tolerations: - key: "nvidia.com/gpu" operator: "Exists" effect: "NoSchedule"

关于 Taints and Tolerations 的细节参见 Kubernetes 文档。

拓扑分布(Topology Spread)

利用 Kubernetes 的topologySpreadConstraints,你可以让 Pod 互相"推开",从而限制故障的爆炸半径。可以把它理解为 Pod 亲和性的演进:在允许分散的同时建立 Pod 与节点之间的关系。

注意:偏好型拓扑分布(ScheduleAnyway)可能导致创建的节点多于预期,因为 Karpenter 倾向于创建新节点来满足分布约束(详见 Preferences 文档)。

示例:

spec: topologySpreadConstraints: - maxSkew: 1 topologyKey: "topology.kubernetes.io/zone" whenUnsatisfiable: ScheduleAnyway labelSelector: matchLabels: dev: jjones - maxSkew: 1 topologyKey: "kubernetes.io/hostname" whenUnsatisfiable: ScheduleAnyway labelSelector: matchLabels: dev: jjones - maxSkew: 1 topologyKey: "karpenter.sh/capacity-type" whenUnsatisfiable: ScheduleAnyway labelSelector: matchLabels: dev: jjones

将此配置加入 PodSpec 会产生如下效果:

  • Pod 会跨可用区、主机和容量类型(topologyKey)分布。
  • devlabelSelector会把所有带dev=jjones标签的 Pod 纳入拓扑计算。建议使用 selector 匹配 Deployment 中的所有 Pod。
  • 每台主机上的 Pod 数量差不超过 1(maxSkew)。例如,如果有三个节点和五个 Pod,Pod 的分布可以是 1、2、2 或 2、1、2 等;如果改为 5 个 Pod,则可以是 5、0、0 或 3、2、0、或 2、1、2 等。

Karpenter 支持的三个topologyKey取值为:

  • topology.kubernetes.io/zone
  • kubernetes.io/hostname
  • karpenter.sh/capacity-type

(其中karpenter.sh/capacity-type作为拓扑键的能力在 nodepools 文档 中有明确说明。)

详见 Pod Topology Spread Constraints。

注意:NodePool 不会尝试平衡或再平衡其节点的可用区分布。可用区平衡可以通过为需要多可用区持久性的 Pod 定义分区(zonal)拓扑分布约束来实现,NodePool 会在优化计算成本的同时尊重这些约束。

Pod 亲和/反亲和

通过 PodSpec 上的podAffinitypodAntiAffinity配置,你可以告知 Karpenter 调度器:希望 Pod 相对于不同的拓扑域一起调度或分开调度。

注意:Pod 上的偏好亲和性可能导致创建的节点多于预期(详见 Preferences 文档)。

示例:

spec: affinity: podAffinity: requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: matchExpressions: - key: system operator: In values: - backend topologyKey: topology.kubernetes.io/zone podAntiAffinity: requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: matchLabels: app: inflate topologyKey: kubernetes.io/hostname

上述亲和性规则会使 Pod 只在已经运行了带system=backend标签 Pod 的可用区调度;反亲和规则则会使它避免运行在任何带有app=inflate标签 Pod 的节点上。如果该反亲和 term 位于 Deployment 的 PodSpec 上,且 Deployment 自身带有匹配的app=inflate标签,则 Deployment 的多个 Pod 不会运行在同一节点上。

详见 Inter-pod affinity and anti-affinity。

持久卷拓扑(Persistent Volume Topology)

Karpenter 会自动检测存储调度需求,并将其纳入节点启动决策。

在下面的示例中,StorageClassus-west-2aus-west-2b定义了分区拓扑,并使用 binding modeWaitForFirstConsumer。当 Pod 被创建时,Karpenter 会沿着Pod → PersistentVolumeClaim → StorageClass的引用链,识别出该 Pod 需要在us-west-2aus-west-2b中存储。它随机选择us-west-2a,在该可用区置备节点,并等待 kube-scheduler 将 Pod 绑定到该节点。随后 CSI 驱动根据PersistentVolumeClaim创建PersistentVolume,并为其赋予us-west-2a的节点亲和性规则。

之后,如果该 Pod 被删除,新 Pod 创建并请求同一 PVC,Karpenter 会识别出该 PVC 已有对应的PersistentVolume,并把其所在可用区us-west-2a纳入 Pod 的调度 requirements:

apiVersion: v1 kind: Pod metadata: name: app spec: containers: ... volumes: - name: storage persistentVolumeClaim: claimName: ebs-claim --- kind: StorageClass apiVersion: storage.k8s.io/v1 metadata: name: ebs provisioner: ebs.csi.aws.com volumeBindingMode: WaitForFirstConsumer allowedTopologies: - matchLabelExpressions: - key: topology.ebs.csi.aws.com/zone values: ["us-west-2a", "us-west-2b"] --- apiVersion: v1 kind: PersistentVolumeClaim metadata: name: ebs-claim spec: accessModes: - ReadWriteOnce storageClassName: ebs resources: requests: storage: 4Gi

注意(AWS 专用):EBS CSI 驱动使用topology.ebs.csi.aws.com/zone而不是标准的topology.kubernetes.io/zone标签。Karpenter 感知标签别名(label aliasing),会在内存中将该标签翻译为topology.kubernetes.io/zone。为 EBS CSI 驱动配置StorageClass时,必须使用topology.ebs.csi.aws.com/zone

注意:拓扑键topology.kubernetes.io/region不受支持——遗留的 in-tree CSI 提供商会指定该标签。请改用 out-of-tree CSI 提供商,详见 Kubernetes 官方迁移说明。

加权 NodePool:控制调度优先级

Karpenter 允许你通过.spec.weight字段为 NodePool 排序,使 Karpenter 调度器优先尝试调度某个 NodePool。权重语义与 Pod/Node 亲和性中的 weight 类似:权重越高优先级越高,不指定权重等价于权重 0。当多个 NodePool 与 Pod 匹配时,Karpenter 使用权重最高的 NodePool(见 nodepools 文档)。

Savings Plans 与 Reserved Instances

如果你购买了 Savings Plan 或 Reserved Instances,你可能希望 Karpenter 优先使用这些预留容量而非其他实例类型。

为此,你需要告诉 Karpenter 控制器:优先使用哪些实例类型,以及使用这些实例类型最多能置备多少容量。可以通过 NodePool 的.spec.limits字段限制该 NodePool 可启动的容量,再结合.spec.weight,让 Karpenter 在回退到通用实例类型之前,优先从预留实例 NodePool 拉取容量:

apiVersion: karpenter.sh/v1 kind: NodePool metadata: name: reserved-instance spec: weight: 50 limits: cpu: 100 template: spec: requirements: - key: "node.kubernetes.io/instance-type" operator: In values: ["c4.large"] --- apiVersion: karpenter.sh/v1 kind: NodePool metadata: name: default spec: template: spec: requirements: - key: karpenter.sh/capacity-type operator: In values: ["spot", "on-demand"] - key: kubernetes.io/arch operator: In values: ["amd64"]

这里limits.cpu: 100DecimalSI值描述(Kubernetes API 会将其强转为字符串,建议避免使用整数以防 GitOps 偏差),limits到达上限后该 NodePool 将不再置备新实例,直到有节点被终止释放容量。limits检查是最终一致(eventually consistent)的,快速扩容时可能出现轻微超限。

Fallback:集群级默认配置

不指定 nodeSelector 或 affinity 的 Pod 理论上可以被分配到任何配置的节点。但在某些场景下,你可能要求这些 Pod 调度到特定容量类型或架构,而给所有工作负载 Pod 都加上 nodeSelector 或 affinity 又过于繁琐甚至不可行。此时可以通过给一个 NodePool 设置较高的.spec.weight,并将其限制到特定容量类型或架构,从而为没有节点配置限制的 Pod 设置节点默认配置:

apiVersion: karpenter.sh/v1 kind: NodePool metadata: name: default spec: weight: 50 template: spec: requirements: - key: karpenter.sh/capacity-type operator: In values: ["spot", "on-demand"] - key: kubernetes.io/arch operator: In values: ["amd64"] --- apiVersion: karpenter.sh/v1 kind: NodePool metadata: name: arm64-specific spec: template: spec: requirements: - key: karpenter.sh/capacity-type operator: In values: ["spot", "on-demand"] - key: kubernetes.io/arch operator: In values: ["arm64"] - key: node.kubernetes.io/instance-type operator: In values: ["a1.large", "a1.xlarge"]

注意:基于 Karpenter 执行 Pod 批处理(batching)与装箱(bin packing)的方式,Karpenter 并不保证总是选择给定需求下的最高优先级 NodePool。例如,如果 Pod 无法用最高优先级的 NodePool 调度,Karpenter 会强制使用较低优先级的 NodePool 创建节点,使该批次中的其他 Pod 也能调度到该节点;如果已有容量可用,kube-scheduler 也会直接调度 Pod,而不是让 Karpenter 置备新节点。

高级调度技巧

基于节点资源的调度

你可能希望 Pod 请求 Kubernetes 原生不作为可调度资源提供的节点属性,例如高性能网络(High Performance Networking)或 NVMe 本地存储。此时可以利用 Karpenter 的 Well-Known 标签,在 NodePool 或工作负载层面通过 Requirements、NodeSelectors 或 Affinities 应用它们。

Pod 示例——要求任意 NVMe 磁盘:

... affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: "karpenter.k8s.aws/instance-local-nvme" operator: "Exists" ...

NodePool 示例:

... requirement: - key: "karpenter.k8s.aws/instance-local-nvme" operator: "Exists" ...

Pod 示例——要求至少 100GB NVMe 磁盘:

... affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: "karpenter.k8s.aws/instance-local-nvme" operator: Gt values: ["99"] ...

NodePool 示例:

... requirement: - key: "karpenter.k8s.aws/instance-local-nvme" operator: Gt values: ["99"] ...

注意:Karpenter 目前还无法在调度 Pod 时考虑 ephemeral-storage 请求——它只是在请求节点属性并顺带获得相应数量的资源。你可能需要调整 CPU 或内存等可调度资源以达到理想的匹配,尤其是在启用了 Consolidation 的情况下。另外,你的 NodeClass 需要支持在存在 NVMe 实例存储时自动格式化并挂载它。

Pod 示例——要求至少 50 Gbps 网络带宽:

... affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: "karpenter.k8s.aws/instance-network-bandwidth" operator: Gt values: ["49999"] ...

NodePool 示例:

... requirement: - key: "karpenter.k8s.aws/instance-network-bandwidth" operator: Gt values: ["49999"] ...

注意:使用Gt/Lt操作符时,请确保 values 低于目标资源的实际标签值(例如用49999匹配 50000 Mbps 的带宽)。

Exists操作符:工作负载隔离

Exists操作符可以在 NodePool 上实现跨节点的工作负载隔离:

apiVersion: karpenter.sh/v1 kind: NodePool spec: template: spec: requirements: - key: company.com/team operator: Exists ...

有了该 requirement,工作负载可以为同一个键(如company.com/team)指定自定义值(如team-ateam-b等)作为必需的nodeAffinitynodeSelector。Karpenter 会根据 Pod 的节点需求,动态地将该键值对应用到它启动的节点上。

如果每个可以匹配该 NodePool 的 Pod 组都在其nodeAffinitynodeSelector中指定了这个键,你就可以根据其值将 Pod 隔离到不同节点上——无需为每个工作负载子集创建独立的 NodePool,即可实现更动态的隔离。例如,为以下 Deployment 提供nodeSelector,即可将各自 Pod 隔离到不同节点。

Team A Deployment:

apiVersion: v1 kind: Deployment metadata: name: team-a-deployment spec: replicas: 5 template: spec: nodeSelector: company.com/team: team-a

Team A Node:

apiVersion: v1 kind: Node metadata: labels: company.com/team: team-a

Team B Deployment:

apiVersion: apps/v1 kind: Deployment metadata: name: team-b-deployment spec: replicas: 5 template: spec: nodeSelector: company.com/team: team-b

Team B Node:

apiVersion: v1 kind: Node metadata: labels: company.com/team: team-b

注意:如果工作负载匹配 NodePool 但未指定标签,Karpenter 会为节点生成一个随机标签。

On-Demand/Spot 比例拆分

利用 Karpenter 为节点分配标签的能力,并结合跨这些标签的拓扑分布,可以实现一种将工作负载按期望比例拆分到 On-Demand 与 Spot 实例上的简易方法。

做法是为 Spot 和 On-Demand 各创建一个 NodePool,它们在一个名为capacity-spread的新标签上使用互不相交(disjoint)的值。下面的示例中,Spot NodePool 提供 4 个唯一值,On-Demand NodePool 提供 1 个值。当我们跨这个新标签均匀分布时,最终会得到 4:1 的 Spot 与 On-Demand 节点比例。

警告:这与带指定比例的拓扑分布并不完全相同。我们是在构造"虚拟域"并均匀分布,而这些"虚拟域"与 Spot/On-Demand 的比例恰好与期望的 Spot/On-Demand 比例一致。例如,若使用下面的示例启动 Pod,Karpenter 会启动带capacity-spread标签值 1、2、3、4、5 的节点,kube-scheduler会跨这些节点均匀调度,从而得到期望的比例。

NodePools:

apiVersion: karpenter.sh/v1 kind: NodePool metadata: name: spot spec: template: spec: requirements: - key: "karpenter.sh/capacity-type" operator: In values: ["spot"] - key: capacity-spread operator: In values: - "2" - "3" - "4" - "5" --- apiVersion: karpenter.sh/v1 kind: NodePool metadata: name: on-demand spec: template: spec: requirements: - key: "karpenter.sh/capacity-type" operator: In values: ["on-demand"] - key: capacity-spread operator: In values: - "1"

工作负载拓扑分布约束:

topologySpreadConstraints: - maxSkew: 1 topologyKey: capacity-spread whenUnsatisfiable: DoNotSchedule labelSelector: ...

结合 NodePool 配置的最佳实践

调度约束最终要与 NodePool 协同生效。综合 nodepools 文档 与 examples/v1 示例目录 中的真实配置(如 general-purpose.yaml、spot.yaml、multiple-arch.yaml),落地时建议注意:

  • requirements 与 Pod 约束取交集:节点选择同时使用 NodePool 与 Pod 的 requirements,二者无交集则不启动节点;未对某 well-known 标签定义 requirement 时,云提供商可用的任意值都可被选择。
  • 实例类型尽量用列表而非单值node.kubernetes.io/instance-typekarpenter.k8s.aws/instance-familyinstance-categoryinstance-generation等一般应作为列表,甚至不定义,以最大化高效装箱的选择空间。
  • minValues 保证灵活性下限:NodePool 的 requirements 支持minValues(ALPHA),要求调度器对某个键至少考虑 N 个唯一值,否则该 NodePool 的调度循环会失败并回退到其他 NodePool。
  • 限制上限用 limitsspec.limits可约束 NodePool 消耗的总资源(cpu/memory/GPU 等),配合weight实现"优先用预留容量,超出即回退"的策略。
  • 推荐最小约束集:官方建议对通用负载至少指定kubernetes.io/archkubernetes.io/oskarpenter.sh/capacity-typekarpenter.k8s.aws/instance-categorykarpenter.k8s.aws/instance-generation等少量 requirements,避免出现异常行为或极端实例类型。

小结

Karpenter 的调度能力覆盖了从最基础的资源请求到复杂的多 NodePool 加权优先级:资源请求决定实例选型的输入,nodeSelector/节点亲和性/拓扑分布/Pod 亲和反亲和/持久卷拓扑等 Kubernetes 标准约束全部受支持,偏好会按权重逐级放宽,污点与容忍配合 NodePool 实现硬件隔离,Exists操作符与capacity-spread标签则提供了工作负载动态隔离与 Spot/On-Demand 比例拆分的轻量方案。将 Pod 约束与 NodePool 的requirementslimitsweight组合使用,即可在保证"Pod 约束必须落在 NodePool 约束范围内"的前提下,构建灵活、可控且成本优化的集群调度策略。本文对应的完整调度概念文档位于 website/content/en/v1.0/concepts/scheduling.md,配套的 NodePool 详解见 website/content/en/v1.0/concepts/nodepools.md。

【免费下载链接】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),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/17 7:17:36

EMC整改实战:时钟抖动与展频SSC参数配置指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/17 7:16:21

SWC替代Babel:构建提速90秒到17秒的实践与避坑指南

大约一年半前,我在一个维护了三年多的中大型前端工程里,第一次把“WHAT”这个标题当成一个正式问题问出了口:这套用Rust重写Web编译链路的SWC平台,到底强在哪、弱在哪、哪些项目适合切、哪些项目切了就是给自己挖坑?项…

作者头像 李华
网站建设 2026/9/17 7:15:42

C语言实现字母异位词检测的哈希计数法

1. 问题背景与核心思路字母异位词(Anagram)是算法面试中的经典问题,指两个字符串包含的字母完全相同但排列顺序不同。LeetCode第242题要求判断给定的两个字符串是否为字母异位词,这个问题看似简单,却涉及字符串处理、哈…

作者头像 李华
网站建设 2026/9/17 7:15:35

PostgreSQL图书管理系统:从E-R建模到第三范式实战

简介:本资源是西南交通大学计算机类专业《数据库原理与设计实验》课程的完整实验报告范本,面向高校数据库课程学习者、实验备考学生及教学参考者,聚焦SQL建表、约束定义、规则绑定、增删改查等核心实践能力训练。压缩包为单个1.11MB的DOCX文档…

作者头像 李华
网站建设 2026/9/17 7:15:26

COMSOL在页岩气钻井液优化中的数值模拟应用

1. 项目背景与核心价值页岩气开发过程中,井壁失稳是导致钻井事故的主要原因之一。去年参与西南某区块页岩气水平井项目时,我们团队就遇到过因钻井液性能不当引发的井壁坍塌问题,直接导致近两周的非生产时间。这个案例让我深刻认识到数值模拟在…

作者头像 李华
网站建设 2026/9/17 7:14:45

LLM智能化测试用例生成实践:从Prompt到RAG的完整指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华