Upgrade to Pro — share decks privately, control downloads, hide ads and more …

進化するK8s運用:KarpenterによるCluster Autoscalerからの脱却、運...

shibatch
April 22, 2025
830

進化するK8s運用:KarpenterによるCluster Autoscalerからの脱却、運用ノウハウ完全公開

Karpenter。複雑化しがちなKubernetes(K8s)クラスタの運用負荷を軽減し、コスト効率を考えたノードのスケールを自動的に制御するベンダニュートラルで強力なツールです。

AWS EKSではAuto Modeにおける標準実装になるなどオートスケーラとしての確固たる地位を築きつつあり、K8s運用担当者にとって気になる存在ではないでしょうか。 私はそんなKarpenterをクラスタ構築時点の導入、運用中クラスタのCluster Autoscalerからの移行、ベータ版からのアップグレードといったひと通りの導入を行い、運用してきました。

このセッションではKarpenterの導入と運用の勘所をすべてお伝えし、みなさまのK8s運用負荷軽減のお役に立つことを目的としています。KarpenterはインスタンスがPodに合わせてスケールする性格上、避けたほうがよい構成や、逆に積極的に活用したい設定が存在します。

また、具体的かつ実践的な導入方法、ドキュメントではわかりにくかった点や、運用上の苦労話といった知見も余すことなく共有します。きっとみなさまのお役に立つセッションになると確信しています。

shibatch

April 22, 2025
Tweet

