Amazon Web Services ブログ

Karpenter の統合機能を用いた Kubernetes コンピューティングコストの最適化

この記事は Optimizing your Kubernetes compute costs with Karpenter consolidation (記事公開日: 2023 年 2 月 7 日) を翻訳したものです。

イントロダクション

Karpenter は Kubernetes における最適なノード選択に関する課題を解決するために開発されました。Karpenter の what-you-need-when-you-need-it モデル(訳注:必要な時に必要な分のリソースを調達すること) は、Pod の要求に基づきクラスターにコンピュートキャパシティを追加することによって、Kubernetes のコンピュートリソースの管理プロセスを簡略化します。最近のワークロード統合のリリースによって、インスタンスのリソース使用率を向上させコンピューティングコストを削減するために、Pod の配置を継続的に監視および最適化することができるようになりました。

この記事では Karpenter の統合機能について見ていき、Kubernetes のデータプレーンコストの最適化に与える影響について、実例を交えて解説します。

Karpenter のワークロード統合

以前のバージョンでは、Karpenter は DaemonSet 以外の Pod が存在しないワーカーノードだけを削除していました。時間が経過すると、ワークロードが再スケジュールされ、一部のワーカーノードが十分に利用されなくなる可能性がありました。ワークロード統合は、Pod のリソースとスケジューリングの制約を守りながら、ワークロードを最も小さくコストの低いインスタンスに統合することで、Karpenter の効率的かつコスト最適なオートスケーリングというビジョンをさらに実現することを目的としています。ワークロード統合は、Karpenter の Provisioner CRD (Custom Resource Definition) で有効化できます。Provisioner は、Karpenter が制御するクラスター内のノードのライフサイクルアクションに責任を負います。Provisioner は、起動したノードのキャパシティの制約や動作 (有効期限や統合など) を定義することができます。

Provisioner で統合機能を有効にすると、Karpenter はコンピュートキャパシティを統合してノードの使用率を高めコスト効率を向上させるために、クラスターのワークロードを継続的に監視します。Karpenter はお客様が指定したスケジューリングの制約 (Pod Affinity rule、Topology Spread Constraints など) にも従います。Karpenter はワークロードの要求に基づいてノードを起動するので、ワークロードの要求を正確に指定することが重要になります。そのためには、Pod の CPU 及び メモリ要求の両方を追加する必要があります。これによって、特にクラスター上で複数のワークロードを並列で実行する場合にリソーススタベーションやリソース不足を防ぐことができます。またこれらの設定は、Karpenter のワークロード統合機能を有効に活用するためにも重要なものになります。

前提

この記事の例を実行するためには、以下のセットアップが必要です。

このセットアップのプロセスを自動化するために、Amazon EKS Blueprints for Terraform を使用できます。クラスターにアドオンとして Karpenter をデプロイするための例も掲載されています。この記事の例を実行するために、Terraform のソースコードを修正する必要はありません。

Provisioners

Karpenter は Provisioner CRD (Custom Resource Definition) に基づいてノードを制御します。Provisioner は、コンピューティングキャパシティ、インスタンスタイプ、追加の kubelet の設定、リソースパラメーター、その他のノードのライフサイクルに関する仕様などを決定する役割を担うものです。複数の Provisioner CRD を、それぞれの用途に応じて重複しないようにクラスターにデプロイすることができます。

Amazon EKS Blueprints の examples/karpenter/provisioners フォルダに、 Provisioner のサンプルがあります。今回は express-test という Node.js のサンプルアプリケーションのための単一の Provisioner を使用することにします。したがって、その Provisioner をクラスターにデプロイしない限り、全てのアプリケーション Pod はスケジュールされません。

Provisioner ファイルは、以下のコードで提供されます。

Provisioner

apiVersion: karpenter.sh/v1alpha5
kind: Provisioner
metadata:
  name: express-test
spec:
  # Enables consolidation which attempts to reduce cluster cost by both removing un-needed nodes and down-sizing those
  # that can't be removed. Mutually exclusive with the ttlSecondsAfterEmpty parameter.
  consolidation:
    enabled: true
  requirements:
    - key: "karpenter.sh/capacity-type" # If not included, the webhook for the AWS cloud provider will default to on-demand
      operator: In
      values: ["spot", "on-demand"]
    - key: "karpenter.k8s.aws/instance-cpu"
      operator: In 
      values: ["c", "m", "r"]
  provider:
    instanceProfile: KarpenterNodeInstanceProfile-alpha
    subnetSelector:
      karpenter.sh/discovery: 'alpha'
    securityGroupSelector:
      karpenter.sh/discovery/alpha: 'alpha'
  labels:
    managedBy: carpenter
Apache Configuration

このファイルを保存し、以下のコマンドでクラスターにデプロイすることができます。

kubectl apply -f provisioner.yaml
Apache Configuration

ウォークスルー

ワークロード統合の例

