異種ワークロードの配置設計 — バッチ・常駐サービス・GPU利用をどうノードに割り当てるか

異種ワークロードをKubernetesクラスター上に混在させるとき、「全ノードに乗せれば効率的」という直感は早期に裏切られます。バッチ処理が一時的にCPUを全量消費すれば、同じノード上の常駐サービスのレイテンシが跳ね上がりSLO違反を引き起こし、GPUノードが汎用ジョブに占有されて推論サービスが起動できない事態も珍しくありません。本記事では、ワークロードの消費パターンを分類したうえで、ノードプール設計・Taint/Toleration・スケジューリングポリシーの判断軸を体系的に整理します。判断の核心は「何を分けるか」ではなく、「なぜ分けるか・どこで線を引くか」の言語化にあります。

同一ホストに混在させてよいワークロードと分けるべきワークロードの整理

ワークロードの特性を「消費パターン」で分類する

配置設計において最初に問うべきは、そのワークロードが「定常消費型」か「バースト型」かという点です。常駐サービス(Webアプリ、APIサーバー、キャッシュ層)はリソース消費が比較的予測可能で、長時間にわたって一定の帯域を占有します。一方、バッチ処理や機械学習トレーニングは実行中に短時間でCPU・メモリ・I/Oを大量に消費し、完了後に一気に解放するという波形を描きます。この二種類を同一ノードに配置した場合、バーストのタイミングが常駐サービスの処理時間に直接干渉します。消費パターンの違いを先に分類しないまま進めると、後工程のノード設計が常に後手に回ることになります。

混在が許容されるケースと分離が必要なケースの境界線

混在が許容されるのは、ワークロードのリソース消費タイムラインが重複しない場合です。たとえば夜間バッチと日中サービスの組み合わせであれば、同一ノードへの配置でもSLO違反リスクは相対的に低いと判断できます。逆に、常時稼働しているストリーミング処理や応答遅延に敏感なAPIが同じノードに同居する構成では、バッチの突発スパイクが直撃します。現場では「ピーク時に常駐サービスのp99レイテンシが通常の3倍以上になった」という話をよく聞きます。分離の判断基準は「スパイクの影響範囲が許容SLO内に収まるか否か」です。収まらないなら、ノードを分けることが唯一の根本対処になります。

リソース要求の非対称性が引き起こす問題

混在配置で看過されがちなもう一点が、リソース要求の非対称性です。バッチジョブは大容量メモリを要求しながらCPU利用率は低め、常駐サービスはCPU消費が安定している一方でメモリは少量、というパターンが典型的です。一見補完的に見えますが、バッチがメモリを大量確保してOOMKillerが動いたとき、同じノードの常駐サービスが巻き込まれるリスクが存在します。またディスクI/Oを大量発生させるバッチが同居している場合、常駐サービスのログ書き込みやデータ読み出しがブロックされることもあります。リソース要求の「種類」だけでなく「タイミングと上限」まで踏まえて混在可否を評価することが、設計精度を高める重要な視点です。

スペック同一ノードと用途特化ノードの構成比較

フラットノード構成のメリットと限界

全ノードを同一スペックで揃えるフラット構成は、調達・管理・障害対応のシンプルさで優れています。スペアノードの準備が1種類で済み、オートスケール設定も単一のノードグループで完結します。10台前後の小規模クラスターであれば、フラット構成のほうが運用負荷は低く合理的な選択肢です。しかし、GPUワークロードや大容量メモリを要するバッチが登場した時点で、全ノードにGPUを積む非効率、あるいは「GPUなしノードにGPUジョブをスケジュールしない」というルール管理の複雑化という問題が生じます。フラット構成は「ワークロードが均質なうちは機能する」という前提条件を常に意識しておく必要があります。

用途特化ノードプール構成の設計思想

