requests と limits の値を決める基準 — 運用で通用するルール
requests はスケジューリング用の予約、limits は上限という概念は K8s 中級 #4 で扱いました。ところが現場の質問は概念ではなく「それで、いくつと書くのか」です。この記事は値を決める実務のルールと、値が間違っていたときに起きる事故の形を整理します。Pod Pending と HPA の記事で先送りにした宿題でもあります。
requests — 実測の p95 の近くから始めます #
requests はスケジューラへの約束であり、ノードで競合が起きたときに保証される割り当て分です。だから基準は希望ではなく実測です。
- 平時の負荷で 1〜2 週間の使用量を観測し、CPU は平均と p95 の間、メモリは p95〜ピーク付近を出発点にします。メモリをより保守的に取るのは、CPU 不足は遅くなるだけですが、メモリ不足はプロセスが死ぬからです。
- 勘で始めるしかないなら、小さく始めて上げます。 逆方向(大きく取って削る)はまず起きないので、過大な予約が化石のように残ります。
- VPA を推奨(Off/Initial)モードで付ければ、実測ベースの推奨値がただで手に入ります。auto モードを有効にしなくても、算定の道具として十分に有用です。
requests が実測より大きく取られると何が起きるかは、Pod Pending の 2 番で見ました。クラスタの実使用率は低いのに予約が満杯でスケジュールが詰まり、ノードの費用は予約基準で出ていきます。過大な requests は目に見えない固定費です。
CPU limit — 掛けないことがデフォルトになり得ます #
CPU limit は論争のある項目で、論点はスロットリングです。CPU limit は cgroup の時間割り当て(quota)方式なので、limit に達したコンテナは次の周期まで強制的に止まります。このスロットリングが p99 レイテンシのスパイクの常連の原因です。実使用が limit の近くでもないのに、マルチスレッドのアプリの瞬間バーストが周期内で quota を使い切ってスロットリングされる、直感に反する事故もあります。
実務のルールはこう取れます。
- requests をきちんと取ったなら、CPU limit は省略をデフォルトとして検討します。CPU は圧縮可能なリソースなので、limit がなくても競合時は requests の比率で公平に分けられ、余った CPU は使うほうが得です。
- CPU limit が必要なケースは、マルチテナントで隣の暴走を強く隔離すべきとき、ベンチマークなど一貫した環境が必要なときです。その場合はスロットリング指標(
container_cpu_cfs_throttled_periods_total)を一緒に観測します。 - スロットリングが観測されたら、limit を上げるか外すのが先で、その次がアプリのスレッド数の調整です。
memory limit — 逆に、掛けるのが基本です #
メモリは CPU と違って圧縮不可能なリソースです。取り返す方法が殺すことしかないので、limit のないコンテナのメモリリークはノード全体を脅かし、ノードの圧迫時にはカーネルの OOM キラーがどのプロセスを殺すか予測しにくくなります。 memory limit を掛ければ、少なくとも「リークしたそのコンテナが、明確な OOMKilled という理由で」死にます。事故をなくす設定ではなく、事故を診断可能にする設定です。
- 値は p95〜ピークに余裕を乗せて(例: ピークの 120〜150%)取り、OOMKilled が観測されたら、リークか正当な要求の増加かをまず見分けます。コンテナ内の
freeがホストの値を見せる錯覚と cgroup OOM の区別は、メモリの記事で扱いました。 - requests と limits を同じにする(Guaranteed) 選択なら、重要ワークロードの予測可能性が得られます。次の節の退避の順序のためです。
QoS クラス — ノードが逼迫したとき誰が先に追い出されるか #
requests・limits の組み合わせが Pod の QoS クラスを決め、ノードのメモリが不足したときの退避(eviction)の順序に影響します。
| クラス | 条件 | 圧迫時 |
|---|---|---|
| Guaranteed | 全コンテナで requests=limits | 最後まで保護 |
| Burstable | requests < limits(または一部のみ設定) | 中間 — requests 超過分が大きいほど不利 |
| BestEffort | どちらもなし | 最初に退避 |
ルールは単純です。死んではいけないもの(データベース、コア API)は Guaranteed に、一般のサービスは Burstable に、BestEffort は本番に置きません。 BestEffort は設定を「しなかった」結果として生まれるので、LimitRange でネームスペースのデフォルトを強制し、事故で生まれるのを防ぐほうが安全です。
運用のルーティン — 一度決めて終わりではありません #
使用パターンはリリースごとに変わるので、算定にはルーティンが必要です。
- 観測ベースの再調整の周期を置きます(四半期、または大きなリリース後)。requests に対する実使用、スロットリング、OOMKilled の三つを見ます。ダッシュボードの構成は K8s 上級 #5 のスタックで十分です。
- 両側の事故パターンを指標で監視します。過大予約(クラスタの予約率が高く実使用が低い)と過小設定(スロットリング・OOMKilled の発生)は、逆方向の同じ問題です。
- HPA との相互作用を覚えておきます。 HPA の記事で見たとおり、ターゲットパーセントの分母が requests なので、requests を調整するとスケールの動作も一緒に変わります。二つは常にセットでレビューします。
まとめ #
- requests は実測(CPU は平均〜p95、メモリは p95〜ピーク)から出発し、VPA の推奨モードを算定の道具に使います。過大な requests は見えない固定費です。
- CPU limit はスロットリングのコストを理解して選びます。requests が適切なら省略が合理的なデフォルトで、掛けたならスロットリング指標を一緒に見ます。
- memory limit は掛けるのが基本です。事故を防ぐのではなく、OOMKilled という診断可能な形にする設定です。
- 重要ワークロードは Guaranteed、一般は Burstable、BestEffort は LimitRange で元から遮断します。
- 算定は一回きりではなくルーティンです。予約率、スロットリング、OOMKilled の三指標で四半期ごとに再調整します。