Kubernetes が遅くなる箇所 — API サーバー、etcd、スケジューラ
サービスのトラフィックは正常なのに kubectl get が数秒かかり、デプロイを掛けるとロールアウトが鈍く、たまに Webhook がタイムアウトします。ワークロード(データプレーン)ではなく、クラスタを制御する側(コントロールプレーン)が遅くなった状況です。この記事はそのボトルネックが生まれる 3 か所、API サーバー、etcd、スケジューラを順に診断します。Pod 単位のトラブルシューティングは Pending の記事と CKA トラブルシューティングシリーズで扱ったので、この記事はクラスタの規模が大きくなった後に直面する領域です。
まず区別からです。コントロールプレーンが遅くても、すでに動いている Pod のトラフィック処理はおおむね正常です。逆に「サービスが遅い」なら、この記事ではなくサーバーが遅い理由シリーズの領域です。症状が kubectl・デプロイ・コントローラーの反応速度に集中しているとき、この記事の手順をたどります。
1. API サーバー — 誰がこんなに叩いているのか #
すべてのリクエストは API サーバーを通るので、コントロールプレーン遅延の第一容疑者です。そして API サーバーを重くするのは、大半が人間の kubectl ではなく自動化されたクライアントです。
- 高価な LIST の繰り返し — 全ネームスペースの Pod をラベルフィルタなしで LIST する呼び出しは、etcd と API サーバーのメモリを大きく食います。これをポーリングループで回すオペレーター、CI スクリプト、監視エージェントが典型的な犯人です。よくできたコントローラーは LIST 一回 + WATCH(informer キャッシュ)で働きます。ポーリング LIST が見えたら是正の対象です。
- Webhook のチェーン — アドミッション Webhook が遅いと、すべての書き込みリクエストがその Webhook の遅延をそのまま被ります。Webhook のバックエンド Pod が不調なノードにいたり再起動中だったりすると、クラスタ全体の書き込みが一緒に遅くなります。
- 確認: API サーバーの指標
apiserver_request_duration_secondsを verb・resource 別に見れば何が遅いか、apiserver_flowcontrol_*(API Priority and Fairness)を見れば誰がキューに掛かっているかが分かります。監査ログ(audit log)を有効にしていれば、クライアント別の呼び出し量ランキングが出せます。 - 対処: 犯人クライアントのポーリングを informer パターンに改修、LIST にラベルセレクタ・ページネーションを適用、遅い Webhook のタイムアウト短縮と failurePolicy の見直しです。APF が標準で持つ優先度のおかげで、暴走クライアントがクラスタを丸ごと止めることは減りましたが、そのクライアント自身と同じ優先度クラスのリクエストは相変わらず遅くなります。
2. etcd — コントロールプレーンのディスク問題 #
etcd はクラスタの唯一の保存場所で、書き込みのたびにディスクへ fsync します。だから etcd の性能問題は大半がディスクの問題です。SSD の記事で扱った fsync 遅延の話が、ここでそのまま再登場します。
- fsync 遅延 — etcd が推奨する基準は fsync の p99 が一桁ミリ秒です。
etcd_disk_wal_fsync_duration_secondsの指標がこれを超えているなら、ネットワークストレージの IOPS 上限、同じディスクを使う隣人、PLP のないデバイスといったストレージ側の原因を見ます。etcd ログの「apply entries took too long」という警告も同じ信号です。 - DB の肥大化 — オブジェクトが多かったり変更が頻繁だったりすると(特に Events、そして状態を頻繁に更新するオペレーターの CR)、リビジョンが積もって DB が大きくなり、大きくなるほど遅くなり、クォータ(デフォルト 2GB)に達するとクラスタが読み取り専用に落ちる事故が起きます。
etcd_mvcc_db_total_size_in_bytesを推移で観測し、コンパクションが回っているか、デフラグが必要かを見ます。 - 大きいオブジェクト — 巨大な ConfigMap(ファイルの束を丸ごと)、大型の CRD オブジェクトは書き込み・ウォッチの両方を重くします。データファイルはオブジェクトストレージへ出すのが筋です。
- 対処: etcd 専用の低遅延ディスク(ローカル SSD 推奨)、イベントの TTL・オブジェクトのダイエット、定期的なコンパクション・デフラグです。マネージドクラスタ(EKS など)なら etcd はプロバイダーの担当ですが、オブジェクト数と変更率を低く保つのは相変わらずユーザーの担当です。
3. スケジューラ — キューが長いか、計算が重いか #
スケジューラの遅延は「Pod の作成 → ノードの割り当て」の間の時間として現れます。形は二つあります。
- キューが長いケース — 大量のバッチジョブが数千の Pod を一度に作ると、スケジューリングのキューが詰まります。これはボトルネックというより自然な待ちで、バッチワークロードの作成速度の調整が対処です。
- Pod 一つの計算が重いケース — 数千ノードのクラスタで、複雑な pod affinity/anti-affinity(特に topology ベース)は評価コストが大きいのです。
scheduler_scheduling_attempt_duration_secondsが伸びているなら、必須でない anti-affinity を topology spread constraints に置き換えるのが定石の対処です。
スケジューラが遅いことと、スケジュールする場所がないこと(Pending)は別の問題です。前者は時間がかかっても最終的には割り当てられ、後者はイベントに FailedScheduling が残ります。
診断手順のまとめ #
- データプレーンの問題と区別 — 症状が kubectl・デプロイ・コントローラーの反応に集中しているかを確認します。サービスの応答が遅いなら別の記事の領域です。
- API サーバーの指標 —
apiserver_request_duration_secondsでどの verb・resource が遅いか、APF の指標と監査ログで暴走クライアントを探します。 - etcd の指標 — fsync の p99 と DB サイズの推移を見ます。ディスクが原因の大半です。
- Webhook — 書き込みだけ遅いなら、アドミッション Webhook チェーンの遅延を見ます。
- スケジューラ — 割り当ての遅延なら、キューの長さとスケジューリング試行時間を見て、重い affinity を整理します。
まとめ #
- コントロールプレーンの遅さは、ワークロードのトラフィックとは別の層の問題です。症状が kubectl・デプロイ・コントローラーに集中しているかでまず区別します。
- API サーバーを重くするのは、大半が自動化クライアントのポーリング LIST と遅いアドミッション Webhook です。informer パターンとセレクタ・ページネーションが対処です。
- etcd の問題は大半がディスク(fsync)の問題で、残りはオブジェクトの肥大化です。fsync p99 一桁 ms と DB サイズの推移を指標に取ります。
- スケジューラは大量作成のキュー待ちと重い affinity の計算が遅延を作ります。topology spread への転換が定石です。
- マネージドクラスタでも、オブジェクト数・変更率・Webhook の品質はユーザーの担当として残ります。