Skip to content

Kubernetes Karpenter 节点自动供给

Karpenter 是 Kubernetes 新一代的节点自动供给(Node Auto Provisioning)解决方案,专为现代云原生工作负载设计,特别是 GPU 推理等需要快速弹性扩展的场景。相比传统的 Cluster Autoscaler,Karpenter 提供了更快的节点启动速度、更灵活的实例选择和更精细的资源调度能力。在 GPU 推理服务场景中,Karpenter 是实现 Pod 和节点协同弹性的关键组件。

Karpenter 核心概念

什么是 Karpenter

Karpenter 是一个开源的 Kubernetes 节点供给器,它能够根据 Pending Pod 的需求自动创建最合适的节点。与 Cluster Autoscaler 不同,Karpenter 直接与云厂商 API 交互,可以更快速地响应资源需求变化,并且提供了更灵活实例类型选择策略。

Karpenter 的工作原理可以概括为以下几个核心概念。首先是 NodePool(节点池),NodePool 定义了节点的配置模板,包括实例类型、标签、污点、容量限制等。当集群中有 Pending Pod 无法被调度时,Karpenter 会根据 Pod 的资源请求和 NodePool 的配置自动创建合适的节点。其次是 Consolidation(整合),Karpenter 支持自动整合空闲节点,通过合并或 consolidation 来优化成本。第三是 Capacity Types(容量类型),Karpenter 支持按需(On-Demand)和竞价(Spot)实例的混合使用。

Karpenter 与 Cluster Autoscaler 的对比

Cluster Autoscaler 是 Kubernetes 最早期的节点自动供给方案,而 Karpenter 是后起之秀,设计上更加现代化。在 GPU 推理场景中,Karpenter 的优势更加明显。

从响应速度来看,Cluster Autoscaler 通常需要 30 秒到数分钟,而 Karpenter 可以更快地在 10 秒内开始节点创建。从实例选择来看,Cluster Autoscaler 受限于节点组配置,选择不够灵活,而 Karpenter 可以根据 Pod 请求动态选择最合适的实例类型。从成本优化来看,Cluster Autoscaler 支持基础的 Spot 实例,而 Karpenter 支持更细粒度的成本控制。从扩展性来看,Cluster Autoscaler 扩展性有限,而 Karpenter 支持更多云厂商和配置选项。

为什么 GPU 推理需要 Karpenter

GPU 推理服务的弹性不仅仅依赖于 Pod 级别的扩缩容,更关键的是节点级别的弹性。当推理请求增加时,Pod 可以通过 HPA 或 KEDA 扩容,但如果节点上没有可用的 GPU,Pod 会一直处于 Pending 状态,用户请求仍然会超时。这就是 GPU 推理服务中最常见的「假扩容」问题。

Karpenter 能够很好地解决这个问题。当 Pending Pod 请求 GPU 资源时,Karpenter 会自动识别并快速创建带有合适 GPU 的节点。这种 Pod 和节点的协同弹性是 GPU 推理服务稳定运行的基础。

Karpenter 核心概念详解

NodePool

NodePool 是 Karpenter 的核心资源,定义了如何创建节点。NodePool 的配置非常灵活,可以根据不同的工作负载需求创建不同的节点类型。

yaml
apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
  name: gpu-inference
