🔜

Karpenter の NodeOverlays (alpha) を試してみる

に公開

概要

Karpenter の NodeOverlays (2025年11月17日時点で alpha) を試してみた備忘録

NodeOverlays とは?

Karpenter uses NodeOverlays to inject alternative instance type information into the scheduling simulation for more accurate scheduling decisions. NodeOverlays enable users to fine-tune instance pricing and add extended resources to instance types that should be considered during Karpenter’s decision-making process. They provide a flexible way to account for real-world factors like savings plans, licensing costs, and custom hardware resources that aren’t captured in the base instance data from cloud providers.

Karpenterは、より正確なスケジューリングの判断を行うため、NodeOverlaysを使用して代替インスタンスタイプの情報をスケジューリングのシミュレーションに注入します。NodeOverlaysを使用することで、ユーザーはインスタンスの価格を微調整し、Karpenterの意思決定プロセスで考慮すべきインスタンスタイプに拡張リソースを追加することができます。これにより、クラウドプロバイダーの基本インスタンスデータには含まれていない、Savings Plans、ライセンスコスト、カスタムハードウェアリソースなどの実世界の要因を柔軟に考慮することが可能になります。

https://karpenter.sh/docs/concepts/nodeoverlays/

何が嬉しいの?

これまで Savings Plans や Reserved Instance を購入していた場合、それらのインスタンスを使わせるためには、専用の NodePool を定義し、それらを優先的に使わせるようにする必要がありました。

https://karpenter.sh/v1.0/concepts/scheduling/#savings-plans-and-reserved-instances

NodeOverlay を使うことでこれらの定義を NodePool から外出しすることができます。
また、拡張機能の定義を上書きできます。

価格の調整をためしてみる

最初の状態

検証環境として、nginx pod とその他諸々の pod が c6g.medium で動いています。
(※ m5.large は karpenter controller を動かす用)

  • 現在の pod の状態
$ kubectl get pod -o wide
NAME                     READY   STATUS    RESTARTS   AGE     IP               NODE                              NOMINATED NODE   READINESS GATES
nginx-5995856f4f-4sbn4   1/1     Running   0          5m50s   192.168.104.11   ip-192-168-127-179.ec2.internal   <none>           <none>
$ eks-node-viewer
2 nodes (     2350m/2870m) 81.9% cpu █████████████████████████████████░░░░░░░ $0.130/hour | $94.900/month
14 pods (0 pending 14 running 14 bound)

ip-192-168-54-72.ec2.internal   cpu ████████████████████████████████░░░  91% (9 pods) m5.large/$0.0960   On-Demand - Ready -
ip-192-168-127-179.ec2.internal cpu ██████████████████████░░░░░░░░░░░░░  64% (5 pods) c6g.medium/$0.0340 On-Demand - Ready -
  • NodePool
apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
  name: default
spec:
  template:
    spec:
      requirements:
        - key: kubernetes.io/arch
          operator: In
          values: ["amd64", "arm64"]
        - key: kubernetes.io/os
          operator: In
          values: ["linux"]
        - key: karpenter.sh/capacity-type
          operator: In
          values: ["on-demand"]
        - key: karpenter.k8s.aws/instance-category
          operator: In
          values: ["c", "m", "r"]
        - key: karpenter.k8s.aws/instance-generation
          operator: Gt
          values: ["5"]
      nodeClassRef:
        group: karpenter.k8s.aws
        kind: EC2NodeClass
        name: default
      expireAfter: 720h # 30 * 24h = 720h
  limits:
    cpu: 1000
  disruption:
    consolidationPolicy: WhenEmptyOrUnderutilized
    consolidateAfter: 1m

NodeOverlay の有効化

  • alpha 機能の有効化 (helm 利用)
helm upgrade karpenter oci://public.ecr.aws/karpenter/karpenter \
  --version 1.8.1 \
  --namespace karpenter \
  --reuse-values \
  --set settings.featureGates.nodeOverlay=true
  • 確認
