Skip to main content

CPU使用率は正常なのにリクエストが詰まる。KubernetesでHPAにKEDAを足すべきタイミング

CPU使用率は正常なのに、なぜリクエストが詰まるのか

section01

KubernetesのHorizontalPodAutoscaler(HPA)を本番で運用していると、「CPU使用率もメモリ使用率も正常なのに、処理が追いついていない」という状況に出くわすことがある。原因は単純で、HPAはKubernetesの公式ドキュメントが定義する通り、「観察されたリソース使用率(CPUやメモリ使用量など)に基づいてワークロードのレプリカ数を周期的に調整するコントローラー」であり、キューの滞留数やKafkaのコンシューマーラグといった「クラスタの外側」にあるシグナルを見ることができない構造になっているからだ。

典型的なのは、非同期ジョブワーカーやメッセージキュー処理を担うPodだ。CPU負荷は低いまま、処理待ちのメッセージだけが積み上がっていく。HPAにこの状態を検知させる方法はない。ここで登場するのがCNCFの卒業プロジェクトであるKEDA(Kubernetes Event-Driven Autoscaling)だ。KuboのようにK3sベースでKubernetesクラスタを運用している現場でも、この「HPAの死角」は同じ形で現れる。

KEDAはHPAを置き換えるのではなく補完する

section02

ここで多くのチームが誤解するのが、「KEDA vs HPA」を二者択一の比較だと捉えてしまうことだ。実際にはKEDA公式のScaledObject仕様が明記している通り、KEDAはHPAを自動的に生成・管理する仕組みであり、HPAそのものを置き換えているわけではない。

KEDAのGitHubリポジトリの説明でも、KEDAは「event driven Kubernetesワークロードのためのきめ細かいオートスケーリング(ゼロからのスケールも含む)」を実現するコンポーネントと位置づけられている。ScaledObjectというカスタムリソースを作成すると、KEDAはその定義に基づいてHPAリソースを裏側で生成し、外部イベントのメトリクスをHPAが参照できる形に変換して橋渡しする。

つまり構造としては、HPAが「スケーリングの実行エンジン」であり、KEDAはその手前に立つ「メトリクス翻訳レイヤー」だ。レプリカ数が1以上の領域ではHPAがスケーリングを主導し、0から1への立ち上げだけをKEDAが制御するという役割分担になっている。この構造を理解しないまま「KEDAを入れればHPAは不要」と考えると、併用設計でつまずく。

K3sで使い分ける判断基準(3つの軸)

section03

KEDAとHPAの比較ガイドが提示する実践的な判断軸は、次の3つに整理できる。

1. メトリクスソースがクラスタ内部か外部か

同期型のAPIサーバーやWebフロントエンドのように、CPU・メモリがそのまま負荷の指標になるワークロードはHPA単体で十分だ。一方、メッセージキューの深さ、Kafkaのコンシューマーラグ、外部クラウドサービスのキュー長といった「クラスタの外側」にあるシグナルでスケールしたい場合は、KEDAの出番になる。

2. scale-to-zeroが必要かどうか

HPAには「1レプリカ未満にはスケールできない」という構造的な制限がある。常時起動のコストを許容できない非同期ワーカーやバッチ処理では、アイドル時にゼロまでスケールダウンできるKEDAの価値が大きい。KEDA導入によるコスト効果を分析した記事でも、アイドル状態のPodを完全に削除できる点が最もインパクトの大きいコスト削減要因として挙げられている。

3. 負荷のバースト特性

緩やかに増減する負荷であればHPAのポーリング周期でも追従できる。しかし、突発的でバースト性の高い負荷(セール開始直後の注文処理など)には、外部イベントをトリガーに即座に反応できるKEDAの仕組みが向いている。

多くの本番環境では、これらを二者択一にせず「同期サービスにはHPA、非同期ワーカーにはKEDA」という形で併用しているのが実情だ。K3sのような軽量Kubernetesディストリビューションを使っている環境でも、この判断軸はそのまま当てはまる。

併用設計で事故らないための pollingInterval / cooldownPeriod 設定

section04

HPAとKEDAを併用する際に実務上つまずきやすいのが、ScaledObjectのデフォルト値をそのまま使ってしまうケースだ。KEDA公式のScaledObject仕様によれば、主要パラメータのデフォルトは次の通りになっている。

  • pollingInterval(デフォルト30秒): 各トリガーをチェックする間隔。ただしこれは0→1スケーリングにのみ関与し、1→Nのスケーリングは下位で生成されるHPAが自身のポーリング周期(デフォルト15秒)で主導する
  • cooldownPeriod(デフォルト300秒): 最後にトリガーがアクティブだったと報告されてから、スケールダウンを開始するまでの待機時間
  • minReplicaCount(デフォルト0)/ maxReplicaCount(デフォルト100): スケールの下限・上限

ここで注意すべきなのは、minReplicaCountが0より大きい設定になっている場合、ワークロードは決してゼロにはスケールされず、スケーリングの主導権は実質的にHPA側へ完全に移るという点だ。「KEDAを入れたのにscale-to-zeroが効かない」という事故の多くは、このminReplicaCountの設定ミスか、cooldownPeriodがコールドスタートの重いワークロードに対して短すぎることが原因になっている。pollingIntervalとcooldownPeriodの値を近づけておくことで、スケールアップとスケールダウンが交互に起きる「フラッピング」を防ぎやすくなる。

HPA単体のwalkthroughにある kubectl autoscale deployment php-apache --cpu=50% --min=1 --max=10 のようなシンプルな設定しか経験していないと、KEDAを足したときにこのパラメータ間の依存関係を見落としがちだ。併用設計をする際は、まずHPA単体での挙動を確認したうえで、ScaledObjectのパラメータを1つずつ検証していくのが安全だ。

K3sクラスタでのHPA運用そのものにクセがある場合は、HPAのレプリカ数が数分おきに増減する問題や、VPAによるリソース見積もりの落とし穴も合わせて確認しておくと、KEDA併用時の挙動分析がしやすくなる。

こうした併用設計や監視基盤の構築は、K3sベースでPrometheus/Grafanaが標準搭載されたマネージドKubernetes環境なら、メトリクス基盤をゼロから構築する手間をかけずに検証を始められる。KuboはAI-Driven DeploymentとCaptain UIにより、ScaledObjectの挙動を可視化しながら調整できる。

まとめ

HPAとKEDAは対立する選択肢ではなく、役割の異なるレイヤーだ。HPAはCPU・メモリといった内部リソースメトリクスに基づく「土台」であり、KEDAはキューの深さや外部イベントに基づく「拡張」として機能する。K3s環境で判断に迷ったら、まず「メトリクスソースが内部か外部か」「scale-to-zeroが必要か」「負荷がバースト性かどうか」の3軸で整理し、併用する場合はpollingIntervalとcooldownPeriodの依存関係を理解した上でチューニングすることが事故を防ぐ鍵になる。

この併用設計をコスト効率よく検証したいなら、EKSの約58%のコストで本格的なKubernetesを運用できるKubo Cloudで試すという選択肢もある。構成の検証コストを抑えたい場合は、料金プランを比較してみてほしい。

Related articles

← Back to all posts