PerfectScale
HPAとVPAの先へ:Kubernetesにおけるポッドの精密スケーリング
このページはEnglish、Deutsch、Español、Français、Italiano、Portuguêsでもご覧いただけます。
About Joshua Fox
Joshua Fox has been a software architect in innovative technology companies for 20 years. Now, he advises tech startups and growth companies on architecture and cost optimization for Google Cloud Platform and Amazon Web Services; also publishing articles, and speaking to cloud engineers.
He has a PhD from Harvard University and a BA in math from Brandeis.
My personal page1. はじめに
Kubernetesのオートスケーリングは、堅牢性を保ちながらコストを抑えるために設計されています。オートスケーラーはCluster Autoscalerでノード数を調整したり、Vertical/Horizontal Pod Autoscalerでポッドの数やサイズを調整したりできます。
ただし、限界もあります。標準的なオートスケーラーは平均CPU使用率といった粗い指標に依存しており、精度が十分とは言えません。その結果、リソースが無駄に確保されたままになるか、負荷時にシステムが不安定になるリスクを抱えることになります。本記事ではポッドオートスケーラーに焦点を当て、cAdvisor、Kube-State-Metrics、eBPFから得られる細粒度なテレメトリが、ワークロードを踏まえた精密なスケーリング判断をどう可能にするかを解説します。その判断はKubernetesのInPlacePodVerticalScaling APIを通じて、稼働中のポッドに適用されます。
2. 既存オートスケーリングの課題
標準的なオートスケーリングには、3つの根本的な課題があります。
- シグナルの粒度不足: 単純なCPU平均値では複雑なアプリケーションのパフォーマンスプロファイル全体を捉えきれず、スケーリング判断が遅れたり誤ったりします。
- HPAとVPAの競合: HPAはリクエストされたリソースを基準にスケールするのに対し、VPAはそのリクエスト自体を書き換えます。両者が独立して動作すると、相反する指示を出して振動を引き起こすおそれがあります。
- エビクション前提のリサイジング: 従来のVPAはリソース変更を反映するためにポッドのエビクションを伴うため、コールドスタート遅延を許容できないステートフルなワークロードやキャッシュ依存のワークロードを中断させてしまいます。
3. 多層テレメトリパイプライン
まずノードレベルのメトリクスをベースラインに据え、その上により細かいシグナルを得るためにテレメトリ層を4つ重ねます。
- Layer 0(ベースライン): Kubernetes Metrics Serverが提供するノードレベルのCPU・メモリ使用率。粗粒度の基盤となります。
- Layer 1: cAdvisorによるコンテナレベルのテレメトリ。この層では、平均値を押し上げるだけで持続的な負荷を示さない短時間のCPUスパイク、いわゆるマイクロバーストレイテンシと、メモリのワーキングセットを捕捉し、無害なカーネルページキャッシュの増加を理由に誤ったスケーリング判断が下されるのを防ぎます。
- Layer 2: Kube-State-Metrics(KSM)は、保留中のレプリカやHPAの飽和シグナルなど、ワークロードの健全性に関するコンテキストを提供します。これによりコントローラーは、追加のアクションを起こす前に、スケーリングが必要なのか、すでに進行中なのかを判定できます。
- Layer 3: eBPFはカーネルをプローブしてアプリケーションの深いシグナル、たとえばJVMのヒープ割り当てレートやガベージコレクション(GC)圧を可視化します。これによりOOMが発生する前に予防的なリソース調整が可能になります。たとえば、
usdt:/path/to/libjvm.so:hotspot:mem__pool__gc__beginなどのHotSpot USDTマーカーを用いたbpftraceプローブは、GC頻度の上昇をレイテンシに影響が出る数秒前に検知でき、コントローラーに先手を打つ余裕を与えます。 - Layer 4: GPUは今やAI/MLのコストとパフォーマンスを左右する重要なリソースです。NVIDIA DCGMを介したGPU固有のメトリクスにより、SM使用率やメモリ帯域の飽和といったGPU側のボトルネックに応じたスケーリングが可能になります。
4. 統合スケーリングコントローラーの構築
統合コントローラーは、これらのシグナルをステートマシンとして束ねます。stable、scaling-horizontal、scaling-vertical、cooldownといった明示的な状態を保持し、定義された条件が揃ったときだけ状態を遷移します。各シグナルを独立して評価するため相反する指示を出すリスクがある既存のルールエンジン型スケーラーとは、ここが大きく異なります。
コントローラーは3つのフェーズで動作します。
フェーズ1:シグナル集約
各テレメトリ層は、ワークロードごとに集約される単一の圧力メトリクスに対し、重み付きスコアを寄与します。Layer 3(eBPF/GC圧)などのアプリケーション層のシグナルには、より高い重みが与えられます。
フェーズ2:軸の調停
コントローラーは水平スケーリングが進行中かどうかを検知して競合を回避します。進行中のスケーリングは、AbleToScale=Trueであり、かつcurrentReplicasとdesiredReplicasの差がゼロでないことから判別できます。
一方、ワークロードが(アノテーションまたはStatefulSetの所有関係によって)シングルトンまたはステートフルと宣言されている場合、コントローラーは水平ではなく必ず垂直スケーリングへとルーティングします。
フェーズ3:再調整とクールダウン
スケーリングを実行した後は、クールダウン期間がスラッシングを防ぎます。コントローラーは、閾値を上回るまたは下回るメトリクスウィンドウが連続して2回以上観測された場合にのみ再評価を行い、一時的なスパイクで何度もスケールイベントが発生しないようにします。
5. In-Place Pod Vertical Scaling
2025年12月にKubernetes v1.35でGeneral Availability(一般提供)となったInPlacePodVerticalScaling APIにより、ポッドをエビクトせずにリソースを変更できるようになりました。コントローラーがポッド仕様にパッチを当てると、kubeletがコンテナのcgroupsを動的に更新します。接続の切断もプロセスの再起動も発生しません。
具体例を挙げます。eBPF層が「30秒以内にヒープ枯渇が起きそうな持続的なJava GC圧」を検知した場合、コントローラーはresources.requests.memoryとresources.limits.memoryを更新したPATCH /api/v1/namespaces/{ns}/pods/{name}を発行します。kubeletはコンテナのcgroupメモリ制限をその場で調整し、変更を反映します。
この仕組みは、JVMアプリケーションやデータベースのサイドカー、インメモリキャッシュのようにコールドスタート時間の長いサービスで特に価値があります。エビクション前提のリサイジングでは、数十秒に及ぶレイテンシスパイクが避けられないからです。
6. まとめ
標準的なHPAとVPAの構成では、高い稼働率とコスト効率の両立に失敗することが少なくありません。eBPFとcAdvisorによる細粒度の可観測性と、統合ステートマシンによる水平・垂直スケーリングの協調を組み合わせ、in-placeポッドリサイジングで変更を反映すれば、最小限の中断で自己修復可能かつ適正サイズなクラスタを構築できます。
PerfectScaleは、本記事で述べたアーキテクチャパターンに着想を得た実装です。筆者はDoiTのForward Deployed Engineeringに所属し、AWSおよびGoogle Cloud Platformに関するアドバイスをお客様に提供しています。ご質問があればお気軽にお問い合わせください。