cloudpilot-ai

导航

CA 不够用了?Azure 推 Karpenter + Spot,让 AKS 便宜 80%!

引言

在 2023 年的 KubeCon 北美大会上,微软宣布在 Azure Kubernetes Service(AKS) 中引入 Karpenter 作为 Cluster Autoscaler(CA)的替代方案,并将其命名为 Node Autoprovisioning(NAP)。

虽然 Cluster Autoscaler 一直以来是 AKS/Kubernetes 的默认节点扩缩工具,但其存在诸多限制,促使微软引入 Karpenter。

本文将深入探讨这些挑战,并介绍 Karpenter 如何有效解决它们。

CA vs Karpenter vs CloudPilot AI

太长不看版

如果你想了解这几款工具的更多技术细节、查看 Karpenter 与 Cluster Autoscaler 的架构差异,回复关键词【对比】,获取完整版 PDF 文件。

Cluster Autoscaler 的局限性

以下是 Cluster Autoscaler 进行节点自动扩缩的流程示意图:

  1. 受限于虚拟机规模集( VMSS Groups ):

Cluster Autoscaler 仅支持 AKS 中的虚拟机规模集。

每个 VMSS 由特定类型的 VM 实例组成,具有固定的 VM SKU、硬件规格和 CPU:内存比(例如 Standard D4sv5,4 vCPU 和 16GB 内存)。

  1. 节点池约束:

当新 Pod 需要部署但当前节点容量已满时,Cluster Autoscaler 会尝试创建新的节点,但只能基于现有 VMSS SKU 进行扩容。如果该类型的实例不可用,Pod 将保持待调度状态。

  1. 扩缩能力受限:

Cluster Autoscaler 仅能基于指定的 VMSS 进行弹性扩缩,即使其他虚拟机 SKU 有闲置资源,也无法利用这些 VM SKU 的剩余容量。

Karpenter的优势

Karpenter 是一款开源的 Kubernetes 集群自动扩缩工具,专为优化性能和成本而设计,旨在以灵活、高性能和简洁的方式实现节点的弹性扩展。它比 Cluster Autoscaler 的扩缩速度更快,并且能够直接创建独立节点,无需传统的节点组限制。

Karpenter 的核心特性:

✅高效扩缩:快速弹性扩展 Kubernetes 节点。

✅灵活调度:无需依赖 VMSS 也能启动新节点。

✅成本优化:支持自动补丁更新和 Kubernetes 版本升级,降低总体成本。

✅基于 YAML 配置的 NodePool,可自定义节点类型及调度策略。

Karpenter 的中断管理

中断控制器 (Disruption Controller)负责在 Kubernetes 集群中终止或替换节点,并采用以下三种策略来决定如何处理节点:

1. 节点到期

Karpenter 会为节点设置存活时间(TTL),到期后进行替换。例如:

spec:
  disruption:
    consolidationPolicy: WhenUnderutilized
    expireAfter: 300s  # 

2. 延迟合并

设置中断间隔,控制在触发中断操作前等待的时间。

3. 资源整合

Karpenter 通过分析节点资源使用情况来主动减少集群成本。

📌 支持的策略模式:

  • 无工作负载时:仅移除无 Pod 运行的节点。

  • 资源利用不足时:当节点资源利用率低时,尝试减少或替换节点。

示例:

apiVersion: karpenter.sh/v1beta1
kind: NodePool
metadata:
  name: ondemand
spec:
  disruption:
    consolidationPolicy: WhenEmpty
    consolidateAfter: 60s

在 AKS 上启用 Karpenter

启用 Karpenter 之前,需要满足以下前置条件:

✅ 安装 Az CLI 并更新至 0.5.17 以上版本

✅ 注册 NAP 预览功能

✅ AKS 需使用 Cilium + Overlay 作为网络配置

如果在 Karpenter 安装上有困难,欢迎尝试 Karpenter 的托管云服务 CloudPilot AI(www.cloudpilot.ai),仅需5分钟即可完成安装。

在现有 AKS 集群上启用 Karpenter

确保 AKS 集群启用了 Azure 网络插件,并使用 Cilium 作为网络策略。

关键参数:--node-provisioning-mode Auto,用于将 Karpenter 设为默认节点扩缩工具。

az aks update --name <aks-cluster-name> --resource-group <rg-name> --node-provisioning-mode Auto

创建新的 AKS 集群并启用 Karpenter