用途特化構成では、「CPUコンピュート向けノードプール」「メモリ最適化ノードプール」「GPUノードプール」のように、ワークロードの特性に合わせてノード群を分割します。この構成の本質は「リソース無駄を最小化しながら干渉リスクを構造的に排除する」点にあります。GPUノードは高単価のため、推論・トレーニング以外のジョブが誤ってスケジュールされないよう制御が必須です。一方、用途特化構成は管理対象のノードグループ数が増えるため、オートスケール設定・Taint管理・ノードイメージのメンテナンスが乗算的に増えます。ノードプールを3種類以上に分けるなら、その管理工数を見積もったうえで判断することが求められます。

構成比較:フラットノードと用途特化ノードプール

観点 フラットノード構成 用途特化ノードプール構成
管理シンプルさ 高い(単一グループ) 低い(グループ数分の設定が必要)
リソース効率 均質ワークロードでは高い 異種ワークロード混在時に高い
ワークロード分離 ソフト制御に依存 Taintで構造的に保証
コスト最適化 過剰スペックになりやすい 用途別に最適インスタンスを選択可
スケール対応 単一グループで完結 グループ単位で独立スケール可能
推奨規模 10台前後まで 20台以上・GPU含む構成

Taintとtolerationをどう設計するかの判断軸(用途別ノードプール)

Taintの粒度をどこに置くか

Taintの設計でまず決めるのは「粒度」です。粒度が粗すぎると意図しないジョブが紛れ込み、細かすぎると管理が煩雑になります。一般的な粒度として、ノードプール単位(gpu:NoSchedule、batch:NoScheduleなど)でTaintを付与し、そのプールを使ってよいワークロードだけがtolerationを持つ設計が広く採用されています。この設計の利点は「明示的に許可したワークロードしか乗れない」という排他制御がTaint一本で完結する点です。逆に、同一プール内でさらに細かく分けたい場合はTaintよりもNodeAffinityの活用を検討するほうが設計意図が明確になります。

tolerationの過剰付与がもたらすリスク

設計上の落とし穴として頻出するのが、tolerationの過剰付与です。「すべてのTaintを許容する」tolerationを汎用Deploymentに付与すると、GPUノードプールに一般ジョブが混入する事態が起きます。GPUノードは1台あたりのコストが通常ノードの5倍以上になるケースも多く、GPU未使用のジョブがそこで動き続けることは純粋な無駄です。toleration設計の鉄則は「必要最小限のTaintのみ許容する」ことです。CIパイプラインやHelmチャートのデフォルト値にtolerationが混入するケースもあるため、ノードプール設計時にtoleration付与の承認フローを設けることが長期的な統制に効きます。

ノードアフィニティとTaintの使い分け

TaintはノードからPodを「拒否」する仕組みであり、NodeAffinityはPodがノードを「好む・要求する」方向の指定です。この非対称性を理解したうえで使い分けることが、スケジューリング設計の精度を上げます。たとえばGPUノードへのGPUジョブ誘導は、Taintで「GPU以外をブロック」しながらNodeAffinityで「GPUジョブをGPUノードに引きつける」という二重制御が堅牢です。ソフトな優先度付けだけが目的なら、RequiredではなくPreferredのNodeAffinityだけで十分な場合もあります。「排他制御が必要か」「優先度付けで済むか」という問いから設計を始めることで、Taintを使うべきかNodeAffinityで足りるかの判断軸が自然に明確になります。

バースト消費するバッチが常駐サービスに影響しない分離設計

Namespace分割とResource Quotaによる上限設計

バッチと常駐サービスを同一クラスターに共存させる場合、Namespace分割とResource Quotaの組み合わせが基本的な保護層になります。バッチ用NamespaceにCPU・メモリの上限を設け、クラスター全体リソースの一定割合を超えないよう制御します。規模の目安として、常駐サービスに対してバッチが使えるCPU比率を40〜60%程度に抑えることで、スパイク時の影響を許容範囲内に収められることが多いです。ただしResource QuotaはNamespace全体の上限であり、個別Podのバーストを直接制御するものではありません。Pod単位での細かい制御はLimitRange設定と組み合わせることで補完されます。

QoSクラスの設計と優先度の明示化

