共用方式為

节点自动预配中断策略

本文介绍如何为 Azure Kubernetes 服务(AKS)节点自动预配(NAP)配置节点中断策略,以优化资源利用率和成本效益。

中断配置示例

下面是 NodePool 的典型中断配置,它平衡成本优化与稳定性:

apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
  name: default
spec:
  disruption:
    # Consolidate underutilized nodes to optimize costs
    consolidationPolicy: WhenEmptyOrUnderutilized
    
    # Wait 30 seconds before consolidating
    consolidateAfter: 30s
    
    # Replace nodes every 7 days to get fresh instances
    expireAfter: 168h
    
    # Rate limit disruptions
    budgets:
    - nodes: "10%"        # Allow 10% of nodes to be disrupted at once
    - nodes: "0"          # Block all disruptions during business hours
      schedule: "0 9 * * 1-5"
      duration: 8h

节点中断概述

当节点上的工作负荷缩减时,节点自动预配使用节点池规范上的中断规则来确定何时以及如何删除这些节点,并可能重新计划工作负荷以提高效率。 节点自动预配主要使用 合并 来删除或替换节点以实现最佳 Pod 放置。

节点自动预配可以自动控制何时应根据规范优化节点,或者可以使用手动删除节点 kubectl delete node

控制流

Karpenter 在每个节点上设置 Kubernetes 终结器 ,并声明它预配的节点。 终止控制器在删除基础 NodeClaim 之前会阻止删除节点对象并清空节点。

中断方法

NAP 会自动发现符合中断条件的节点,并在需要时启动替换项。 可以通过以下方法触发中断:

  • 自动化方法:Expiration、Drift 和合并
  • 手动方法:通过 kubectl 删除节点
  • 外部系统:删除对节点对象的请求

自动中断方法

过期日期

节点在达到 NodePool 值 spec.disruption.expireAfter 中指定的年龄后标记为过期和中断。

spec:
  disruption:
    expireAfter: 24h  # Expire nodes after 24 hours

固结

NAP 的工作原理是通过确定何时确定群集成本来主动降低群集成本:

  • 节点可以删除,因为它们为空
  • 节点可以在其他节点上运行时删除节点
  • 节点可以替换为价格较低的变体

合并有三种按顺序执行的机制:

  1. 空节点合并 - 并行删除完全空的节点
  2. 多节点合并 - 删除多个节点,可能启动单个替换
  3. 单节点合并 - 删除任何单个节点,可能启动替换

Drift

Drift 处理对 NodePool/AKSNodeClass 的更改。 对于 Drift,NodePool/AKSNodeClass 中的值以与设置的相同方式反映在 NodeClaimTemplateSpec/AKSNodeClassSpec 中。 如果自己的 NodePool/AKSNodeClass 中的值与 NodeClaim 中的值不匹配,则 NodeClaim 将检测为偏移。 与 Pod 的上游 deployment.spec.template 关系类似,Karpenter 使用 NodeClaimTemplateSpec 的哈希对拥有的 NodePool 和 AKSNodeClass 进行批注,以检查偏移。 某些特殊情况是通过 Karpenter 或 CloudProvider 接口发现的,由 NodeClaim/Instance/NodePool/AKSNodeClass 更改触发。

偏移的特殊情况

在特殊情况下,偏移可以对应于多个值,并且必须以不同的方式进行处理。 解析字段上的偏移可以创建不更改自定义资源定义(CRD)或 CRD 更改不会导致偏移的情况。 例如,如果 NodeClaim 具有 node.kubernetes.io/instance-type: Standard_D2s_v3,并且要求从 node.kubernetes.io/instance-type In [Standard_D2s_v3] 更改为 node.kubernetes.io/instance-type In [Standard_D2s_v3, Standard_D4s_v3],则 NodeClaim 不会偏移,因为它的值仍与新要求兼容。 相反,如果 NodeClaim 正在使用 NodeClaim imageFamily,但 spec.imageFamily 字段已更改,则 Karpenter 将检测 NodeClaim 作为偏移并旋转节点以满足该规范