az aks create --name <aks-cluster-name> --resource-group <rg-name> \
  --node-provisioning-mode Auto --network-plugin azure --network-plugin-mode overlay --network-dataplane cilium

验证 Karpenter 是否已启用

kubectl api-resources | grep -e aksnodeclasses -e nodeclaims -e nodepools

返回结果示例:

aksnodeclasses      aksnc,aksncs      karpenter.azure.com/v1alpha2      false      AKSNodeClass
nodeclaims                               karpenter.sh/v1beta1             false      NodeClaim
nodepools                                karpenter.sh/v1beta1             false      NodePool

禁用 Cluster Autoscaler

如果希望从 Cluster Autoscaler 切换到 Karpenter,首先需要在 AKS 集群上禁用 Cluster Autoscaler:

az aks update --name <aks-cluster-name> --resource-group <rg-name> --disable-cluster-autoscaler

部署示例应用

为了观察节点自动扩缩的实际运行情况,可以部署一个示例应用:

osama [ ~ ]$ kubectl get nodes
NAME                                STATUS   ROLES   AGE     VERSION
aks-default-h2jxh                   Ready    agent   35m     v1.27.9
aks-nodepool1-41633911-vmss000000   Ready    agent   3d19h   v1.27.9

调整 Vote 应用副本数量以触发扩容事件