このセクションでは、各 Pod に 1 CPU コア、Zone での Topology Spread Constraints を持つ express-test アプリケーションの複数のレプリカをデプロイします。これに加えて、ワークロードのマニフェストには、前のステップで作成した Provisioner によって管理されるコンピュートリソースにスケジュールされるよう、Pod の nodeSelector ルールを指定することになります。Karpenter が最初のノードのセットをどのようにプロビジョニングするかを確認した後に、レプリカ数を更新することで Deployment を変更し、この変更に対する Karpenter の統合を確認します。

アプリケーションのリソースマニフェストは、以下のコードになります。

アプリケーションのマニフェスト

apiVersion: apps/v1
kind: Deployment
metadata:
  name: express-test
spec:
  replicas: 20
  selector:
    matchLabels:
      app: express-test
  template:
    metadata:
      labels:
        app: express-test
    spec:
      topologySpreadConstraints:
      - maxSkew: 1
        topologyKey: "topology.kubernetes.io/zone"
        whenUnsatisfiable: ScheduleAnyway
        labelSelector:
          matchLabels:
            app: express-test
      nodeSelector:
        karpenter.sh/provisioner-name: express-test
      containers:
        - name: express-test
          image: lukondefmwila/express-test:1.1.4
          resources:
            requests:
              cpu: "1"
              memory: "64Mi"
Apache Configuration

以下のコマンドを実行することで、Deployment を保存し、クラスターに適用することができます。

kubectl apply -f deployment.yaml
Apache Configuration

その後、Amazon EKS Node Viewer を使用して、Karpenter ノードのコンピュート使用量とコストを表示することができます。

Karpenter は、マニフェストで指定した要求を満たす 3 つのノード (2 台の t3.2xlarge インスタンスと、1 台の c6a.2xlarge) をクラスターに追加します。これらはコンピュートの要求とスケジューリング制約の両方に対応しています。またこれらは 8 つの CPU コアを持つスポットインスタンスで、各々のノードは eu-west-1a、eu-west-1b、eu-west-1c という別々の AZ (Availability Zone) にプロビジョニングされています。Karpenter は 20 個のレプリカ (異なる AZ に分散) によって要求された CPU コア、DaemonSet の CPU 使用量、kubelet によって予約されたリソースを考慮したノードを追加しています。

上のスクリーンショットにあるように、eks-node-viewer CLI ツールは、現在起動しているノードが 1 ヶ月あたりいくらかかるのかを表示しています。

次のステップでは、元の Deployment マニフェストを変更します。以下のように、Pod のレプリカ数を 20 から 10 に減らし、要求されるリソース使用量を減らします。

アプリケーションのマニフェスト

apiVersion: apps/v1
kind: Deployment
metadata:
  name: express-test
spec:
  replicas: 10
  ...
Apache Configuration

kubectl apply -f deployment.yaml コマンドを再実行することで、これらの変更をデプロイすることができます。

この時点ではノードは十分に活用されていません。先ほど Karpenter は 20 のレプリカ数を考慮して、各々約 8 CPU が利用可能な 3 つのノードを追加しました。クラスターのコストを削減する唯一の方法は、リソース使用率の低いノードを削除することです。まずはじめに、Pod を分離して排出します。ノードの監視を続けると、この一連のイベントがおこなわれることに気づくことができます。最後に、残り 2 つのスポットインスタンスに Pod がスケジューリングされます。これらの変更の結果は、以下のスクリーンショットで確認できます。

Karpenter の統合機能を有効にすることによって、3 つのインスタンスのうち 1 つを削除することでクラスターのコンピュートコストを最適化し、同時にアプリケーションワークロードで定義されたリソースの要求と Topology Spread Constraints を満たすことができました。このワークロードのデータプレーンのコストは、上のスクリーンショットで示したように削減されました。

本番環境では Karpenter と一緒に kubecost のようなツールを使用して、Kubernetes のコストを監視および管理することが推奨されます。Amazon EKS のコスト監視のために kubecost をセットアップする方法については、こちらのユーザーガイドを参照できます。

クリーンアップ

追加の運用コストの発生を防ぐため、作成したインフラストラクチャを全て削除することを忘れないでください。まず Karpenter で作成されたノードを削除する必要があります。これらは Provisioner CRD で管理されているので、Kubernetes クラスターからこのリソースを削除します。その後、Terraform を使って残りのインフラストラクチャを削除します。ターミナルで正しいフォルダにいることを確認し、terraform destroy コマンドを実行します。Amazon EKS Blueprints のクリーンアップ手順でおこなうことも可能です。

結論

この記事では、Karpenter の新しいワークロード統合機能とリソース要求のグッドプラクティスを組み合わせることで、Kubernetes のデータプレーンのコストを削減できることを紹介しました。

こちらは、このテーマに関する追加の資料です。

ドキュメントを読んだり、コミュニティの Kubernetes Slack チャンネル #karpenter に参加することで、Karpenter についてより詳しく学ぶことができます。

 

翻訳はソリューションアーキテクトの後藤が担当しました。原文はこちらです。