NodePool
Fields
spec.template.spec.requirements
AKSNodeClass

一些示例情况:

Fields
spec.vnetSubnetID
spec.imageFamily
VNet 子网 ID 偏移

重要

spec.vnetSubnetID 字段可以触发偏移检测,但将此字段从一个有效子网修改到另 一个有效子网不是受支持的作。 此字段是唯一可变的,用于在初始配置期间提供转义槽来更正无效或格式不正确的子网标识符。

与通过 ARM 模板创建的传统 AKS NodePools 不同,Karpenter 应用自定义资源定义(CRD),无需 ARM 提供的扩展验证即可立即预配节点。 客户负责了解其群集的无类 Inter-Domain 路由(CIDR)范围,并确保配置 vnetSubnetID时不会发生冲突。

验证差异

  • ARM 模板 NodePools:包括全面的无分类 Inter-Domain 路由(CIDR)冲突检测和网络验证
  • Karpenter CRD:在不自动验证的情况下立即应用更改 - 需要客户尽职尽责

客户责任:在修改 vnetSubnetID之前,请验证:

  • 自定义子网与群集 Pod CIDR、服务 CIDR 或 Docker Bridge CIDR 不冲突
  • 足以满足缩放要求的 IP 地址容量
  • 正确的网络连接和路由配置

支持的用例:仅修复无效的子网标识符(ID)

  • 更正格式不正确的子网引用,防止节点预配
  • 更新指向不存在或无法访问的子网的子网标识符

不支持的用例:有效子网之间的子网迁移

  • 在子网之间移动节点进行网络重组
  • 出于容量或性能原因更改子网配置

支持策略:Microsoft不支持通过 vnetSubnetID 修改从子网迁移到子网迁移的问题。

行为字段

行为字段被视为 NodePool 上的过度拱形设置,以指示 Karpenter 的行为方式。 这些字段与 NodeClaim 或实例上的设置不对应。 用户设置这些字段来控制 Karpenter 的预配和中断逻辑。 由于这些字段不映射到 NodeClaims 的所需状态, 因此不考虑 Drift 的行为字段

NodePool
Fields
spec.weight
spec.limits
spec.disruption.*

阅读 《偏移设计》 了解详细信息。

如果 NodeClaim 偏离了自己的 NodePool,则 Karpenter 会在 NodeClaims 上添加 Drifted 状态条件。 如果以下任一情况,Karpenter 还会删除 Drifted 状态条件:

  • Drift功能门未启用,但 NodeClaim 已偏移,Karpenter 将删除状态条件。
  • NodeClaim 不会偏移,但状态条件为 Karpenter 将其删除。

中断配置

通过 NodePool 部分 spec.disruption 配置中断:

spec:
  disruption:
    # Consolidation policy
    consolidationPolicy: WhenEmptyOrUnderutilized | WhenEmpty
    
    # Time to wait after discovering consolidation opportunity
    consolidateAfter: 30s
    
    # Node expiration time
    expireAfter: Never
    
    # Disruption budgets for rate limiting
    budgets:
    - nodes: "20%"
    - nodes: "5"
      schedule: "@daily"
      duration: 10m

合并策略

WhenEmptyOrUnderutilized

NAP 会考虑所有节点进行合并,并在节点未充分利用或为空时尝试删除或替换节点

spec:
  disruption:
    consolidationPolicy: WhenEmptyOrUnderutilized
    expireAfter: Never

WhenEmpty

NAP 仅考虑用于合并不包含工作负荷 Pod 的节点:

spec:
  disruption:
    consolidationPolicy: WhenEmpty
    consolidateAfter: 30s

终止宽限期

配置 Karpenter 等待 Pod 正常终止的时间。 此设置优先于 Pod 的终止GracePeriodSeconds,并绕过 PodDisruptionBudgets 和 karpenter.sh/do-not-disrupt 批注

apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
  name: default
spec:
  template:
    spec:
      terminationGracePeriod: 30s

中断预算

可以通过 NodePool 的速率限制 Karpenter 的 spec.disruption.budgets中断。 如果未定义,则 Karpenter 默认使用一个预算 nodes: 10%。 预算会考虑出于任何原因删除的节点。 他们只会阻止卡彭特通过过期、偏移、空虚和巩固来自愿中断。

节点预算

计算预算是否阻止节点中断时,Karpenter 计算 NodePool 拥有的总节点数。 然后,它减去正在删除的节点和未读的节点。

如果预算配置了百分比值,例如20%,Karpenter 将计算允许的中断数。allowed_disruptions = roundup(total * percentage) - total_deleting - total_notready

对于 NodePool 中的多个预算,Karpenter 采用每个预算的最小值(最严格)。

预算示例

以下具有三个预算的 NodePool 定义了以下要求:

apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
  name: default
spec:
  disruption:
    consolidationPolicy: WhenEmptyOrUnderutilized
    expireAfter: 720h # 30 * 24h = 720h
    budgets:
    - nodes: "20%"      # Allow 20% of nodes to be disrupted
    - nodes: "5"        # Cap at maximum 5 nodes
    - nodes: "0"        # Block all disruptions during maintenance window
      schedule: "@daily"
      duration: 10m
  • 第一个预算允许中断该 NodePool 拥有的 20 个节点%
  • 第二个预算充当上限,仅允许在超过 25 个节点时发生 5 次中断
  • 最后一个预算会阻止每天前 10 分钟的中断

计划和持续时间

Schedule 使用具有特殊宏的 cron 作业语法,例如@yearly,、@daily@monthly@weekly@hourly、。 持续时间 允许复合持续时间,例如 10h5m30m160h。 持续时间和计划必须一起定义。

预算配置示例

维护时段预算

在工作时间防止中断:

budgets:
- nodes: "0"
  schedule: "0 9 * * 1-5"  # 9 AM Monday-Friday
  duration: 8h             # For 8 hours

仅限周末的中断

仅允许周末中断:

budgets:
- nodes: "50%"
  schedule: "0 0 * * 6"    # Saturday midnight
  duration: 48h            # All weekend
- nodes: "0"               # Block all other times

逐步推出预算

允许增加中断率:

budgets:
- nodes: "1"
  schedule: "0 2 * * *"    # 2 AM daily
  duration: 2h
- nodes: "3"
  schedule: "0 4 * * *"    # 4 AM daily
  duration: 4h

手动节点中断

节点删除

可以使用 kubectl 手动删除节点:

# Delete a specific node
kubectl delete node $NODE_NAME

# Delete all NAP-managed nodes
kubectl delete nodes -l karpenter.sh/nodepool

# Delete nodes from a specific nodepool
kubectl delete nodes -l karpenter.sh/nodepool=$NODEPOOL_NAME

NodePool 删除

NodePool 通过所有者引用拥有 NodeClaims。 NAP 在删除拥有的 NodePool 时,通过级联删除正常终止节点。

中断控制

Pod 级控件

通过设置 karpenter.sh/do-not-disrupt: "true" 批注阻止 NAP 中断某些 Pod:

apiVersion: apps/v1
kind: Deployment
spec:
  template:
    metadata:
      annotations:
        karpenter.sh/do-not-disrupt: "true"

此批注可防止以下情况发生自愿中断:

  • 固结
  • Drift
  • 过期日期

节点级控件

阻止 NAP 中断特定节点:

apiVersion: v1
kind: Node
metadata:
  annotations:
    karpenter.sh/do-not-disrupt: "true"

NodePool 级控件

禁用 NodePool 中所有节点的中断:

apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
  name: default
spec:
  template:
    metadata:
      annotations:
        karpenter.sh/do-not-disrupt: "true"

后续步骤