Transcript

  1. 5 アジェンダ 1. Karpenter⼊⾨編 1.1. 特徴 1.2. Cluster Autoscalerとの違い 2.

    インストール編 2.1. インストールはなにをするのか 2.2. 絶対にやるべき設定 3. 運⽤編 3.1. nodeclass, nodepool 3.2. 避けた⽅がよい設定 3.3. できていないがやりたかったこと
  2. Karpenter⼊⾨編 8 • この頃SUZURIをとあるPaaS→EKSに移⾏する検証をしていた • なるべく運⽤開始後にKubernetesの運⽤負荷が低くできないか… • その頃の⾃分のあたまでは以下の選択肢があった ◦ EKS

    (on EC2) ▪ ちゃんとノードグループの設計をしてメンテナンスをしないといけないイメージ ▪ インスタンスタイプを利⽤するアプリケーションリソースに合わせて…⼿間 ◦ EKS (on Fargate) ▪ ノード部分が抽象化されているので運⽤はラクできそう ▪ Spotインスタンスが使えない / Daemonsetが使えない • ここで知った第3の選択肢、EKS(on EC2) + Karpenter Karpenterを知ったのはCloudNative Days Tokyo 2023
  3. Karpenter⼊⾨編 9 • ʻKubernetesクラスター⽤のノードの動的なスケールアウトとスケールインを⾃動 化するノードプロビジョナー’ • AWS EKSで動く(他、Azure AKS) •

    この登壇ではAWS EKSでの動作について扱います • EKS (on EC2)と組み合わせて動く • 最近(2024/12)リリースされた EKS Auto Modeは Karpenterでの運⽤が前提 • EC2の運⽤負荷が低減でき、Spotインスタンスが使えてDaemonsetも普通に使える いいとこ取り Karpenterをひとことで⾔うと
  4. Karpenter⼊⾨編 10 コスト最適化がラクにできる!!! • ⾃動で最適なノード台数をprovisionsしてくれる ◦ Podのresource requestベース • インスタンスタイプの中から最適なものを⾃動で選択する

    • 強⼒なSpotインスタンスを⽤いたコスト最適化設計 ◦ 「スポットインスタンスのベストプラクティス」に沿いやすい ▪ https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/spot-best-practices.html • ノードの起動が早い ◦ EC2のAPIを直接実⾏するため Karpenterの特徴
  5. Karpenter⼊⾨編 11 1. デプロイ • CA: ノードグループに依存したスケーリング設定。 • Kar: ノードグループに依存しない。よりシンプルなスケーリング設定。

    2. インスタンスタイプ、spot, on-demand設定 • CA: ノードグループとインスタンスタイプが1:1。また、spot / on-demandのどち らかを選択。 複数インスタンスタイプを使う場合はその分ノードグループが必要。 • Kar: 1つのノードプールで複数のインスタンスタイプから⾃動選択、reserved / spot / on-demand から⾃動選択。設定が少なくて済む。 Cluster Autoscalerとの⽐較(1)
  6. Karpenter⼊⾨編 12 3. スケーリング速度 • CA: Karより遅い。CA→ノードグループ→オートスケーリンググループ→EC2 • Kar: CAより速い。

    Karpenter→EC2 4. Spotインスタンスの終了への対応 • CA: キャパシティリバランシングで事前に別のSpotインスタンスを起動。または中 断通知によるdrain。 • Kar: 中断通知によるdrainのみ。 Cluster Autoscalerとの⽐較(2)
  7. Karpenter⼊⾨編 13 Cluster Autoscalerとの⽐較(まとめ) Karpenterのアドバンテージ • シンプルな設定で複数のインスタンスタイプやspotインスタンスへの対応ができる • 少ないノードグループ •

    スケーリングが速い • Reserved Instanceへの対応 Cluster Autoscalerのアドバンテージ • Spotインスタンスのオートリバランシング
  8. Karpenter⼊⾨編 15 • KarpenterはEKSやAKSで動作するノードプロビジョナー • EKS Auto Modeで採⽤され、EKSでは今後の標準になっていきそう • Cluster

    Autoscalerと⽐較して、少ない設定でコスト最適化ができる • Spotインスタンスでのコスト最適化がやりやすい • Reserved InstanceとSpotインスタンスを組み合わせたコスト最適化もできる • ⾃動化の範囲が広く、少ない設定でコスト最適化ができる ⼊⾨編まとめ
  9. インストール編 17 • ⼀度もスッとインストールできた経験がないから… • ⾃分は4度インストールとアップデートの経験をした ◦ SUZURI クラスタサービスイン(v0.36) ◦

    minne クラスタ Cluster Autoscalerからの切り替え(v1.0.2) 👈特に苦労した ◦ SUZURI Karpenterアップデート (v0.36 → v1.3.3) 👈特に苦労した ◦ minne Karpenterアップデート (v1.0.2 → v1.3.3) • なぜ苦労したのか、どうすれば苦労せずに済むかをお伝えしたい なぜインストール編があるか
  10. インストール編 18 • CloudFormation テンプレートをダウンロード & デプロイする • eksctlコマンドでクラスタを作成する •

    helmでKarpenterをインストールする • Karpenterの設定(NodeClass, NodePool)を定義する https://karpenter.sh/docs/getting-started/getting-started-with-karpenter/ ね?簡単でしょ? … とはいかない 公式のインストール⼿順
  11. インストール編 19 • CloudFormation & eksctlコマンドを使う前提であり、 Terraformを 使うことは想定されていない 🤔 •

    クラスタを新規作成する前提でインストール手順がある 🤔 • AWSサービスはすべてTerraformで管理している • CloudFormationと棲み分け考えて周知するのは面倒
  12. インストール編 20 CloudFormationで何をやっているかを紐解いて、Terraformコードにする curl -fsSL https://raw.githubusercontent.com/aws/karpenter-provider-aws/v"${KARPENTER_VERSION }"/website/content/en/preview/getting-started/getting-started-with-karpenter/cloud formation.yaml > "${TEMPOUT}"

    \ && aws cloudformation deploy \ --stack-name "Karpenter-${CLUSTER_NAME}" \ --template-file "${TEMPOUT}" \ --capabilities CAPABILITY_NAMED_IAM \ --parameter-overrides "ClusterName=${CLUSTER_NAME}" ↓ドキュメントではcurlでCloudFormationのtemplateを取得してdeployしている
  13. インストール編 21 CloudFormationで何をやっているかを紐解いて、Terraformコードにする curl -fsSL https://raw.githubusercontent.com/aws/karpenter-provider-aws/v"${KARPENTER_VERSION }"/website/content/en/preview/getting-started/getting-started-with-karpenter/cloud formation.yaml > "${TEMPOUT}"

    \ && aws cloudformation deploy \ --stack-name "Karpenter-${CLUSTER_NAME}" \ --template-file "${TEMPOUT}" \ --capabilities CAPABILITY_NAMED_IAM \ --parameter-overrides "ClusterName=${CLUSTER_NAME}" ↓ドキュメントではcurlでCloudFormationのtemplateを取得してdeployしている • こちとら全部Terraformでコード管理しているんですけど …
  14. インストール編 23 • Karpenter Controller⽤のIAM Role/ IAM Policy の作成 ◦

    Karpenterの機能が動作するための権限 ▪ EC2を起動する権限など ▪ これがないとNodeClassがうまく起動しない • Karpenter Node⽤のIAM Role / IAM Policyの作成 ◦ CNIやSSMのPolicy ◦ NodeGroupにつけるものと同じもの CloudFormation使ってやっていることは主に4つ
  15. インストール編 24 • EventBridgeのルール ◦ EC2の中断を監視し、SQSにルーティングする • SQS Queue ◦

    中断通知をKarpenterに送信する 👉これらのCloudFormationでの記載をTerraformのコードに書き換えていった 結構⼤変 IAMで278L、EventBridgeで60L、SQSが45L AIでいい感じに…はできなかったので変換するようなソリューションがあればいいなぁ CloudFormation使ってやっていることは主に4つ
  16. インストール編 25 ClusterConfigを作っていろいろやっている(⼀部抜粋) eksctlコマンドでクラスタを作成している eksctl create cluster -f - <<EOF

    --- apiVersion: eksctl.io/v1alpha5 kind: ClusterConfig metadata: name: ${CLUSTER_NAME} region: ${AWS_DEFAULT_REGION} version: "${K8S_VERSION}" tags: karpenter.sh/discovery: ${CLUSTER_NAME} iam: withOIDC: true podIdentityAssociations: - namespace: "${KARPENTER_NAMESPACE}" serviceAccountName: karpenter roleName: ${CLUSTER_NAME}-karpenter permissionPolicyARNs:
  17. インストール編 26 • EKS Pod Identityを使う設定 ◦ Clusterごとにユニークな K8s role

    を作って Karpenter ControllerのIAM roleと紐づける ◦ SQSを⽤いた中断通知に使っている模様 ◦ このために EKS アドオンの Pod Identity Agentを⼊れている 👉これはTerraformコードにはしなかった(eksctlコマンドで⼊れた) • IAM Roleに⾃動でEKSアドオンバージョンなどのタグが付くのでterraform 管 理に向かないと判断 eksctlコマンドでやっている設定は主に3つ % eksctl create podidentityassociation
  18. インストール編 27 • aws-auth ComfigMapへの設定 ◦ IAM RoleとK8s RBACの紐付け(IAM Identity

    Mapping) 👉すでにConfigMapがコード管理されていたので挿⼊した • Karpenter Pod配置⽤のNodeGroupの作成 ◦ KarpenterのPod⾃体はKarpenterではなくてNodeGroupで管理する 👉Terraformコードで管理 eksctlコマンドでやっている設定は主に3つ
  19. インストール編 28 helmインストール helm upgrade --install karpenter oci://public.ecr.aws/karpenter/karpenter --version "${KARPENTER_VERSION}"

    --namespace "${KARPENTER_NAMESPACE}" --create-namespace \ --set "settings.clusterName=${CLUSTER_NAME}" \ --set "settings.interruptionQueue=${CLUSTER_NAME}" \ --set controller.resources.requests.cpu=1 \ --set controller.resources.requests.memory=1Gi \ --set controller.resources.limits.cpu=1 \ --set controller.resources.limits.memory=1Gi \ --wait • ここまでの設定がちゃーんとされていれば起動する
  20. インストール編 29 helmインストール helm upgrade --install karpenter oci://public.ecr.aws/karpenter/karpenter --version "${KARPENTER_VERSION}"

    --namespace "${KARPENTER_NAMESPACE}" --create-namespace \ --set "settings.clusterName=${CLUSTER_NAME}" \ --set "settings.interruptionQueue=${CLUSTER_NAME}" \ --set controller.resources.requests.cpu=1 \ --set controller.resources.requests.memory=1Gi \ --set controller.resources.limits.cpu=1 \ --set controller.resources.limits.memory=1Gi \ --wait 👈👈必要 • トラブルシューティングでinterruptionQueueの設定を抜くと起動する…とコミュニ ティで書かれているがSQSを使ったspotインスタンスの中断制御が動かなくなる󰢄 • ちゃんとIAMの設定をしましょう
  21. インストール編 30 helmインストール(CRD) helm upgrade --install karpenter-crd oci://public.ecr.aws/karpenter/karpenter-crd --version x.y.z

    --namespace "${KARPENTER_NAMESPACE}" --create-namespace • バージョンアップグレードの場合はCRDを別途helm upgradeしないといけない
  22. インストール編 31 • 0.X →1.X へはCRD Conversion Webhookがうまく動かない ◦ 結局クリーンインストールした

    • とはいえクリーンインストールも気をつけることはある ◦ 旧版のCRD(nodepool, ec2nodeclass)のリソースを消してからKarpenterを消す ◦ リソースが残っていると新版のインストール時にこける→バージョンの切り戻し ◦ サービス運⽤中のクラスタでKarpenterが動かない期間が⻑いと肝を冷やす ◦ 1.1.X 以上のバージョンだと破壊的な変更はないので楽だろう ▪ つまりこのつまづきは今は発⽣しにくいと思う つまづきポイント
  23. インストール編 32 • とにかくIAMの設定が多い!!! ◦ 少しでも間違えると当たり前だけれども起動しなかったりする ◦ Terraformへの変換もれで起動しないことはよくあった ◦ CloudFormationに抵抗がないならそのほうがラクではある

    • Cluster Autoscalerからのマイグレーションはドキュメントにあるが、クリーンイン ストール⼿順と⽐べると抜けている情報がある…… ◦ SQSの中断制御周りの情報がない ◦ 初⾒殺し ◦ helm installでエラーになる ◦ SQS、CloudWatchの設定は”Getting Started with Karpenter”を参照する つまづきポイント
  24. インストール編 33 • CAとKarpenterが同時に動いている状態⾃体は設定に重複がなければ問題はない • CAの設定からまずはtagやtaintの設定があるノードグループを抜く • tagやtaintがついているノードグループのノード数を0にする • PodがevictされKarpenterでノードが作成され始める

    • うまくいったらCAを消す(replica=0にするなど) • tagやtaintのついていないノードグループのノード数を0にする • KarpenterですべてのPodをプロビジョニングしている状態になる Cluster Autoscalerからのインプレースな移⾏の戦略 いきなりすべてを移行するのではなく、 ノードグループ単位の移行 がおすすめ
  25. インストール編 34 • Karpenterはインストール前にIAM、SQS、CloudWatch Alarm、EventBridgeの設 定、Pod Identity、aws-auth ComfigMapへの設定が必要 • CloudFormationを使うのが楽だがTerraformでも代替できる

    • helmでインストールする。アップグレードの際は別途CRDのhelmインストールも 必要 • ClusterAutoscalerから切り替える際はノードグループ単位で作業するとサービス継 続しながらでも切り替えやすい インストール編まとめ
  26. 運⽤編 36 Karpenterの基本的なCustom Resourceは以下 • NodeClass ◦ 以前(v0.33)までは node Templateという名前だった

    ◦ ノードが起動するためのuserdataを定義 • NodePool ◦ ノードに付与するtag、taint、どのNodeClassを使うか、どのインスタンスタイプを使う かを定義 Karpenterのしくみ
  27. 運⽤編 37 ⾃分はひとつの「default」NodeClassを作って、複数のNodePoolが参照する形に Karpenterのしくみ % kubectl get ec2nodeclass NAME READY

    AGE default True 27d % kubectl get nodepool NAME NODECLASS NODES READY AGE arm64-default default 25 True 27d arm64-monitor default 2 True 27d arm64-on-datadog default 5 True 27d multi-default default 2 True 27d multi-monitor default 0 True 27d multi-on-datadog default 0 True 27d ノードが起動するための userdataを定義 参照するNodeClassはひとつのみ
  28. 運⽤編 39 • ストレージタイプはgp3 • httpPutResponseHopLimit ◦ インスタンスのメタデータを取得できるホップ数 ◦ これを1->2にしないと

    ALBC などで外からMetadataを参照できない NodeClass(2) blockDeviceMappings: - deviceName: /dev/xvda ebs: volumeSize: 80Gi volumeType: gp3 metadataOptions: httpPutResponseHopLimit: 2 # for AWS Load Balancer Controller (IMDSv2)
  29. 運⽤編 40 • userdata ◦ 社内セキュリティ要件でWazuhを⼊れている ◦ ssm get-parameterは…… NodeClass(3)

    --// Content-Type: text/x-shellscript; charset="us-ascii" #!/bin/bash PARAM_NAME="/eks/wazuh-agent-auth-password" AGENT_AUTH_PASSWORD=$(aws ssm get-parameter --name "$PARAM_NAME" --with-decryption --query "Parameter.Value" --output text) /var/ossec/bin/agent-auth -m wazuh.pepabo.com -P $AGENT_AUTH_PASSWORD -G minne systemctl restart wazuh-agent
  30. 運⽤編 41 • userdata ◦ spegel ◦ コンテナレジストリのミラーをノード分散で作る。便利。 NodeClass(4) Content-Type:

    text/x-shellscript; charset="us-ascii" #!/bin/bash sed -i '/\[plugins."io.containerd.grpc.v1.cri".registry\]/!b;n;cconfig_path = "/etc/containerd/registries"' /etc/containerd/config.toml sed -i '/discard_unpacked_layers = true/s/true/false/' /etc/containerd/config.toml systemctl daemon-reload systemctl restart containerd
  31. 運⽤編 43 • arm64-default ◦ arm64のspotインスタンスを選択する設定 • multi-default ◦ arm64/x86_64のon-demand/spotインスタンスから選択する設定

    ◦ spotインスタンスを優先し、取得できなければon-demandを取得する動きをする ◦ karpenterの特徴であり、便利な点 NodePoolはこう運⽤しています % kubectl get nodepool -o wide NAME NODECLASS NODES READY AGE WEIGHT CPU MEMORY arm64-default default 78 True 30d 20 412 2907533476Ki multi-default default 17 True 30d 10 76 517494820Ki
  32. 運⽤編 44 NodePool運⽤例 - arm64-default requirements: - key: kubernetes.io/arch operator:

    In values: ["arm64"] - key: karpenter.sh/capacity-type operator: In values: ["spot"] - key: karpenter.k8s.aws/instance-family operator: In values: ["c7g","m7g","r7g","c6g","m6g","r6g"] weight: 20
  33. 運⽤編 45 NodePool運⽤例 - multi-default requirements: - key: kubernetes.io/arch operator:

    In values: ["arm64","amd64"] - key: karpenter.sh/capacity-type operator: In values: ["spot","on-demand"] - key: karpenter.k8s.aws/instance-family operator: In values: ["c7i","m7i","r7i","c7g","m7g","r7g", "c6i","m6i","r6i","c6g","m6g","r6g"] weight: 10
  34. 運⽤編 46 Pod側ではNodeSelectorでspot/on-demandを指定できる nodeSelector: kubernetes.io/arch: "arm64" karpenter.sh/capacity-type: "on-demand" • NodePoolがspot/on-demand両⽅を許容していても、Pod側のNodeSelector

    でon-demandのみを指定できたりする • NodeGroupのようにノード側でtaintを指定しなくていいので楽 • Jobなど途中終了してほしくないもので活⽤すると便利
  35. 運⽤編 47 • Karpenterの⽬⽟機能!! • クラスター内のノードを⾃動的に管理‧最適化 • ちゃんとpodをdrainしてからノードを終了する • disruptionが発⽣する要因は主に4つ

    ◦ Consolidation …使っていないノードを統合する ◦ Drifted …古い設定のノードを終了する ▪ NodeClassを更新した ▪ amiのバージョンが更新された ◦ Expiration …NodeClassで設定した起動期間を過ぎた ◦ Interruption…spotインスタンスの中断による終了 NodePool - disruption(中断制御)
  36. 運⽤編 48 NodePool - disruptionは柔軟な設定ができる(ドキュメント例) disruption: budgets: - nodes: “0”

    schedule: "0 9 * * mon-fri" duration: 8h reasons: - Drifted - Underutilized - nodes: "100%" reasons: - Empty - nodes: "10%" reasons: - Drifted - Underutilized • 月-金の9時-17時はノード統合とノード最 新化はしない • 空のノードはどの時間帯でも即終了させ る • 月-金の9時-5時以外のすべての時間帯 で、ノード統合とノード最新化はノード全 体の10%ずつおこなう ノード統合はけっこう激しく動く特性がある。その ためデイタイムは避ける設定にしている例
  37. 運⽤編 49 NodePool - disruptionで⾃分が⾏き着いたのはシンプルな設定 disruption: budgets: - nodes: “1”

    • どの時間帯でもノード統合、ノード最新 化、空のノード終了を実行する • 1ノードずつ実行する これで十分! ※クラスタの規模によって調整は必要
  38. 運⽤編 51 NodePool - Spotインスタンスを使うならdisruptionは⼊れよう • EC2インスタンスの料⾦が上昇し続ける現象が発⽣🤔 • このとき、Spot優先で取得できなかったらon-demandを使う設定にしていた •

    Spotインスタンスの終了の契機で、⽣え変わるインスタンスがSpotかon-demand かが選択される • on-demandインスタンスは⾃発的に終了しないため徐々に増え続けていたことが 原因
  39. 運⽤編 53 • 結論:あまりPod側で凝ったスケジュールの設定はしないほうがよい • deploymentへのAvailability Zone 分散 と Host分散の同時設定はオススメしない

    避けた⽅がよい設定(1) affinity: podAntiAffinity: requiredDuringScheduling IgnoredDuringExecution: - labelSelector: matchExpressions: - key: app operator: In values: - minne-app topologyKey: "kubernetes.io/hostname" topologySpreadConstraints: - maxSkew: 1 topologyKey: "topology.kubernetes.io/zone" whenUnsatisfiable: DoNotSchedule labelSelector: matchLabels: app: minne-app
  40. 運⽤編 54 • 結論:あまりPod側で凝ったスケジュールの設定はしないほうがよい • deploymentへのAvailability Zone 分散 と Host分散の同時設定はオススメしない

    • リソースのrequestが異なる複数のdeploymentで設定すると、AZ分散とhost分散 を両⽴できる最適な構成が実現できず、disruptionによるノードの削除→ノードの 作成を繰り返す状態になってしまう 避けた⽅がよい設定(1)
  41. 運⽤編 56 • Karpenterはノードの作成と削除が⾼頻度で⾏われる傾向にある • そのためIPアドレス取得のAPIコールが多すぎるのが原因? ◦ minneで確認してみると1時間に186回 • CNIプラグインには⼀度に/28でIPアドレスを取得できるprefixモードがある

    • インプレースな切り替えに挑戦してみたがうまく切り替わらず断念…… 課題:NodeやPodのIPアドレスの取得に時間がかかることがある Failed to create pod sandbox: rpc error: code = Unknown desc = failed to setup network for sandbox "efa491cb046eadb3e2799b3eb4f47293c2ed7c749fcc21cfb9903c62cf484791": plugin type="aws-cni" name="aws-cni" failed (add): add cmd: failed to assign an IP address to container
  42. 運⽤編 57 • Karpenterには主に NodeClass,NodePoolというカスタムリソースがある • NodeClassはuserdataの設定など、NodePoolはNodeで使うインスタンスタイプの 指定やタグの設定をする • Pod側でもspot,

    on-demandをNodeSelectorで指定できる • disruption(中断制御)はぜひ設定する • AZ分散とhost分散を組み合わせたり、他のPod削除するソフトウェアと組み合わせ ると管理が複雑化するため避ける 運⽤編まとめ