KubernetesのQoS(Quality of Service)クラスはGuaranteed・Burstable・BestEffortの3段階に分かれます。常駐サービスには必ずGuaranteedを適用し、requests=limitsで明示的にリソース量を固定することが分離設計の前提です。一方、バッチジョブにBestEffortを適用すると、ノードリソースが逼迫した際に真っ先にEvictionの対象になります。重要度の低いバッチ処理を意図的にBestEffortに置くことで「常駐サービスを守る緩衝材」として機能させる設計もあります。判断のポイントは「バッチの再実行コスト」と「常駐サービスへの影響コスト」を天秤にかけることです。再実行が軽い処理ほどBestEffortへの配置が合理的な選択になります。

バッチ専用ノードプールとオートスケール設計の組み合わせ

根本的な分離を実現したい場合は、バッチ専用ノードプールを用意して常駐サービスのノードプールと完全に切り離す構成が最も確実です。この設計では、バッチがどれだけCPU・メモリを消費しても、常駐サービスのノードには物理的に影響が及びません。加えてバッチ専用プールにはオートスケールを積極活用し、ジョブ未実行時はノード数をゼロに近づける「スケールダウン最適化」を組み合わせると、コスト効率と分離の両立が図れます。オートスケールとバッチの組み合わせでは、ノードがプロビジョニングされるまでの起動遅延(一般的に2〜5分程度)を許容できるジョブかどうかを、設計フェーズで確認しておく必要があります。

GPUノードの台数とスポット活用の判断基準

推論ワークロードとトレーニングワークロードの分離判断

GPUノード設計でまず判断すべきは、推論(Inference)とトレーニング(Training)を同一ノードプールで扱うかどうかです。推論は常駐サービスに近い特性を持ち、低レイテンシ・高可用性が求められるため専用の常時稼働ノードプールが適しています。一方、トレーニングはバッチ処理に近く、時間がかかっても完了すればよいという性質からスポットインスタンスと相性がよいです。同一GPUノードプールに両方を混在させると、長時間のトレーニングジョブがGPUリソースを長期占有し、推論サービスの起動を妨げる事態が起きます。GPUノードプールは「推論用(常時起動・小台数)」と「トレーニング用(スポット・可変台数)」の2段構えが設計上の基本形になります。

スポットインスタンス活用の設計条件

スポットインスタンスをGPUノードに活用する場合、「中断されても再開できるワークロード」であることが必須条件です。機械学習トレーニングでいえば、チェックポイント保存を一定間隔(たとえば30分ごと)で実施し、中断後にそこから再開できるよう実装することが最低条件になります。スポットGPUノードのコスト削減効果はオンデマンド比で50〜70%になるケースもあり、チェックポイント実装工数に見合う判断材料です。一方、推論サービスのようにリクエストを受け続けるワークロードをスポットに乗せることは、ノード中断時のサービス断リスクが高く避けるべき選択です。「中断コスト」と「スポットコスト削減」のトレードオフを明確にしたうえで適用範囲を決定することが、スポット活用設計の要点になります。

スケジューリングポリシーをどこで統制するかの設計

Namespace単位とCluster単位の統制設計

スケジューリングポリシーの統制設計において、「どのレイヤーで制御するか」は重要な判断軸です。Namespace単位の統制は、Resource QuotaやPriorityClassの付与によって特定チームやアプリケーション群をスコープとした制御が可能です。クラスター全体での統制は、NodeSelectorやTaintといったノードレベルの仕組みで実施します。両者を組み合わせる際の原則は「ノードプール分離はTaintで保証、Namespace内の優先度はPriorityClassで管理」という役割分担です。どちらか一方に統制を集中させると、もう一方で抜け穴が生じやすくなります。統制レイヤーを設計時に明示しておくことが、運用フェーズでの混乱を防ぐ上で効果的です。

スケジューラー拡張の判断基準

