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、ライセンスコスト、カスタムハードウェアリソースなどの実世界の要因を柔軟に考慮することが可能になります。
何が嬉しいの?
これまで Savings Plans や Reserved Instance を購入していた場合、それらのインスタンスを使わせるためには、専用の NodePool を定義し、それらを優先的に使わせるようにする必要がありました。
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.0340、m6g.medium/$0.0385 となっていますが、これは割引前の価格が表示されているようです。NodeOverlay によって m6g.medium は -45% で計算されています。
spec.capacity について
インスタンスの拡張情報を上書き定義できます。
ただし、ソースコードを見ると、CPU / メモリ / エフェメラルストレージ / pods のような Well-Known Resource は対象外です。
Well-Known Resource は以下にあります。
まとめ
まだ alpha 機能ですが、NodePool を書き換えることなく node の情報を上書きできます。
Savings Plans / リザーブドインスタンスを活用されている方は NodeOverlay を使うことでコスト最適化をより簡単にできるかと思います。
Discussion