$ kubectl rollout status deployment karpenter -n karpenter
Waiting for deployment "karpenter" rollout to finish: 1 old replicas are pending termination...
Waiting for deployment "karpenter" rollout to finish: 1 old replicas are pending termination...
Waiting for deployment "karpenter" rollout to finish: 1 old replicas are pending termination...
Waiting for deployment "karpenter" rollout to finish: 1 of 2 updated replicas are available...
deployment "karpenter" successfully rolled out
$ kubectl get pod -n karpenter [karpenter controller の pod の名前] -o jsonpath='{.spec.containers[0].env[?(@.name=="FEATURE_GATES")].value}'
ReservedCapacity=true,SpotToSpotConsolidation=true,NodeRepair=false,NodeOverlay=true,StaticCapacity=false%      

NodeOverlay=true になりました。

  • NodeOverlay デプロイ
apiVersion: karpenter.sh/v1alpha1
kind: NodeOverlay
metadata:
  name: savings-plans-overlay
spec:
  weight: 100
  requirements:
    - key: karpenter.k8s.aws/instance-category
      operator: In
      values: ["m"]
    - key: karpenter.k8s.aws/instance-generation
      operator: Gt
      values: ["5"]
    - key: karpenter.sh/capacity-type  
      operator: In
      values: ["on-demand"]
  priceAdjustment: "-45%"

spec.priceAdjustment で「m 系の第5世代より後のインスタンスタイプの価格が -45% になっている」と設定します。
spec.price でインスタンスの価格を直接指定することもできます。

$ kubectl apply -f nodeoverlays.yaml
nodeoverlay.karpenter.sh/savings-plans-overlay configured

m6g.medium のコストの方が低いのでインスタンスの最適化が始まりました。

3 nodes (     2700m/3810m) 70.9% cpu ████████████████████████████░░░░░░░░░░░░ $0.169/hour | $123.005/month
18 pods (4 pending 14 running 18 bound)

ip-192-168-54-72.ec2.internal   cpu ████████████████████████████████░░░  91% (9 pods) m5.large/$0.0960   On-Demand -        Ready        -
ip-192-168-127-179.ec2.internal cpu █████████████░░░░░░░░░░░░░░░░░░░░░░  37% (4 pods) c6g.medium/$0.0340 On-Demand Deleting NotReady/35s -
ip-192-168-110-41.ec2.internal  cpu ██████████████████████░░░░░░░░░░░░░  64% (5 pods) m6g.medium/$0.0385 On-Demand -        Ready        -
•

最適化が完了しました。

2 nodes (     2350m/2870m) 81.9% cpu █████████████████████████████████░░░░░░░ $0.135/hour | $98.185/month
14 pods (0 pending 14 running 14 bound)

ip-192-168-54-72.ec2.internal  cpu ████████████████████████████████░░░  91% (9 pods) m5.large/$0.0960   On-Demand - Ready -
ip-192-168-110-41.ec2.internal cpu ██████████████████████░░░░░░░░░░░░░  64% (5 pods) m6g.medium/$0.0385 On-Demand - Ready -
•

c6g.medium/$0.0340m6g.medium/$0.0385 となっていますが、これは割引前の価格が表示されているようです。NodeOverlay によって m6g.medium は -45% で計算されています。

spec.capacity について

インスタンスの拡張情報を上書き定義できます。
ただし、ソースコードを見ると、CPU / メモリ / エフェメラルストレージ / pods のような Well-Known Resource は対象外です。

https://github.com/kubernetes-sigs/karpenter/blob/a1357ee7fbf3fcc814c89e06a73df21c36453dae/pkg/apis/v1alpha1/nodeoverlay_validation.go#L50-L57

Well-Known Resource は以下にあります。
https://github.com/kubernetes-sigs/karpenter/blob/a1357ee7fbf3fcc814c89e06a73df21c36453dae/pkg/apis/v1/labels.go#L96-L101

まとめ

まだ alpha 機能ですが、NodePool を書き換えることなく node の情報を上書きできます。
Savings Plans / リザーブドインスタンスを活用されている方は NodeOverlay を使うことでコスト最適化をより簡単にできるかと思います。

Discussion