spec:
  template:
    metadata:
      labels:
        workload-type: online-inference
        accelerator: nvidia-l40s
    spec:
      requirements:
        # 架构要求
        - key: kubernetes.io/arch
          operator: In
          values: [amd64]
        # 实例类别(g 表示 GPU 实例)
        - key: karpenter.k8s.aws/instance-category
          operator: In
          values: [g]
        # GPU 数量
        - key: karpenter.k8s.aws/instance-gpu-count
          operator: In
          values: ["1", "4", "8"]
        # GPU 类型
        - key: karpenter.k8s.aws/instance-gpu-name
          operator: In
          values: [l40s, a10g, h100]
        # 容量类型
        - key: karpenter.k8s.aws/instance-capacity-type
          operator: In
          values: [on-demand, spot]
      # 污点配置,防止普通 Pod 调度到 GPU 节点
      taints:
        - key: nvidia.com/gpu
          effect: NoSchedule
      # 节点标签
      nodeClassRef:
        group: karpenter.k8s.aws
        kind: EC2NodeClass
        name: gpu-nodeclass
  # 破坏策略(节点回收策略)
  disruption:
    consolidationPolicy: WhenEmptyOrUnderutilized
    consolidateAfter: 300s
    # 节点过期时间
    expireAfter: 720h
  # 资源限制
  limits:
    cpu: "1000"
    memory: 1000Ti
    nvidia.com/gpu: "100"

这个配置定义了一个用于在线推理的 GPU 节点池,要求节点带有 NVIDIA GPU(L40s/A10g/H100),使用 on-demand 或 spot 实例,节点上有 GPU 污点防止普通 Pod 占用。

EC2NodeClass

EC2NodeClass 定义了如何与 AWS API 交互创建节点:

yaml
apiVersion: karpenter.k8s.aws/v1
kind: EC2NodeClass
metadata:
  name: gpu-nodeclass
spec:
  # IAM 角色(需要 Karpenter 控制器权限)
  role: KarpenterNodeRole
  # AMI 配置
  amiFamily: AL2023
  # 标签配置
  tags:
    Environment: production
    workload-type: inference
  # 用户数据(节点初始化脚本)
  userData: |
    #!/bin/bash
    echo "Hello from Karpenter user data"
  # 块设备配置
  blockDeviceMappings:
    - deviceName: /dev/sda1
      ebs:
        volumeSize: 100Gi
        volumeType: gp3
  # 安全组配置
  securityGroupSelector:
    karpenter.sh/discovery: gpu-cluster
  # 子网配置
  subnetSelector:
    karpenter.sh/discovery: gpu-cluster

Consolidation 策略

Karpenter 支持多种节点整合策略,用于优化成本:

  • WhenEmpty:当节点上没有 Pod 时删除节点
  • WhenUnderutilized:当节点资源利用率低时删除或整合节点
  • WhenEmptyOrUnderutilized:结合以上两种策略
  • Never:不自动删除节点
yaml
disruption:
  consolidationPolicy: WhenEmptyOrUnderutilized
  consolidateAfter: 300s  # 等待 5 分钟确认

多个 NodePool 的设计

建议在 GPU 推理场景中设置多个 NodePool:

yaml
# 核心在线池 - 使用稳定实例
apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
  name: gpu-core
spec:
  template:
    spec:
      requirements:
        - key: karpenter.k8s.aws/instance-capacity-type
          operator: In
          values: [on-demand]
      taints:
        - key: workload-type
          value: core
          effect: NoSchedule
  limits:
    nvidia.com/gpu: "50"
---
# 弹性池 - 允许使用 Spot
apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
  name: gpu-elastic
spec:
  template:
    spec:
      requirements:
        - key: karpenter.k8s.aws/instance-capacity-type
          operator: In
          values: [spot]
      taints:
        - key: workload-type
          value: elastic
          effect: NoSchedule
  limits:
    nvidia.com/gpu: "100"

Karpenter 在 GPU 推理中的应用

GPU 节点池设计

建议至少拆成三类节点池:

节点池用途实例类型保留容量
在线核心池承接高优先级在线推理On-Demand保留基础容量
弹性池应对流量峰值On-Demand + Spot按需补货
低价池批处理或可降级流量Spot按需

标签设计上建议明确:

yaml
nodeSelector:
  workload-type: online-inference
  accelerator: nvidia-l40s
tolerations:
  - key: nvidia.com/gpu
    operator: Exists
    effect: NoSchedule

与 HPA/KEDA 协同

