これは、実務で頻繁に直面する状況です。大規模なSaaSサービスを運用する開発・運用チームは、Kubernetesクラスターの拡張性と柔軟性を活用し、アプリケーションを安定的に提供しております。しかし、予期せぬトラフィック増加やサービスデプロイメントのたびにクラスターリソースの不足問題に直面したり、逆にアイドルリソースが多く、不要なコストが発生する状況を経験することがあります。このようなリソースの非効率性は、最終的にクラウドインフラコストの上昇に直結し、開発・運用予算に大きな負担となります。安定したサービス運用と同時にコスト効率を確保することは、すべてのクラウドネイティブ環境における核となる目標と言えるでしょう。
弊社のチームも、このような課題に直面いたしました。目標は明確です。Kubernetesクラスターのリソース活用率を最適化して不要なコスト支出を削減し、同時にアプリケーションのパフォーマンスと可用性を損なうことなく、迅速にスケーリングできるメカニズムを構築することでした。これは、単にインフラコストを削減するだけでなく、サービス運用の俊敏性と安定性を同時に確保する重要なプロセスでございます。
課題:非効率なリソース管理と高額なクラウド費用
従来は、主にKubernetesの基本機能であるCluster Autoscalerを活用してクラスターノードを管理してまいりました。Cluster Autoscalerは有用なツールではありますが、いくつかの限界が存在します。最大の課題は、ノードプロビジョニングの速度です。新しいノードが必要な場合、既存で定義されたEC2 Auto Scaling Groupからインスタンスを起動するのにかなりの時間を要するため、ワークロードの需要変化に即座に対応することが困難でした。特にトラフィックが急増する状況では、PodがPending状態にとどまり、サービス遅延や障害につながるケースが発生することもありました。
また、スポットインスタンスの活用にも多くの制約がありました。Cluster Autoscalerは主にオンデマンドインスタンスを中心に動作し、スポットインスタンスを柔軟に活用するためには複雑な設定と管理負担が伴いました。これはコスト最適化の側面から見て大きな損失でした。スポットインスタンスは、オンデマンドインスタンスと比較して最大90%まで安価な費用でコンピューティングリソースを活用できる強力な手段であるためです。現場で最も多く寄せられる質問の一つは、スポットインスタンスをいかに安定的に活用できるか、という点です。
この他にも、クラスターのノードタイプが固定されているため、多様なワークロードの要件を満たすことが困難でした。メモリ集約型ワークロードとCPU集約型ワークロードが混在する環境では、単一のノードタイプで最適なリソース活用率を達成することは容易ではありません。結果として、多くのアイドルリソースが発生したり、特定のノードでリソースが枯渇するといった非効率な状況が繰り返されました。これらの問題を解決し、より優れたコスト効率と運用安定性を確保することが弊社の核となる要件でございました。
技術選定プロセス:KarpenterとPrometheusの組み合わせ
このような課題を解決するため、複数のソリューションを検討いたしました。既存のCluster Autoscalerの改善案から、多様なサードパーティ製ノード管理ソリューションまでが候補に挙がりました。その中で、Amazonが開発したKubernetesノードプロビジョナーであるKarpenterが、弊社の要件に最も合致すると判断されました。
候補技術比較:Cluster Autoscaler vs. Karpenter
| 特徴 | Cluster Autoscaler | Karpenter |
|---|
| ノードプロビジョニング方式 | EC2 Auto Scaling Group ベース | 直接EC2 APIコール, Just-in-Time |
| プロビジョニング速度 | 比較的遅い (Auto Scaling Group ウォームアップ) | 非常に速い (数秒以内) |
| スポットインスタンス活用 | 設定が複雑、限定的 | デフォルトでサポート、高い柔軟性 |
| ノードタイプの柔軟性 | Auto Scaling Group内で固定されたタイプ | ワークロード要件に応じて動的に選択 |
| スケールダウン | 非アクティブノードの削除 | Podの再スケジュールを考慮、コスト最適化を優先 |
KarpenterはJust-in-Timeプロビジョニング方式を使用し、PodがPending状態として検出されると、すぐにそのPodの要件に最も適したノードをEC2 APIを介して作成します。これは、既存のCluster AutoscalerのAuto Scaling Groupベースの方式よりもはるかに高速なノード準備時間を提供します。また、スポットインスタンス、オンデマンドインスタンス、様々なインスタンスファミリー(C、M、Rタイプなど)を柔軟に活用できるため、コスト効率を最大化できる点が魅力的でした。Karpenterは、スケジューリング不可能なPodがなくても、既存ノードに分散されたPodをより少ない数のノードに再配置してアイドルノードを削減するノード統合(consolidation)機能を通じて、コスト削減を積極的に試みます。
これに加えて、クラスターのリソース使用率とPodの状態を正確にモニタリングすることが重要であると判断いたしました。このため、Kubernetesモニタリングの事実上の標準であるPrometheusを選択しました。Prometheusは、様々なExporterを介してKubernetes内部のメトリクスだけでなく、ノード、Pod、コンテナレベルの詳細なメトリクスを収集し、時系列データベースに保存することができます。これらのメトリクスは、Karpenterがノードプロビジョニングおよびスケールダウンの決定を下す上で重要な洞察を提供し、Grafanaと連携して可視化することで、クラスターの運用状態を一目で把握できるようになります。KarpenterとPrometheusを統合することで、リアルタイムでクラスターの状態を把握し、それに基づいた最適なノード管理を自動化できるという結論に至りました。
実装プロセス:KarpenterとPrometheusを活用した動的オートスケーリング
ここからは、KarpenterとPrometheusを活用してKubernetesクラスターの動的オートスケーリングを実装する具体的なプロセスを説明いたします。実務ですぐに適用できるYAML/コード中心のガイドです。
Karpenterのインストールと初期設定
KarpenterはHelm Chartを通じて簡単にインストールできます。インストール前に、KarpenterがEC2インスタンスをプロビジョニングおよび管理できるよう、適切なIAM権限を付与する必要があります。これには、AWS EC2 APIにアクセスするためのService AccountとIAM Roleを作成するプロセスが必要です。
まず、OIDCプロバイダーを設定し、Karpenterが使用するIAM Policyを作成します。
aws iam create-policy \
--policy-name KarpenterControllerPolicy-${CLUSTER_NAME} \
--policy-document '{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "ec2:CreateLaunchTemplate", "ec2:CreateFleet", "ec2:RunInstances", "ec2:CreateTags", "ec2:TerminateInstances", "ec2:DeleteLaunchTemplate", "ec2:DescribeLaunchTemplates", "ec2:DescribeInstances", "ec2:DescribeImages", "ec2:DescribeSubnets", "ec2:DescribeSecurityGroups", "ec2:DescribeInstanceTypes", "ec2:DescribeInstanceTypeOfferings", "ec2:DescribeAvailabilityZones", "ec2:DeleteTags", "ec2:AssociateAddress", "ec2:DisassociateAddress" ], "Resource": "*" }, { "Effect": "Allow", "Action": "iam:PassRole", "Resource": "arn:aws:iam::${AWS_ACCOUNT_ID}:role/KarpenterNodeRole-${CLUSTER_NAME}" }, { "Effect": "Allow", "Action": "ssm:GetParameter", "Resource": "arn:aws:ssm:${AWS_REGION}::parameter/aws/service/ami-amazon-linux-2-recommended/recommendation/image_id" } ] }'
Karpenter 컨트롤러를 위한 IAM Role과 Service Account 생성
(IRSA: IAM Roles for Service Accounts)
TEMP_FILE=$(mktemp)
curl -fsSL https://raw.githubusercontent.com/kubernetes-sigs/karpenter/main/website/content/en/preview/getting-started/getting-started-with-karpenter/cloudformation.yaml > $TEMP_FILE
aws cloudformation deploy
--stack-name Karpenter-${CLUSTER_NAME}
--template-file ${TEMP_FILE}
--capabilities CAPABILITY_NAMED_IAM
--parameter-overrides ClusterName=${CLUSTER_NAME} ClusterEndpoint=${CLUSTER_ENDPOINT}
その後、Helmを使用してKarpenterをインストールします。
helm upgrade --install karpenter karpenter/karpenter --namespace karpenter --create-namespace \
--set serviceAccount.create=false \
--set serviceAccount.name=karpenter \
--set clusterName=${CLUSTER_NAME} \
--set clusterEndpoint=${CLUSTER_ENDPOINT} \
--set defaultProvisioner.enablePodDeletion=false
NodePoolおよびEC2NodeClassの定義
Karpenterの核となるのはNodePoolとEC2NodeClassのリソースです。NodePoolはノードプロビジョニングポリシーを定義し、EC2NodeClassはEC2インスタンスの詳細属性(AMI、セキュリティグループ、タグなど)を定義します。
以下の例は、スポットインスタンスを優先的に使用し、多様なインスタンスタイプを含むNodePoolの定義です。
apiVersion: karpenter.k8s.aws/v1beta1
kind: EC2NodeClass
metadata:
name: default
spec:
amiFamily: AL2
role: KarpenterNodeRole-${CLUSTER_NAME}
securityGroupSelector:
karpenter.sh/discovery: ${CLUSTER_NAME}
subnetSelector:
karpenter.sh/discovery: ${CLUSTER_NAME}
tags:
karpenter.sh/discovery: ${CLUSTER_NAME}
environment: production
---
apiVersion: karpenter.sh/v1beta1
kind: NodePool
metadata:
name: default
spec:
template:
spec:
nodeClassRef:
name: default
requirements:
- key: karpenter.k8s.aws/instance-category
operator: In
values: [c, m, r]
- key: karpenter.k8s.aws/instance-cpu
operator: Gt
values: ['2']
- key: karpenter.k8s.aws/instance-memory
operator: Gt
values: ['4Gi']
- key: karpenter.k8s.aws/instance-family
operator: In
values: [c5, c5a, m5, m5a, r5, r5a]
- key: karpenter.sh/capacity-type
operator: In
values: [spot, on-demand]
taints:
- key: dedicated
value: critical-workloads
effect: NoSchedule
startupTaints:
- key: karpenter.sh/provisioning
effect: NoSchedule
limits:
cpu: "1000"
memory: "1000Gi"
disruption:
consolidationPolicy: WhenUnderutilized
expireAfter: 720h
budgets:
- nodes: 1
このNodePool定義は、CPU 2コア以上、メモリ4GiB以上のC、M、Rインスタンスファミリー内でスポットインスタンスを優先的に使用するようKarpenterに指示します。特定のワークロードにノードを隔離するには、`taints`を活用することができます。
PrometheusとKube-state-metricsを活用したメトリクス収集
Karpenterが効率的に動作し、クラスターの状態を正確に把握するためには、Prometheusを通じたモニタリングが不可欠です。特にKube-state-metricsは、Kubernetes APIサーバーから様々なリソースの状態をメトリクスとして露出し、Prometheusが収集できるようにします。これにより、PodのPending状態、ノードのリソース活用率、ノードの状態など、Karpenterが意思決定を行う上で必要な核となる情報を得ることができます。
Prometheus Operatorを使用する場合、Kube-state-metricsを簡単にデプロイし、ServiceMonitorを通じてメトリクスを収集できます。
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: kube-state-metrics
namespace: monitoring
labels:
app.kubernetes.io/name: kube-state-metrics
spec:
endpoints:
- port: http-metrics
interval: 30s
selector:
matchLabels:
app.kubernetes.io/name: kube-state-metrics
namespaceSelector:
matchNames:
- kube-system
このServiceMonitorは、`kube-system`ネームスペースにデプロイされた`kube-state-metrics` Podの`http-metrics`ポートから30秒ごとにメトリクスを収集するようPrometheusに指示します。収集されたメトリクスを基にGrafanaダッシュボードを構成することで、Karpenterのノードプロビジョニングおよびスケールダウンイベントを追跡し、クラスターの全体的な状態をリアルタイムでモニタリングすることが可能です。一つのヒントとして、Karpenter自体もコントローラーメトリクスをPrometheusフォーマットで公開しているため、これを収集してKarpenterの内部動作までモニタリングすることをお勧めいたします。
結果と成果:コスト削減と運用効率の向上
KarpenterとPrometheusベースの動的オートスケーリングを導入後、弊社は期待以上の成果を達成することができました。定量的、定性的指標の両方で顕著な改善が見られました。
主要な成果の要約:
- クラウド費用削減:従来と比較して平均25%のクラウドインフラ費用削減効果が見られました。これは、スポットインスタンスの活用率増加とノード統合機能によるものです。特定のピークタイムにおける不要なオンデマンドインスタンスのオーバープロビジョニングが減少したことが主要な要因となりました。
- リソース活用率向上:クラスター全体のCPUおよびメモリのリソース活用率が平均60%から85%以上に向上しました。Karpenterがワークロードに最適化されたノードタイプを動的に選択し、不要なアイドルノードを迅速に回収したためです。
- プロビジョニング速度改善:新しいノードプロビジョニングにかかる時間が、従来の数分から数十秒以内に短縮されました。これにより、PodのPending状態が大幅に減少し、サービス遅延の問題が解消されました。
定性的な側面では、運用チームの業務負担が著しく軽減されました。手動でノードグループを調整したり、インスタンスタイプを変更する作業がなくなり、クラスターリソース不足による緊急対応状況も大幅に減少いたしました。開発チームはインフラリソースに関する心配なく、アプリケーション開発およびデプロイに集中できるようになりました。PrometheusとGrafanaを通じてリアルタイムでクラスターの状態と費用指標を可視化できるようになったことで、よりデータに基づいた意思決定が可能となりました。
Karpenter導入前後の比較:
| 項目 | 導入前 (Cluster Autoscaler) | 導入後 (Karpenter + Prometheus) |
|---|
| 平均クラウド費用 | 高い | 25%削減 |
| 平均リソース活用率 | 60% | 85%+ |
| ノードプロビジョニング時間 | 数分 | 数十秒以内 |
| スポットインスタンス活用率 | 限定的 | 非常に高い |
| 運用チーム業務負担 | 高い (手動介入が多い) | 低い (自動化された管理) |
| サービス可用性 | トラフィックピーク時に遅延の可能性 | 安定的な拡張性を確保 |
教訓と振り返り:予測不可能性の管理と継続的なチューニング
Karpenterの導入は非常に成功しましたが、いくつかの教訓も得られました。最も重要だったのは、スポットインスタンスの予測不可能性の管理でした。スポットインスタンスはコスト効率が高い一方で、いつでも回収される可能性があるという特性を持ちます。これに備え、クリティカルなワークロードには`nodeSelector`や`nodeAffinity`を使用して、オンデマンドインスタンスや特定の可用性ゾーンに固定する戦略を併用することが重要です。また、Karpenterの`disruption`ポリシーを慎重に設定し、ノード回収時のPodのサービス継続性を保証する必要があります。`consolidationPolicy`と`expireAfter`の設定は、初期段階では保守的に開始し、段階的に最適化していくことが効果的でした。
さらに、Karpenterの`NodePool`定義を通じて、多様なワークロード要件を満たすためのきめ細やかなチューニングが必要でした。例えば、特定のGPUワークロードのためにGPUを持つインスタンスタイプをプロビジョニングしたり、ストレージが多く必要なワークロードのためにストレージが十分なインスタンスタイプを指定するといった最適化作業が重要です。この過程で、Prometheusで収集したメトリクスを綿密に分析し、アプリケーションごとのリソース要件を正確に把握することが不可欠でした。予想に反して、すべてのPodに対して単一の`NodePool`で統合するよりも、特定のワークロードグループに特化した`NodePool`を複数運用する方がより効率的な場合も多く見られました。
意外な副次的効果としては、クラウドのリソースに対するチームメンバーの理解度が高まった点です。Karpenterがどのような基準でノードをプロビジョニングし、回収するのか、スポットインスタンスの長所と短所は何かなどを学習する中で、クラウドインフラに関する全体的な知識レベルが向上しました。これはDevSecOps文化の定着にも肯定的な影響を及ぼしました。
適用ガイド:段階的導入とモニタリング強化
KarpenterとPrometheusを活用したKubernetesのコスト最適化は、すべてのクラウドネイティブ環境に適用可能です。類似の環境で導入をご検討されている場合は、次のステップから適用してみてください。
- モニタリング環境の先行構築:Karpenter導入前に、Prometheus、Grafanaなどを活用して、現在のクラスターのリソース使用率、Podの状態、ノード数などのメトリクスを十分に収集し、分析できる環境を構築する必要があります。現在の問題点を正確に把握することが最適化の出発点です。
- 段階的導入ロードマップの策定:最初からすべてのワークロードにKarpenterを適用するのではなく、重要度の低い開発/テスト環境から開始し、段階的に本番環境へと拡張していくことが安全です。特定の`NodePool`と`EC2NodeClass`をまず定義し、これに特定の`taint`と`toleration`を使用してワークロードを段階的に移行する方法を検討することができます。
- スポットインスタンス活用戦略の策定:スポットインスタンスの回収に備えたPod Disruption Budget (PDB) の設定、Podの`terminationGracePeriodSeconds`の調整、そしてオンデマンドインスタンスの使用が必須であるワークロードの識別および隔離など、スポットインスタンスの活用戦略を事前に策定する必要があります。
- 継続的なチューニングと最適化:Karpenterの`NodePool`定義は一度設定したら終わりではありません。ワークロードの変化に応じて、`instance-type`要件、CPU/Memory制約条件、`disruption`ポリシーなどを継続的にチューニングする必要があります。Prometheusを通じて収集されたメトリクスが、このチューニングの重要なガイドラインとなるでしょう。
Karpenterは、単なるオートスケーリングツールを超え、Kubernetesクラスター運用のパラダイムを変え得る強力なソリューションです。Prometheusと統合してリアルタイムモニタリングとフィードバックループを構築すれば、コスト効率と運用安定性という二つの目標を同時に達成できるでしょう。今すぐKarpenterを導入し、クラウド費用最適化の旅を始めてみてください。