
2026/08/14 19:41
神のために、Kubernetes で CPU リミットを使用するのをやめてください
RSS: https://news.ycombinator.com/rss
要約▶
Japanese Translation:
元の要約は明確で正確であり、よく構成されています。厳密な改善は必要ありませんが、以下に全ての情報を保持しつつさらにより滑らかで流れの良い、やや推敲されたバージョンを示します:
改訂された要約: 主な推奨事項は、Kubernetes コンテナからの CPU リミットの廃止です。これは記憶容量制限(OOM キルを防ぐ保護機能)とは異なり、Linux CFS スケジューラによって 100 ミリ秒以内のウィンドウ内で人工的な凍結を引き起こします。このスロットリングは、処理器数に基づいて動的にリソースを割り当てる .NET アプリケーションに特に悪影響を与える、ガベージコレクションへの飢餓や沈黙するロジックエラーなどの重大な失敗につながります。
これらの制限を撤去することで、以下の顕著な利益が得られます:クラスタあたり年間約 92,000 ドルのハードウェア統合による節約、トラフィックスパイク時のテールレイテンシの減少、および計算集約型タスクに対する起動時間の大幅な短縮です。これを安全に実装するためには、組織はプロセッサ数(具体的には
DOTNET_PROCESSOR_COUNT)に対してフラートワイドデフォルトを設定し、スロットリング比率の観測可能性を向上させた上で変更を展開する必要があります。今後のステップとしては、長期にわたる P95 使用データに基づいてリソースリクエストを再サイズ化し、オートスケーリングを最適化することです。未信憑性の高いワークロードや Guaranteed QoS を必要とするワークロードについては、例外を残して近隣のアプリケーションに影響を与えることを防ぐ必要があります。本文
プラットフォームエンジニアリングにおける CPU リミットの再考と最適化戦略
結論 (TL;DR)
- CPU リミット (Limit) を削除してください。ノードに空き CPU があっても、多くのアプリケーションが頻繁にスロープ(凍結)してしまいます。
- CPU リクエスト (Request) は維持・最適化してください。これが真の保護機能であり、必要なリソースシェアを保証します。
- メモリリミットは維持してください。メモリリミットはノード保護のために不可欠です。
- パフォーマンス向上: トランザクション急増時のテールレイテンシ(p99)が崩壊せず、CPU 負荷の高い起動処理が約2 倍速になります。
- ハードウェア削減: クラスターでの CPU 予約を適正化することで、必要なノード数を削減できます。
- コスト削減: 実測データに基づくモデルでは、クラスターあたり年間数万ドル規模の節約が見込めます。
1. リクエストとリミットの役割の違い
CPU リクエスト(Request)と CPU リミット(Limit)は、異なる役割を果たす全く別の設定です。
| CPU リクエスト (Request) | CPU リミット (Limit) | |
|---|---|---|
| 正体 | 保証された CPU のスライス(一片) | ハードな天井(壁) |
| 他アプリへの保護効果 | あり。リクエストに基づいて CPU を共有する。 | なし。自らのみを制限するのみ。 |
| アイドル CPU の活用 | アプリが無料借用可能。 | 無駄になる(アクセスできない)。 |
- 重要な点: リミットを除去しても、アイドル CPU はリクエストベースで公平に共有され続けます。
- 詳細理論:
(リミット) とcpu.max
(リクエスト) の仕組みはcpu.weight
を参照ください。docs/01-theory.md
2. リミットがなぜアプリケーションを停止させるのか?(スロットリング)
Linux カーネルは、約 100ms の時間枠(窓)ごとに CPU 使用量を測定し、リミットを超えたらアプリ全体を凍結します。これをスロットリングと呼びます。
スロットリングのメカニズム
- 例: 500m のリミットを設定した場合、100ms の窓で最大 50ms しか CPU を使えない状態になります。
- 凍結: 予算を使い果たすと、アプリは次の時間枠来るまで完全に停止します。
.NET サービスへの悪影響
*.NET ランタイムには以下のスレッドが通常稼働しています:
- HTTP ハンドラー
- バックグラウンドコンシューマー
- GC(ガベージコレクション)*
これらは一つの予算を共有します。テストノード(4 コア)で 8 スレッドが同時実行し、50ms の予算を使い切ると:
- 凍結: 毎秒最大 10 回発生することがあります。
- 観測困難さ: ダッシュボードの「平均 CPU」はリミットに達していないように見えますが、実際には頻繁に凍結しています。これを見逃さないよう、
を監視してください。container_cpu_cfs_throttled_periods_total
実証結果
- 負荷: 平均して 320m の CPU を必要とする負荷を与え、500m のリミットを適用。
- 結果: 平均値はリミットを超えていませんでしたが、アプリケーションは停止していました。
- 遅延: 同じアプリでも、リミットにより最悪のレスポンスタイム(p99)が約2.4 倍に劣化しました。
3. リミットなしの場合、CPU はどう共有されるか?(CFS)
Linux には組み込まれたスケジューラ**CFS **(Completely Fair Scheduler) が存在します。このレフェリー(審判)がリクエストに基づいて CPU を公平に配分します。
- 仕組み:
- ポッドにはリクエスト値に基づく「ウェイト(重み)」が割り当てられます。
- ノードが繁忙している場合、ポッドはウェイト比率に応じて CPU を共有します。
- ポッドがアイドルの場合はそのシェアを開放し、他のポッドが利用できます。
- 結論: リミットを加える唯一の効果は、「凍結(スロットリング)」を引き起こすことのみです。
- 例外: 静的 CPU マネージャーを使用した Guaranteed QoS は別ですが、一般的なケースではリクエストだけで十分です。
4. 「すべてのポッドが CPU 100% を使うとノードは死ぬ」という懸念への回答
いいえ。最悪のケースであってもシステムは保護されています。
ノード上での CPU 不足時の挙動
- CFS の動作: すべてのポッドが同時稼働しても、リクエストウェイトに基づいて CPU を分割します。アプリは遅くなりますが、クラッシュしません(CPU は圧縮可能)。
- メモリとの比較: 記憶装置と違い、CPU が不足してもアプリは単に低速になるだけで「死亡(OOM キル)」しません。メモリリミットを維持する真の理由はここにあります。
ノード保護の 3 つの仕組み(リミットに依存しない)
- 予約されたシステム CPU: OS と kubelet が独自のスライスを確保しており、繁忙中のポッドが触ることができません。ノードの反応性が保たれます。
- スケジューラの数学: スケジューラは総リクエスト量 ≤ ノード容量であることを保証します。完全な競合でも各ポッドはリクエスト分得られます。
- CPU の圧縮性: 不足しても完了待ちになるだけでクラッシュしません。
| リソース | 不足時の挙動 | 対策 |
|---|---|---|
| CPU | アプリが一時低速(スロットリング) | リミットを除去し、リクエストを最適化 |
| メモリ | アプリがクラッシュ (OOM キル) | リミットを維持 |
「セーフティブレイク」としてのリミットの限界
- 多くのクラスターではピーク負荷よりも遥かに多い CPU を予約しています。
- 「すべてのポッドが 100%」になる瞬間は理論的であり、実際の問題は「アイドル CPU が使えないスロットリングされたアプリ」です。
- 懸念の払拭: リクエストこそが真のガードレールです。リミットを除去してリクエストを適正化することがコスト削減になります。
5. ベンチマーク結果:リミットの有無による比較
以下のテストは、同一のアプリケーションに対して「CPU リミットあり」「なし」で実施したものです。
- スパイク負荷時:
- リミットあり: 100ms 単位で**89%**の時間を凍結状態でした。
- リミットなし: スパイクを吸収し、全く減速がありませんでした(p99 レイテンシが 87% 改善)。
- 起動処理:
- CPU 負荷の高いコンパイル作業が約2 倍速になりました。
- PostgreSQL:
- スループットが約**10%**向上(測定値はヒントとして扱い、確実な改善です)。
注意: 起動時の恩恵は I/O 待ちの多いサービスでは小さいですが、CPU 計算量の多い処理では劇的です。詳細は
を参照ください。results/SUMMARY.md
6. ノイジー・ネイバー問題(ノイズのある近隣)
「他アプリケーションが CPU を貪欲に使えば、自分のアプリが遅くなるのではないか?」という懸念に答えます。
- テスト結果:
- 同じノード上で別のポッド(8 スレッド常時稼働、リミットなし)を配置しました。
- 保護された側: リクエストベースの公平共有により、近隣者の影響を受けません(p99 が劣化せず)。
- 保護できない側: 自らのリミットが厳しく設定されているアプリは、窓の一部でスロットリングされますが、自身の「ノックアウトベースライン」には劣化しません。
- 結論: リクエストが保護してくれます。リミットは近隣者を助けるどころか、自身を制限するだけです。
7. CPU リミットが引き起こす障害の連鎖
低いリミット設定は単に遅くさせるだけでなく、以下の致命的な連鎖を引き起こします:
- 凍結: アプリが CPU クォータを使い果たし、時間枠の大部分を停止状態のまま過ごす。
- GC の餓死: メモリ回収(GC)に必要な CPU が得られないため、一時的なパージではなく「死亡スパイラル」に陥る。
- **メモリ不足 **(OOM Kill) メモリ割り当て > 回収となり、ポッドがメモリ制限エラーで停止する。CPU の設定によるメモリ障害。
- 依存関係のタイムアウト: キャッシュ読み込みや DB クエリなどの外部呼び出しが失敗。
- デフォルト値へのフォールバック: 「false」を返す機能フラグなどが、意図せずデフォルト動作に切り替わる。
- トラフィック継続: リードネスプローブ(ポートが開いているか)のみチェックされるため、オーケストレータが故障したポッドを再起動しない。
インシデント対応への示唆:
- OOM キルは必ずしもメモリリークではない。スロットリングを確認してください。
- 問題を解決するために「リミットを上げる」のは本質的ではなく、問題の受容です。
8. .NET アプリケーション向けの追加ステップ
.NET は CPU リミットを見て自身をサイズ調整します(ProcessorCount や ThreadPool の設定など)。
| .NET が見る内容 | 500m リミットの場合 | リミットなし (4 コアノード) |
|---|---|---|
| ProcessorCount | 1 | 4 |
| ThreadPool 最小スレッド数 | 1 | 4 |
| GC モード | Workstation (単一ヒープ) | Server (マルチヒープ) |
- Server GC: マルチコアマシンのため高速だが、Workstation GC(単一ヒープ)と比較して不完全です。
- 推奨対応: 共有 Helm チャートなどで
をデフォルト設定し、リミットを除去してください。DOTNET_PROCESSOR_COUNT=4
9. 実施計画 (ロールアウト)
段階的な導入を推奨します。
- 環境変数の設定:
の他、Go (.NET
) や Python (GOMAXPROCS
) のランタイムも同様に適正化してください。num_workers - 観測可能性の確保: ダッシュボードにスロットリング比率を表示し、目標を~0にします。ノード CPU 圧力アラートを追加してください。
- セーフティブレイクとしてのリミット:
でデフォルトリクエストを設定し、必要ならLimitRange
で上限を管理します(コスト削減のため)。ResourceQuota - 段階的廃止: 非本番環境から始め、本番環境へ。
- リクエストは最適化済みであることを確認。
- リミットのみ削除(Rollback は容易)。
- リクエストの再調整: P95 使用量に基づき、適正なリクエスト値に設定します(自動スケーラーによるノード削減が可能に)。
10. 成果とコストメリット
コスト削減モデル
- 現状: クラスターで多くの CPU が予約されています。
- 改善: リミットを除去し、リクエストを適正化すると、必要なコア数が減少します。
- 例:500 コア予約 → 280 コア最適化 → 220 コアの解放。
- 仮定価格 $35/コア/月 × 220 コア ≈ $7,700/月(年間約$92,000)の節約。
KPI の改善目標
| KPI | 現状 | ロールアウト後目標 |
|---|---|---|
| スロットリング発生率 | 高い (10% 以上の凍結) | ~0 |
| トラフィックスパイク時の p99 | 377 ミ秒 | ~50 ミ秒 |
| 起動準備完了時間 | 20 秒 | 10 秒 |
| Postgres スループット | 1,720 TPS | ~1,897 TPS |
11. Q&A (一般的な質問への回答)
- Q: リクエストが小さすぎる場合は安全ですか?。
- A: 公平な共有はリクエストで重み付けされるため、正直(適正)である必要があります。除去時同時にリクエストも修正できます。
- Q: リミットを維持して適正化できないか?
- A: スロットリング下の使用量測定は不正確です。「本当の必要量」ではなく「制限された量」を測ることになります。
- Q: 複数のアプリケーションが一つのネームスペースを共有する場合。
- A:
とLimitRange
で保護されます。リミット自体が保護機能ではありません。ResourceQuota
- A:
- Q: リミットを維持すべき例外はあるか?
- A: はい、3 つあります。
- 不信任されたサードパーティワークロード。
- ベンチマークポッド。
- Guaranteed QoS クラスのピンド-CPU ポッド(レイテンシクリティカル)。
- A: はい、3 つあります。
12. 用語集
- Request: ポッドに保証される CPU のスライス(公平な共有の基準)。
- Limit: ハードな天井。リクエストを超えられない制限。
- **CFS **(Completely Fair Scheduler) Linux のスケジューラ。リクエストに基づいて CPU を公平に配分するレフェリー。
- Throttling: カーネルが予算を使い果たしたため、アプリを凍結させる状態。
- p99: 最悪の 1% のレスポンスタイム(ユーザーが感じるのはここ)。
- OOM Kill: メモリ制限を超えたために OS によってプロセスが強制終了されること。
- LimitRange: 設定されていないポッドへのデフォルトリクエストを設定するルール。
- ResourceQuota: ネームスペース全体のリソース使用上限を制限するルール。
- Headroom: 次の負荷増強(スパイク)に備えた安全なスペア容量。
参考資料と詳細:
: cgroup の理論背景docs/01-theory.md
: .NET ランタイムへの影響docs/02-dotnet.md
: PostgreSQL 固有の特性docs/03-postgres.md
: よくある誤解への回答 (Q&A)docs/04-objections.md
: コストモデルの詳細docs/05-cost.md
: 具体的なロールアウト手順docs/06-rollout.md
ライセンス: MIT License