「CPU使用率は低いのに遅い」――Kubernetesの遅延原因を見誤る典型パターン

本番環境のダッシュボードを見ると、CPU使用率は30%程度しかない。それなのにAPIのレスポンスは明らかに遅い。多くのインフラエンジニアが一度は遭遇する、この「矛盾した監視画面」こそが、Kubernetesの遅延原因を見誤らせる最初の罠です。
原因は、CPUリミットの実装がカーネルレベルで行われている点にあります。Kubernetes公式ドキュメントによれば、CPUリミットはCFS(Completely Fair Scheduler)によって強制され、コンテナがリミットに近づくとカーネルがCPUへのアクセスそのものを制限します。メモリのようにプロセスがOOM killで終了するわけではないため、パッと見ではエラーも出ず、単に「遅い」という現象だけが残ります。
さらに厄介なのは、多くの監視ツールが表示する「CPU使用率」は、スロットリングされた後の使用量である点です。Datadogの解説記事が指摘するように、コンテナは制限されているからこそ使用率が低く見えるのであって、「低い使用率=余裕がある」という直感はここでは通用しません。IBMのトラブルシューティング記事でも、CPU使用率だけを見た調査ではスロットリングを見逃しやすいことが指摘されています。

こうした"見た目に反する遅延"の調査は、監視スタックが最初から整っていないと非常に時間がかかります。KuboのようにPrometheus・Grafanaが標準搭載されたマネージドK3s環境であれば、こうした指標の可視化に追加の構築コストがかかりません。
Podを増やす(HPAでスケールする)ほど状況が悪化する理由

CPUスロットリングを疑い、レプリカ数を増やしてスケールアウトしても遅延が直らない、あるいは悪化することがあります。これは、ボトルネックがアプリケーション層やデータベース層にあるケースです。
代表的なのがデータベースへのコネクションプール枯渇です。PostgreSQLはmax_connectionsパラメータで同時接続数の上限を管理しており、PlanetScaleの解説によれば、Postgresは接続ごとにOSプロセスをフォークするため、接続数が増えるほどメモリとコンテキストスイッチのコストが跳ね上がります。ここでHPAがPodを増やすと、各Podが個別にコネクションプールを持つ設計であれば、合計の接続要求数はさらに膨らみます。
CubeAPMの技術記事は、Kubernetes上のアプリケーションでこの種の接続枯渇が起きるパターンを具体的に解説しています。Podを増やすことは本来「処理能力を増やす」対策であるはずが、データベース側の接続上限に対しては逆効果になり、「too many clients」といったエラーが増えるという逆転現象が起きるのです。
この問題を回避する仕組みとして知られるのがコネクションプーラーです。Neonの公式ドキュメントが説明する通り、PgBouncerのようなプーラーを中間に置くことで、多数のアプリケーション接続を少数の実DB接続に多重化でき、max_connectionsの上限に振れることなくPodの水平スケールが可能になります。
見るべき指標はCPU%ではない――Kubernetesの遅延原因の切り分け方

CPU使用率という単一指標だけを見ている限り、スロットリングも接続プール枯渇も両方見逃す可能性があります。Last9の技術ブログは、container_cpu_cfs_throttled_seconds_totalというPrometheusメトリクスこそが、CPU使用率ではなく「実際にスロットルされた時間」を直接示す指標だと解説しています。CPU使用率が低くても、このメトリクスが高ければスロットリングが疑われます。
一方、データベース側の遅延を切り分けるには、コネクションプールの使用率やキューでの待機時間を可視化する必要があります。HPAをCPUだけで運用している場合、Google Cloudの公式ドキュメントが示すように、Kubernetes 1.6以降のCustom Metrics APIを使えばPrometheus経由でアプリケーション固有のメトリクス(キュー長やレスポンスタイムなど)をHPAのスケール条件に組み込むことができます。CPU使用率だけでスケールを判断する構成から脱却する第一歩がここにあります。
つまり、Kubernetesの遅延原因を正しく特定するには、CPU%・CFSスロットリング時間・DB接続プール使用率・p99レイテンシという最低4種類の指標を並べて見る必要があります。どれか一つだけを見ていると、必ず別の原因に目を塞ぐことになります。
正しい対処法:Podを増やす前にやるべきこと

CPU limitはレイテンシ重視のサービスでは外す選択肢もある
groundcoverの解説は、CPU limitを設定しすぎると、ノードに空きキャパシティがあってもコンテナが不必要にスロットルされる場合があると指摘しています。すべてのワークロードにCPU limitを設定するのではなく、requestsで優先度を確保しつつ、レイテンシに敏感なサービスではlimitを緩めるという選択肢を検討する価値があります。
HPAのスケール条件をCPUからアプリケーション指標に変える
前述のCustom Metrics APIを使い、CPU使用率だけでなくリクエストキューの長さやレスポンスタイムでスケールする設定に切り替えることで、「CPUは空いているのに詰まっている」状態を早期に検知できます。
コネクションプーラーを間に挟む
Komodorの記事も指摘する通り、リソースの誤診断は運用コストの多くを消費します。PgBouncerなどのプーラーをsidecarまたは中間層に配置すれば、Pod数の増加とDB接続数の増加を分離できます。
maxReplicasはDB側の受け入れ可能上限から逆算する
HPAのmaxReplicasを「クラスタのCPUキャパシティ」だけで決めるのではなく、「データベースが安全に受け付けられる接続数 ÷ Pod1台あたりの接続数」から逆算して設定することで、スケールアウトが自滅的にエラーを増やす事態を防げます。

こうした指標を1画面で見られる状態を最初から用意しておくことが、誤診断による対応の長期化を防ぐ最短ルートです。Kubo CloudはK3sベースのマネージドKubernetesで、Prometheus + Grafanaが標準搭載されているため、CFSスロットリング時間やカスタムメトリクスによるHPA設定を、追加の監視基盤構築なしにすぐに始められます。
まとめ:スケールは魔法ではない
「Podを増やす」は便利な対処法ですが、万能ではありません。ボトルネックがカーネルレベルのCPUスロットリングやデータベース側の接続プール枯渇にある場合、スケールアウトは問題を隠すどころか、接続エラーという新しい問題を生み出すことさえあります。
Kubernetesの遅延原因を正しく特定するには、CPU使用率という単一の数字を信じず、スロットリング時間・DB接続プール使用率・p99レイテンシまで見える状態を整えることが第一歩です。
EKSやAKSでゼロから監視スタックを構築するとコストも時間もかかりますが、K3sベースでPrometheus・Grafanaが標準搭載されたKuboなら、月額48,000円〜でこうした可視化環境をすぐに使い始められます。ベンダーロックインのない標準Kubernetesのまま、誤診断に時間を溶かさない運用を試したい方は、お問い合わせから相談してみてください。