Karpenter 与 HPA/KEDA 的协同是 GPU 推理弹性扩展的关键:

  1. 流量增加 → KEDA/HPA 检测到指标超阈值
  2. KEDA/HPA 增加 Pod 副本数
  3. 新 Pod 创建但 Pending(无 GPU 节点)
  4. Karpenter 检测到 Pending Pod
  5. Karpenter 根据 NodePool 配置启动新 GPU 节点
  6. 节点 Ready 后,Pod 被调度并运行
  7. 服务 Ready 后接收流量

这种协同需要确保:HPA/KEDA 的 maxReplicas 不会超过 Karpenter 的 limits;节点启动时间需要考虑在扩容延迟中;预留核心池的基础容量。

镜像预拉取和模型缓存

为了减少冷启动时间,建议配合镜像预拉取和模型缓存:

yaml
# DaemonSet 预拉取镜像
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: image-prepull
spec:
  template:
    spec:
      containers:
      - name: prepull
        image: registry.example.com/llm-inference:v1.0.0
        command: ["sleep", "infinity"]

模型缓存可以使用 localPV 或共享存储(如 EFS/FSx for Lustre)。

Karpenter 安装

bash
# 使用 Helm 安装
helm repo add karpenter https://刻 Karpenter.github.io/charts
helm install karpenter karpenter/karpenter \
  --namespace karpenter \
  --create-namespace \
  --set serviceAccount.create=true \
  --set settings.featureGates.disruptionGracePeriod=30s \
  --set controller.resources.requests.cpu=1 \
  --set controller.resources.requests.memory=1Gi \
  --set controller.resources.limits.cpu=1 \
  --set controller.resources.limits.memory=1Gi

Karpenter 监控和调试

查看节点状态

bash
# 查看 Karpenter 管理的节点
kubectl get nodes -l karpenter.sh/nodepool

# 查看 NodePool 状态
kubectl get nodepool

# 查看 Karpenter 控制器日志
kubectl logs -n karpenter -l app.kubernetes.io/name=karpenter

常见问题排查

问题:节点创建失败

可能原因:AWS 配额不足、IAM 权限不足、可用区资源不足。检查 Karpenter 控制器日志和 AWS 控制台。

问题:Pod 长时间 Pending

可能原因:节点启动时间过长或节点不足。检查节点创建进度和 NodePool 配置。

问题:Spot 实例被回收

可能原因:Spot 实例被云厂商回收。设计应用时需要支持驱逐容忍(tolerations)。

问题:节点创建太快导致成本增加

可能原因:consolidation 策略过于激进。调整 consolidateAfter 和 limits。

Karpenter 最佳实践总结

在 GPU 推理服务中使用 Karpenter 时,有几个关键的最佳实践。首先是多 NodePool 设计:为不同优先级的工作负载使用不同的 NodePool,核心业务使用 On-Demand,低优先级使用 Spot。其次是合理的 limits:根据集群容量设置 GPU 数量限制,防止无限扩容。第三是配合镜像预拉取:使用 DaemonSet 预拉取镜像,减少节点启动后的拉镜像时间。第四是模型缓存:将模型缓存到节点本地或共享存储,减少冷启动时间。第五是监控节点创建时间:节点创建时间应该在可接受的范围内,否则需要优化。

多云支持

虽然 Karpenter 最初为 AWS 设计,但现在也支持其他云厂商:

  • AWS:EC2 NodeClass
  • Azure:AzureNodeClass(预览)
  • GCP:GCPNodeClass(开发中)
  • 阿里云:社区开发中

如果使用其他云厂商,可能需要使用 Cluster Autoscaler 或其他方案。

总结

Karpenter 是 Kubernetes GPU 推理服务弹性扩展的关键组件。它能够快速响应 GPU Pod 的资源需求,自动创建合适的 GPU 节点,实现 Pod 和节点的协同弹性。在使用时,需要根据业务需求设计合理的 NodePool,配合镜像预拉取和模型缓存优化冷启动时间,并设置合理的 limits 防止无限扩容。