標準のKubernetesスケジューラーでは対応しきれない要件が出てきたとき、カスタムスケジューラーやScheduling Frameworkの拡張を検討するタイミングが来ます。判断の基準は「標準スケジューラーのプラグイン設定で対応できるか」という点です。ラックやゾーンをまたいだ厳密なトポロジー制約や、特定アプリケーション間のアフィニティ・アンチアフィニティが複雑に絡む場合は、拡張を検討する価値があります。しかし多くのケースでは、標準のTopologySpreadConstraints・PodAffinity・PodAntiAffinityで対応できることが実情です。カスタムスケジューラーはKubernetesアップグレードのたびに互換性維持コストが発生するため、「本当に標準では無理か」を十分に検証してから導入を決断することが求められます。

落とし穴 — 混在配置で起きるリソース競合と予測不能なレイテンシのパターン

CPUスティールとメモリ圧迫による連鎖障害

混在配置で多く報告される問題の一つが、CPUスティール(他プロセスによるCPU時間の横取り)です。ノード上で複数のコンテナが動いているとき、バッチジョブがCPU requestを超えてスロットリングを受けている間、常駐サービス側もCFS(Completely Fair Scheduler)の影響でCPU割り当て待ちが発生します。構築を任されたある担当者は「常駐サービスのレイテンシが特定時間帯に跳ね上がる原因を数週間追ったが、バッチジョブのスケジュール時刻と完全に一致していた」と報告しています。このような問題は単体のメトリクスでは見えにくく、ノードごとのCPUスティール率とPod別CPUスロットリング率を突き合わせることで初めて可視化されます。

ノード間のリソース温度差によるスケジューリング偏り

クラスターを長期運用するうちに、ノード間でワークロードの偏りが生じるケースがあります。Kubernetesのデフォルトスケジューラーはリソース空き状況に基づいてスケジューリングしますが、長時間稼働しているPodが多いノードは「空きがあるように見えるが実際はメモリ消費が高い」という状態になりがちです。この温度差が積み重なると、新規にスケジュールされたバッチが特定ノードに集中し、そのノードのOOM発生率が上がるという連鎖が起きます。TopologySpreadConstraintsを適切に設定することで分散度を担保できますが、設定がなければデフォルト動作が偏りを許容します。この問題は「ある日突然特定ノードだけが不安定になる」という形で顕在化することが多く、初動の原因特定に時間を取られるパターンの代表例です。

配置設計チェックリスト

以下のチェックリストは、異種ワークロードの配置設計を進める際に判断漏れを防ぐための確認項目です。実装に入る前の設計フェーズで一通り答えを出しておくことで、後工程の手戻りを大幅に減らせます。手を動かす詳細設定については、各プロジェクトのセミナーや実装ドキュメントで深めてください。

  • 各ワークロードの消費パターン(定常型・バースト型・GPU集約型)を分類しているか
  • 混在させるワークロード間でリソース消費タイムラインが重複しないことを確認しているか
  • ノードプール構成(フラット・用途特化)の選択根拠を台数とワークロード種別で明示しているか
  • GPUノードに「推論用(常時起動)」と「トレーニング用(スポット可)」の区別を設けているか
  • 各ノードプールにTaintを付与し、toleration付与の承認ルールを設定しているか
  • 常駐サービスにGuaranteed QoSを適用し、requests=limitsで設定しているか
  • バッチ用NamespaceにResource QuotaおよびLimitRangeを設定しているか
  • スケジューリング統制の役割分担(Taint担当・PriorityClass担当)を明確にしているか
  • スポットインスタンスを使うワークロードに中断耐性(チェックポイント等)が実装されているか
  • ノードごとのCPUスティール率・スロットリング率をモニタリング対象に含めているか

▶ 手を動かして本番まで到達する

その設計判断を、RHEL10の本番環境で手を動かして固めませんか

ここまでの設計判断は、読めば腹に落ちます。ですが「自分の手で本番まで通しきる自信」は、それとは別の話になります。

最大6名の少人数で、構築実務者の到達点まで引き上げます。座学で終わらせず、本番相当の環境で判断を検証する2日間です。

異種ワークロード配置の設計判断を実装で確かめる上級Linuxハンズオンへ →

Linuxセミナー上級編(RHEL10)|最大6名の少人数ハンズオン