Karpenter は AWS が提供するノードライフサイクルマネージャーで、受信したポッドを監視し、状況に応じて適切なノードを起動します。ノード選択の決定はポリシーに基づき、受信したポッドの仕様により駆動されます。これにはリソース要求やスケジューリング制約が含まれます。
その主な機能:
- スケジュールできないポッドのためにノードを起動
- 既存のノードを置き換えてリソース利用率を向上
- タイムアウトまたは不要な場合、ノードを終了
- プリエンプションの前にノードを優雅に終了
利点#
Karpenter はスケジュールされていないポッドの集約リソース要求を監視し、スケジューリング遅延とインフラコストを最小限に抑えるためにノードの起動と終了の決定を行います。
利点
- より迅速:Karpenter は AWS 上で直接 EC2 キュー API と通信し、ポッドの仕様に基づいて適切なインスタンスタイプを直接選択し、迅速な自動スケーリングを実現します。
- よりコスト効率的:Karpenter はワークロードの要求に基づいてノードを提供し、ポッドを最小限の適切なサイズのノードにパッケージ化し、コストを節約します。
- より柔軟:Karpenter を使用すると、数十のノードを作成することなく柔軟性と多様性を実現できます。
- ネイティブの Cluster-Autoscaler と比較して、ネイティブのいくつかの欠点を回避します:例えば、クラスターに追加または削除される容量制限、Auto Scaling グループで混合サイズのクラスターを使用できないなど。
- Karpenter はストレージ依存関係を自動的に検出します。例えば、元のノードが A ゾーンでプルされている場合、ポッドが最初に起動するときに PV(Persistent Volume)が作成され、以降のノード起動決定時には自動的に A ゾーンに作成されます(EBS は単一可用ゾーンのため、手動で AZ を跨いで作成するとアクセスできなくなる可能性があります)。
使用方法#
前提準備#
- Metrics Server をデプロイする必要があります。ポッドとノードのメトリクスを収集してリソース制約に使用します。
- Karpenter コントローラーをインストールします。これは Karpenter のコアコントローラーです。
- Karpenter ダッシュボードをインストールします。Karpenter が管理するノードやパフォーマンスメトリクスを表示するために使用します。
karpenter プロビジョナーのテンプレート
apiVersion: karpenter.sh/v1alpha5
kind: Provisioner
metadata:
name: default
spec:
providerRef:
name: default
taints:
- key: example.com/special-taint
effect: NoSchedule
startupTaints:
- key: example.com/another-taint
effect: NoSchedule
labels:
billing-team: my-team
# プロビジョニングされたノードのパラメータを制約する要件。
# これらの要件はpod.spec.affinity.nodeAffinityルールと組み合わされます。
# 値を含めたり除外したりするために、オペレーター{In, NotIn}がサポートされています。
requirements:
- key: "karpenter.k8s.aws/instance-category"
operator: In
values: ["c", "m", "r"]
- key: "karpenter.k8s.aws/instance-cpu"
operator: In
values: ["4", "8", "16", "32"]
- key: "karpenter.k8s.aws/instance-hypervisor"
operator: In
values: ["nitro"]
- key: "topology.kubernetes.io/zone"
operator: In
values: ["us-west-2a", "us-west-2b"]
- key: "kubernetes.io/arch"
operator: In
values: ["arm64", "amd64"]
- key: "karpenter.sh/capacity-type" # 含まれていない場合、AWSクラウドプロバイダーのWebhookはオンデマンドにデフォルト設定されます。
operator: In
values: ["spot", "on-demand"]
kubeletConfiguration:
clusterDNS: ["10.0.1.100"]
containerRuntime: containerd
systemReserved:
cpu: 100m
memory: 100Mi
ephemeral-storage: 1Gi
kubeReserved:
cpu: 200m
memory: 100Mi
ephemeral-storage: 3Gi
evictionHard:
memory.available: 5%
nodefs.available: 10%
nodefs.inodesFree: 10%
evictionSoft:
memory.available: 500Mi
nodefs.available: 15%
nodefs.inodesFree: 15%
evictionSoftGracePeriod:
memory.available: 1m
nodefs.available: 1m30s
nodefs.inodesFree: 2m
evictionMaxPodGracePeriod: 3m
podsPerCore: 2
maxPods: 20
limits:
resources:
cpu: "1000"
memory: 1000Gi
consolidation:
enabled: true
ttlSecondsUntilExpired: 2592000 # 30日 = 60 * 60 * 24 * 30秒;
ttlSecondsAfterEmpty: 30
weight: 10
同時にノードテンプレートの設定も必要です。
apiVersion: karpenter.sh/v1alpha5
kind: Provisioner
metadata:
name: default
spec:
providerRef:
name: default
taints:
- key: example.com/special-taint
effect: NoSchedule
startupTaints:
- key: example.com/another-taint
effect: NoSchedule
labels:
billing-team: my-team
# プロビジョニングされたノードのパラメータを制約する要件。
# これらの要件はpod.spec.affinity.nodeAffinityルールと組み合わされます。
# 値を含めたり除外したりするために、オペレーター{In, NotIn}がサポートされています。
requirements:
- key: "karpenter.k8s.aws/instance-category"
operator: In
values: ["c", "m", "r"]
- key: "karpenter.k8s.aws/instance-cpu"
operator: In
values: ["4", "8", "16", "32"]
- key: "karpenter.k8s.aws/instance-hypervisor"
operator: In
values: ["nitro"]
- key: "topology.kubernetes.io/zone"
operator: In
values: ["us-west-2a", "us-west-2b"]
- key: "kubernetes.io/arch"
operator: In
values: ["arm64", "amd64"]
- key: "karpenter.sh/capacity-type" # 含まれていない場合、AWSクラウドプロバイダーのWebhookはオンデマンドにデフォルト設定されます。
operator: In
values: ["spot", "on-demand"]
kubeletConfiguration:
clusterDNS: ["10.0.1.100"]
containerRuntime: containerd
systemReserved:
cpu: 100m
memory: 100Mi
ephemeral-storage: 1Gi
kubeReserved:
cpu: 200m
memory: 100Mi
ephemeral-storage: 3Gi
evictionHard:
memory.available: 5%
nodefs.available: 10%
nodefs.inodesFree: 10%
evictionSoft:
memory.available: 500Mi
nodefs.available: 15%
nodefs.inodesFree: 15%
evictionSoftGracePeriod:
memory.available: 1m
nodefs.available: 1m30s
nodefs.inodesFree: 2m
evictionMaxPodGracePeriod: 3m
podsPerCore: 2
maxPods: 20
limits:
resources:
cpu: "1000"
memory: 1000Gi
consolidation:
enabled: true
ttlSecondsUntilExpired: 2592000 # 30日 = 60 * 60 * 24 * 30秒;
ttlSecondsAfterEmpty: 30
weight: 10
原理#
Karpenter の階層制約モデルを利用して、ポッドの実行は三層の制約を受けます。
- 依存するアプリケーションやストレージが利用可能なゾーンで実行する必要がある
- 特定の種類のプロセッサやハードウェアが必要(対応するノードが必要)
- トポロジー拡張などの技術を使用して高可用性を確保したい
第一層はクラウドサービスプロバイダーによるハードウェアの種類やゾーンの制限であり、第三層は他の技術によってポッドのスケジューリングが制御されます。Karpenter は Provisioner を設定することで特定のノードのスケジューリングと決定を行い、第二層の制御を満たします。
Provisioner を使用して実現できる制約には以下が含まれます。
- リソース要求:一定量のメモリまたは CPU を要求します。
- ノード選択:特定のラベル(nodeSelector)を持つノード上で実行するように選択します。
- ノード親和性:特定の属性(親和性)を持つノード上で実行するようにポッドを描画します。
- トポロジー拡張:トポロジー拡張を使用してアプリケーションの可用性を確保します。
ポッド親和性 / 反親和性:他のポッドのスケジューリングに基づいてポッドをトポロジー領域に引き寄せたり遠ざけたりします。
Karpenter がノードの上下線を制御する方法には以下のいくつかがあります。
- Provisioner の削除:Provisioner を削除すると、そのすべてのノードが優雅に下線されます。
- 空状態:ノード上に非デーモンセットのワークロードが存在しない場合、ttlSecondsAfterEmpty が起動されていると、時間が終了した後にノードが回収されます。
- 有効期限:ttlSecondsUntilExpired が設定されている場合、ノードが有効期限に達すると自動的に下線されます。
- 統合:Karpenter は積極的な節約戦略に基づき、ノードを自発的に削除またはより安価なノードに置き換えます。
- 具体的な戦略:
- すべてのポッドがクラスター内の他のノードの空き容量で実行できる場合、そのノードを削除できます。
- すべてのポッドがクラスター内の他のノードの空き容量とより安価な置き換えノードの組み合わせで実行できる場合、そのノードは置き換え可能です。
- 中断:中断検出を起動すると、Karpenter はノードが中断イベントが発生するかどうかを判断し、積極的にノードを下線します。
ドリフト:Karpenter はドリフトが発生しているノードを下線し、インスタンスで使用されている AMI と AWSNodeTemplate で設定された AMI が一致しないノードにマークを付けます。 - 手動削除:eksctl を使用してノードを手動で削除すると、同様に Karpenter によって優雅に下線されます。
使用シーン#
AWS では、固定ノードグループの作成を代替し、taint および親和性 / 反親和性に基づいて、より柔軟な戦略でノードのスケジューリングと制約を指定できます。