使用Spot机器

创建NodePool

我们将部署两个节点池,利用 Karpenter 能够根据所需比例在按需和竞价型实例之间分配工作负载的能力。

执行以下命令创建节点池:

cat << EOF >nodepool-ondemandspotsplit.yaml
apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
  name: ondemand
  labels:
    app.kubernetes.io/managed-by: app-team
spec:
  disruption:
    consolidateAfter: 30s
    consolidationPolicy: WhenEmptyOrUnderutilized
  template:
    metadata:
      labels:
        EKSAutoNodePool: OnDemandSpotSplit
    spec:
      expireAfter: 336h
      nodeClassRef:
        group: eks.amazonaws.com
        kind: NodeClass
        name: default
      requirements:
      - key: eks.amazonaws.com/instance-category
        operator: In
        values:
        - c
        - m
        - r
      - key: eks.amazonaws.com/instance-generation
        operator: Gt
        values:
        - "4"
      - key: kubernetes.io/arch
        operator: In
        values:
        - amd64
      - key: karpenter.sh/capacity-type
        operator: In
        values:
        - on-demand
      - key: capacity-spread
        operator: In
        values:
        - "1"
      taints:
      - effect: NoSchedule
        key: OnDemandSpotSplit
      terminationGracePeriod: 24h0m0s

---
apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
  name: spot
  labels:
    app.kubernetes.io/managed-by: app-team
spec:
  disruption:
    consolidateAfter: 30s
    consolidationPolicy: WhenEmptyOrUnderutilized
  template:
    metadata:
      labels:
        EKSAutoNodePool: OnDemandSpotSplit
    spec:
      expireAfter: 336h
      nodeClassRef:
        group: eks.amazonaws.com
        kind: NodeClass
        name: default
      requirements:
      - key: eks.amazonaws.com/instance-category
        operator: In
        values:
        - c
        - m
        - r
      - key: eks.amazonaws.com/instance-generation
        operator: Gt
        values:
        - "4"
      - key: kubernetes.io/arch
        operator: In
        values:
        - amd64
      - key: karpenter.sh/capacity-type
        operator: In
        values:
        - spot
      - key: capacity-spread
        operator: In
        values:
        - "2"
        - "3"
        - "4"
        - "5"
      taints:
      - effect: NoSchedule
        key: OnDemandSpotSplit
      terminationGracePeriod: 24h0m0s
EOF

kubectl apply -f nodepool-ondemandspotsplit.yaml

输出应类似于:

nodepool.karpenter.sh/ondemand created
nodepool.karpenter.sh/spot created

利用 Karpenter 为节点分配标签的能力,并使用这些标签进行拓扑分布,按照所需比例在按需和竞价型实例之间分配工作负载。

为此,我们为竞价型和按需容量类型各创建了一个节点池,并为名为 capacity-spread 的新标签分配了不同的值。在上面的示例中,我们为竞价型节点池提供了四个唯一值,为按需节点池提供了一个值。当我们均匀分布在新标签上时,我们将得到 4:1 的竞价型与按需节点比例。

部署应用程序

在本实验中,我们将使用 catalog 组件在竞价型实例上运行其副本。让我们配置我们的catalog应用程序部署为 5 个副本。使用 capacity-spread 标签,我们将在新标签上均匀分布,最终得到 4:1 的竞价型与按需节点比例。

配置并重新部署组件:

kubectl patch deployment catalog --type=strategic --patch '{
  "spec": {
    "replicas": 5,
    "template": {
      "spec": {
        "topologySpreadConstraints": [
          {
            "maxSkew": 1,
            "minDomains": 5,
            "topologyKey": "capacity-spread",
            "whenUnsatisfiable": "DoNotSchedule",
            "labelSelector": {
              "matchLabels": {
                "app.kubernetes.io/name": "catalog"
              }
            }
          }
        ],
        "nodeSelector": {
          "EKSAutoNodePool": "OnDemandSpotSplit"
        },
        "tolerations": [
          {
            "key": "OnDemandSpotSplit",
            "operator": "Exists"
          }
        ]
      }
    }
  }
}'

最后,我们可以验证 catalog 组件的 Pod 是否在竞价型实例上运行。

运行以下命令检查节点和 Pod:

kubectl get node -L karpenter.sh/capacity-type

image-20250228093101725

我们可以看到 catalog 应用程序的 Pod 分布在竞价型和按需容量类型之间

总结

在本实验中,我们探索了如何在 EKS Auto Mode中同时利用按需和竞价型实例。我们通过创建两个节点池实现了分割比例策略 - 一个用于按需实例,另一个用于竞价型实例,使用 capacity-spread 标签。我们为竞价型节点池配置了四个唯一的分布值,为按需节点池配置了一个值,有效地创建了 4:1 的竞价型与按需实例比例。

为了演示这种配置,我们将catalog应用程序配置为 5 个副本,并使用拓扑分布约束根据我们所需的比例将 Pod 分布在节点上。

本实验成功展示了如何在 EKS 集群管理中实现可靠性和成本优化之间的平衡。