osama [ ~ ]$ kubectl scale deployment azure-vote-front --replicas=12 -n karpenter-demo-ns
^[[Adeployment.apps/azure-vote-front scaled
osama [ ~ ]$ kubectl scale deployment azure-vote-back --replicas=12 -n karpenter-demo-ns
deployment.apps/azure-vote-back scaled

使用以下 kubectl 命令检查 Karpenter 自动扩容情况

kubectl get events -A --field-selector source=karpenter --sort-by='.lastTimestamp' -n 10
NAMESPACE           LAST SEEN   TYPE     REASON                  OBJECT                                  MESSAGE
default             50m         Normal   Unconsolidatable        nodeclaim/default-95f54                 SpotToSpotConsolidation is disabled, can't replace a spot node with a spot node
default             50m         Normal   Unconsolidatable        node/aks-default-95f54                  SpotToSpotConsolidation is disabled, can't replace a spot node with a spot node
default             38m         Normal   DisruptionBlocked       nodepool/default                        No allowed disruptions due to blocking budget
default             5m33s       Normal   Unconsolidatable        nodeclaim/default-h2jxh                 Can't remove without creating 2 candidates
default             5m33s       Normal   Unconsolidatable        node/aks-default-h2jxh                  Can't remove without creating 2 candidates
default             2m12s       Normal   DisruptionBlocked       nodepool/system-surge                   No allowed disruptions due to blocking budget
karpenter-demo-ns   63s         Normal   Nominated               pod/azure-vote-front-6855444955-bnq7p   Pod should schedule on: nodeclaim/default-mrh7w
karpenter-demo-ns   63s         Normal   Nominated               pod/azure-vote-front-6855444955-gbwk6   Pod should schedule on: nodeclaim/default-mrh7w
karpenter-demo-ns   63s         Normal   Nominated               pod/azure-vote-front-6855444955-l2bgj   Pod should schedule on: nodeclaim/default-mrh7w
karpenter-demo-ns   63s         Normal   Nominated               pod/azure-vote-front-6855444955-nvc56   Pod should schedule on: nodeclaim/default-mrh7w
karpenter-demo-ns   63s         Normal   Nominated               pod/azure-vote-front-6855444955-22glj   Pod should schedule on: nodeclaim/default-mrh7w
karpenter-demo-ns   63s         Normal   Nominated               pod/azure-vote-front-6855444955-sxdl6   Pod should schedule on: nodeclaim/default-mrh7w
karpenter-demo-ns   63s         Normal   Nominated               pod/azure-vote-front-6855444955-t69w4   Pod should schedule on: nodeclaim/default-mrh7w

自定义 Karpenter 配置

Karpenter 在 Kubernetes 中引入了一种新的资源类型:NodePool,用于管理和优化节点调度。

  • 自定义 NodePools:指定特定的 VM 系列、VM 家族,或自定义 CPU 与内存比例。

  • 基于特性选择节点:支持 GPU 加速或网络加速等功能。

  • 定义 CPU 架构:根据特定工作负载需求,选择 ARM 或 AMD 架构。

  • 构建高可用节点架构:通过配置可用区拓扑提高容灾能力。

  • 限制节点级别的 CPU 和内存使用:控制单个节点可分配的 CPU 和内存资源。

以下是 Karpenter 默认的 NodePool 配置 YAML,其中包含:

  • 节点 SKU 类型和容量配置

  • CPU:内存比例限制

  • 在多个 NodePools 存在时的调度权重(Weight)

apiVersion: karpenter.sh/v1beta1
kind: NodePool
metadata:
  name: default
spec:
  disruption:
    consolidationPolicy: WhenUnderutilized
    expireAfter: 10s
  template:
    spec:
      nodeClassRef:
        name: default

      # Requirements that constrain the parameters of provisioned nodes.
      # These requirements are combined with pod.spec.affinity.nodeAffinity rules.
      # Operators { In, NotIn, Exists, DoesNotExist, Gt, and Lt } are supported.
      # https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#operators
      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:
        - ondemand
      - key: karpenter.azure.com/sku-family
        operator: In
        values:
        - E
        - D
      - key: karpenter.azure.com/sku-name
        operator: In
        values:
        - Standard_E2s_v5
        - Standard_D4s_v3
limits:
  cpu: "1000"
  memory: 1000Gi
weight: 100

使用 Spot 节点与 Karpenter

  • 在 AKS-Vote 示例应用中添加 Toleration(如 "karpenter.sh/disruption:NoSchedule"),该 Toleration 在 AKS 集群预配 Spot 节点时默认存在。

  • 请参考我的 GitHub 仓库,获取应用的 YAML 配置和示例 NodePool 配置。( https://github.com/Osshaikh/Karpenter-AKS-Spot )

spec:
      nodeSelector:
        "kubernetes.io/os": linux
      tolerations:
      - key: "kubernetes.azure.com/scalesetpriority"
        operator: "Equal"
        value: "spot"
        effect: "NoSchedule"
      containers:
      - name: azure-vote-front
        image: mcr.microsoft.com/azuredocs/azure-vote-front:v1
  • 缩减应用副本数量,以便 Karpenter 逐步驱逐现有的 On-Demand 节点,并用 Spot 节点替换:
osama [ ~/karpenter ]$ kubectl get nodes
NAME                                STATUS   ROLES   AGE     VERSION
aks-nodepool1-41633911-vmss000000   Ready    agent   3d21h   v1.27.9
aks-nodepool1-41633911-vmss00000b   Ready    agent   24m     v1.27.9

osama [ ~/karpenter ]$ kubectl get pods -n karpenter-demo-ns -o wide
No resources found in karpenter-demo-ns namespace.

osama [ ~/karpenter ]$ kubectl scale deployment azure-vote-back --replicas=10 -n karpenter-demo-ns
deployment.apps/azure-vote-back scaled
osama [ ~/karpenter ]$ kubectl scale deployment azure-vote-front --replicas=10 -n karpenter-demo-ns
deployment.apps/azure-vote-front scaled
osama [ ~/karpenter ]$
  • 部署并扩展 Vote 应用副本,使 Karpenter 根据 NodePool 配置启动 Spot 节点,并在 Toleration 验证后将 Pod 调度到 Spot 节点上。

  • Karpenter 启动新的 Spot 节点,并将该节点指定用于调度 Vote 示例应用。

osama [ ~/karpenter ]$ kubectl get events -A --field-selector source=karpenter --sort-by='.lastTimestamp'
NAMESPACE           LAST SEEN   TYPE      REASON                       OBJECT                                    MESSAGE
karpenter-demo-ns   104s        Normal    Nominated                    pod/azure-vote-back-687ddb67bd-pz8sp      Pod should schedule on: nodeclaim/default-52gbg
karpenter-demo-ns   104s        Normal    Nominated                    pod/azure-vote-back-687ddb67bd-ckdcq      Pod should schedule on: nodeclaim/default-52gbg
karpenter-demo-ns   104s        Normal    Nominated                    pod/azure-vote-back-687ddb67bd-v9nqj      Pod should schedule on: nodeclaim/default-52gbg
karpenter-demo-ns   104s        Normal    Nominated                    pod/azure-vote-back-687ddb67bd-vswvs      Pod should schedule on: nodeclaim/default-52gbg
karpenter-demo-ns   104s        Normal    Nominated                    pod/azure-vote-back-687ddb67bd-lnxmp      Pod should schedule on: nodeclaim/default-52gbg
karpenter-demo-ns   104s        Normal    Nominated                    pod/azure-vote-back-687ddb67bd-jc2jz      Pod should schedule on: nodeclaim/default-52gbg
karpenter-demo-ns   104s        Normal    Nominated                    pod/azure-vote-back-687ddb67bd-hwnbh      Pod should schedule on: nodeclaim/default-52gbg
karpenter-demo-ns   104s        Normal    Nominated                    pod/azure-vote-back-687ddb67bd-r7msb      Pod should schedule on: nodeclaim/default-52gbg
karpenter-demo-ns   104s        Normal    Nominated                    pod/azure-vote-back-687ddb67bd-96lm9      Pod should schedule on: nodeclaim/default-52gbg
karpenter-demo-ns   104s        Normal    Nominated                    pod/azure-vote-back-687ddb67bd-5qcvk      Pod should schedule on: nodeclaim/default-52gbg
default             1s          Normal    DisruptionLaunching          nodeclaim/default-bkz6c                   Launching NodeClaim: Expiration/Replace
default             1s          Normal    DisruptionWaitingReadiness   nodeclaim/default-bkz6c                   Waiting on readiness to continue disruption
default             1s          Normal    DisruptionBlocked            nodepool/system-surge                     No allowed disruptions due to blocking budget
default             1s          Normal    DisruptionWaitingReadiness   nodeclaim/default-5vp7x                   Waiting on readiness to continue disruption
default             1s          Normal    DisruptionLaunching          nodeclaim/default-5vp7x                   Launching NodeClaim: Expiration/Replace

配置多个NodePools

  • 为 Spot 和 On-Demand 资源分别配置 NodePool:

使用 E 系列 VM(如 "Standard_E2s_v5")的 Spot 节点和使用 D 系列 VM(如 "Standard_D4s_v5")的 On-Demand 节点。

  • 设置 NodePool 的优先级

在多 NodePool 场景中,每个 NodePool 需要配置 Weight 属性,Karpenter 会优先选择权重最高的 NodePool 进行调度。

📌 在此配置中,Spot 节点的权重为 100,On-Demand 节点的权重为 60:

osama [ ~ ]$ kubectl get nodepool default -o yaml
apiVersion: karpenter.sh/v1beta1
kind: NodePool
metadata:
  name: default
spec:
  disruption:
    budgets:
    - nodes: 100%
    consolidationPolicy: WhenUnderutilized
    expireAfter: 720h
  template:
    spec:
      nodeClassRef:
        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.azure.com/sku-family
        operator: In
        values:
        - B
      - key: karpenter.azure.com/sku-name
        operator: In
        values:
        - Standard_B2s_v2
  weight: 100
  • 如果未指定具体的 SKU 名称,Karpenter 将默认考虑整个 VM 系列。

  • 要验证示例 VoteApp 是否运行在 Spot 节点上,请使用以下命令:

  • 输出结果应显示节点的容量类型为 "spot":

osama [ ~ ]$ kubectl get pods -n karpenter-demo-ns -o wide
NAME                                READY   STATUS    RESTARTS   AGE   IP             NODE                NOMINATED NODE   READINESS GATES
azure-vote-back-687ddb67bd-w7ghm    1/1     Running   0          63m   10.244.3.11    aks-default-5cr5f   <none>           <none>
azure-vote-front-6855444955-64558   1/1     Running   0          63m   10.244.3.168   aks-default-5cr5f   <none>           <none>
osama [ ~ ]$ kubectl describe node aks-default-5cr5f | grep karpenter.sh
                    karpenter.sh/capacity-type=spot
                    karpenter.sh/initialized=true
                    karpenter.sh/nodepool=default
                    karpenter.sh/registered=true
                    karpenter.sh/nodepool-hash: 12393960163388511505
                    karpenter.sh/nodepool-hash-version: v2

模拟 Spot 节点驱逐

要测试 Spot 节点的驱逐情况,可以使用 Azure CLI 模拟 Spot 节点被回收:

osama [ ~ ]$ az vm simulate-eviction --resource-group MC_aks-lab_aks-karpenter_eastus --name aks-default-5cr5f
osama [ ~ ]$ date
Tue May 21 06:20:02 PM IST 2024
  • 使用以下 curl 命令监控 VoteApp 的可用性:
while true; do echo "$(date) $(curl -s -v -o /dev/null -w 'HTTP %{http_code}\n' http://voteapp.com 2>&1 | grep 'HTTP')"; sleep 2; done
  • 在运行 Spot 模拟驱逐后,现有节点将被标记为终止,Karpenter 将自动创建新的 Spot 节点来调度 VoteApp 的 Pods。 在不到一分钟内,VoteApp 应该会恢复运行,并开始返回 HTTP 200 状态码。
  root@MININT-8C81HDE:/home/osamaex while true; do echo "$(date) $(curl -s -v -o /dev/null -w 'HTTP %{http_code}\n' http://voteapp.com 2>&1 | grep 'HTTP')"; sleep 2; done
    Tue May 21 18:20:04 IST 2024 > GET / HTTP/1.1
    < HTTP/1.1 200 OK
    HTTP 200
    Tue May 21 18:20:07 IST 2024 > GET / HTTP/1.1
    < HTTP/1.1 200 OK
    HTTP 200
    Tue May 21 18:20:09 IST 2024 > GET / HTTP/1.1
    < HTTP/1.1 200 OK
    HTTP 200
    Tue May 21 18:20:12 IST 2024 HTTP 000  $Failure-Alert
    Tue May 21 18:21:14 IST 2024 > GET / HTTP/1.1
    < HTTP/1.1 200 OK                      $Successful-Response
    HTTP 200
    Tue May 21 18:22:58 IST 2024 > GET / HTTP/1.1
    < HTTP/1.1 200 OK
    HTTP 200
  • 查看 Karpenter 记录的事件日志:
       kubectl get events -A --field-selector source=karpenter --sort-by='.lastTimestamp'
  • Karpenter 记录的事件日志显示 Spot 节点被替换为 On-Demand 节点的过程:
  osama [ ~ ]$ 
    NAMESPACE           LAST SEEN   TYPE      REASON           OBJECT                                  MESSAGE
    default             23s         Warning   FailedDraining   node/aks-default-5cr5f                  Failed to drain node, 10 pods are waiting to be evicted
    karpenter-demo-ns   22s         Normal    Evicted          pod/azure-vote-back-687ddb67bd-w7ghm    Evicted pod
    karpenter-demo-ns   22s         Normal    Evicted          pod/azure-vote-front-6855444955-64558   Evicted pod
    karpenter-demo-ns   21s         Normal    Nominated        pod/azure-vote-back-687ddb67bd-tb2pv    Pod should schedule on: nodeclaim/default-6zkkl
    karpenter-demo-ns   21s         Normal    Nominated        pod/azure-vote-front-6855444955-7wzss   Pod should schedule on: nodeclaim/default-6zkkl
  • 验证 Pods 是否已运行在新的 Spot 节点上:
kubectl get pods -n karpenter-demo-ns -o wide
NAME                                READY   STATUS    RESTARTS   AGE   IP             NODE                NOMINATED NODE   READINESS GATES
azure-vote-back-687ddb67bd-tb2pv    1/1     Running   0          18m   10.244.2.103   aks-default-6zkkl   <none>           <none>
azure-vote-front-6855444955-7wzss   1/1     Running   0          18m   10.244.2.47    aks-default-6zkkl   <none>           <none>

通过使用预留实例节省成本

NodePool 配置允许您指定不同的 VM 系列以及多个 VM SKU。

您可以创建一个独立的 NodePool,设置最高权重,并通过karpenter.azure.com/sku-name或 karpenter.azure.com/sku-family 参数,指定所有预留实例的 VM SKU 家族或具体 SKU 名称。

 spec:
      nodeClassRef:
        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:
        - on-demand
      - key: karpenter.azure.com/sku-family
        operator: In
        values:
        - D
      - key: karpenter.azure.com/sku-name
        operator: In
        values:
        - [Standard_D2s_v3, Standard_D4s_v3, Standard_D8s_v3, Standard_D16s_v3, Standard_D32s_v3, Standard_D64s_v3, Standard_D96s_v3]
  weight: 90

结论

在 AKS 引入 Karpenter 标志着节点扩缩效率、灵活性和成本优化方面的一次重大升级。

Karpenter 克服了 Cluster Autoscaler 的局限性,引入了动态且快速的节点预置能力,为 Kubernetes 集群管理提供了更强大、更高效的解决方案。

Karpenter 在支持不同 VM 类型、提高扩缩能力以及优化成本方面展现出了极大的灵活性,使其成为 Kubernetes 集群管理的重要利器。

通过 Karpenter,企业可以实现更敏捷、更具成本效益的 Kubernetes 部署,提升集群的响应速度和资源利用率。

如果你正在寻找 Karpenter 的相关实践教程,关注「 CloudPilot AI 」,我们将持续带来更多干货💡

posted on 2025-03-07 13:42  CloudPilotAI  阅读(256)  评论(0)    收藏  举报