Windows Server 2019 上のリモートデスクトップ(RDS)環境で、「ユーザーごとに CPU 使用率を常に 10%に固定したい」という相談は少なくありません。しかし実際には、OS 標準機能だけで Linux の cgroups のような“ユーザー単位のハードキャップ”を実現することはできません。本記事では、その理由と現実的な代替案、そしてどうしても実現したい場合の技術的アプローチを、運用・開発の両面から詳しく解説します。
Windows Server 2019 で「ユーザーごと CPU 10%固定」はできる?結論
まず最初に結論です。
- Windows Server 2019 の標準機能だけで、「ユーザー単位で CPU 利用を常に 10%以下にハード固定することはできません」。
- 実現できるのはあくまで「プロセス単位」「セッション間の公平配分(Fair Share)」などであり、“ユーザーごとの上限値”という概念は OS にありません。
- WSRM(Windows System Resource Manager)は既に廃止されており、Server 2019 では利用不可です。
主な仕組みごとの位置づけを、ざっくり整理すると次のようになります。
| 機能・ツール | 制御単位 | ユーザーごと 10%固定の可否 | 補足 |
|---|---|---|---|
| WSRM(廃止) | プロセス / ユーザー / セッション | 理論的には近いことが可能だった | Server 2012 R2 まで。2019 では利用不可。 |
| RDS Fair Share(CPU) | セッション | 不可(動的公平配分のみ) | 高負荷時の「取り合い」を緩和する機能。 |
| プロセス優先度 / アフィニティ | プロセス | 不可 | 特定プロセスの「優先度を下げる」ことは可能。 |
| Job Object(ジョブオブジェクト) | ジョブ(プロセスのグループ) | 自作ツールであれば理論上は可能 | ユーザーのすべてのプロセスをジョブに入れる実装が必要。 |
| VDI / VM(Hyper-V 等) | 仮想マシン | 間接的に可能 | ユーザーごとに VM を分け vCPU 上限を設定。 |
つまり、「OS 標準機能の設定だけで、すぐにできる方法」はありません。以降では、現実的に取りうる代替案と、どうしても 10%固定が必要な場合の設計案を解説します。
なぜ Windows では「ユーザー単位の CPU 上限」が用意されていないのか
Linux の cgroups に慣れていると、「ユーザーごとの CPU 制限なんて当たり前では?」と感じるかもしれません。しかし Windows の設計は少し違います。大雑把に言うと、Windows のスケジューラは次のような考え方で動いています。
- CPU の実際のスケジューリング単位は「スレッド」。
- スレッドは「プロセス」に属し、優先度やアフィニティはプロセス / スレッド単位で調整される。
- 「ユーザー」という単位はあくまでセキュリティ(アクセス権)やログオンセッションの識別用であり、CPU スケジューラに直接は登場しない。
そのため、ユーザー別に CPU 使用率をハード上限で縛ろうとすると、
- そのユーザーが起動するすべてのプロセスを追いかける
- プロセス群をどこかの単位(ジョブなど)にまとめる
- まとめた単位に対して CPU 上限を設定する
といった「一段階ラップするしくみ」を自分で用意する必要があります。これを OS が標準では提供していない、というのが現状です。
OS 標準でできること・できないこと
RDS の Fair Share(フェアシェア)CPU
RDS の Fair Share は、「混雑時にセッション間で CPU を公平に配分する」ための仕組みです。多くの環境では既定で有効化されています。
- セッションごとの最近の CPU 使用状況を見ながら、スレッドのスケジューリング優先度を調整する。
- 結果として、一部ユーザーやアプリが CPU を独占しても、他のセッションにある程度 CPU が回るようになる。
- ただし、空いているときは一人のユーザーがサーバーの CPU をほぼすべて使うことも許容される(=ハードキャップではない)。
したがって、Fair Share の役割はあくまで「暴走防止&公平性の向上」であり、「各ユーザーは常に 10%以下」というような絶対上限を保証するものではありません。
プロセス優先度と CPU アフィニティ
タスクマネージャーや PowerShell などから、プロセスに対して優先度や CPU アフィニティを設定することができます。
- 優先度(BelowNormal / Normal / AboveNormal など)を下げることで、他プロセスよりも後回しで実行させる。
- CPU アフィニティを設定し、利用できる論理コアを制限する(例:16 コア中 2 コアだけに固定する)。
これらは「特定プロセスに CPU を使い過ぎてほしくない」という目的には有効ですが、
- ユーザーが別のプロセスを起動すれば新たに設定が必要
- ユーザー単位で統合的に扱うことはできない
という理由から、ユーザーごと 10%固定という要求にはフィットしません。
サードパーティ製ツール(例:Process Lasso)
Process Lasso のようなツールは、
- 特定プロセスの CPU 使用率を一定値以上にしない
- 優先度/アフィニティ設定を自動適用する
といった機能を提供しており、単体アプリの暴走抑止には便利です。しかし、
- 制御対象は基本的に「プロセス」であり「ユーザー」ではない
- RDS サーバー上の全ユーザーの、全プロセスをもれなく定義するのは相当な手間
- アプリの実行ファイル名が変更されたり増えたりすると設定漏れが生じやすい
といった理由から、「ユーザーごとに 10%」を厳密に実現する手段としては現実的ではありません。
目的別の現実解:何を守りたいのかを明確にする
ここからは、実務上よくある「本当にやりたいこと」ごとに、現実的な解を整理していきます。
目的 1:一部ユーザーの暴走で他セッションが固まるのを防ぎたい
この場合、「CPU を 10%に固定すること」自体が目的ではなく、
- あるユーザーが重い処理をしても、他ユーザーが巻き添えで固まらないようにしたい
ということが本当の目的であることが多いです。その場合の現実解は、
- RDS の Fair Share を有効に保つ
- セッション別 CPU の監視を整備する
です。
| やること | ポイント |
|---|---|
| Fair Share CPU の有効化確認 | 既定で有効になっていることが多いですが、グループポリシーやレジストリで無効化されていないか確認します。 |
| セッション別 CPU 監視 | パフォーマンスモニタで「Terminal Services」「Process」系カウンタを収集し、どのユーザーがどの程度 CPU を使っているかを見える化します。 |
| 異常検知&運用ルール | 一定時間以上にわたり極端な CPU 使用が続くセッションを検知したら、管理者に通知し、状況次第ではログオフさせるなどの運用ルールを決めておきます。 |
厳密な 10%固定ではありませんが、「他人の操作感を守る」という観点では最もコスパの良いアプローチです。
目的 2:特定アプリ(自社 VB.NET アプリ)の暴走を抑えたい
ユーザー単位ではなく、自社アプリが CPU を食い尽くすのを防ぎたいのであれば、制御対象を「ユーザー」ではなく「アプリ」に絞る方が現実的です。
具体的な方針は次の通りです。
- 対象:自社 VB.NET アプリの EXE(+関連する子プロセス)に限定する
- 方法:
- プロセス優先度を既定で Below Normal に設定
- CPU アフィニティを制限して、アプリが使えるコア数を減らす
- 必要に応じて、Process Lasso などのツールで「CPU リミッター」をその EXE だけに適用する
ポイントは、
- ユーザー全体ではなく、自社アプリだけを制御対象にすることで設定点数と運用コストを抑える
- アプリ側にも負荷を平準化する工夫(同時処理数の制限、重い処理のバッチ化)を入れておく
といったところです。
目的 3:どうしても「ユーザーごと 10%固定」が必要な場合
監査・契約・SLA などの理由で、論理的にでもよいので「ユーザーごとの CPU 利用はサーバー全体の 10%まで」という制約を課したいケースもあります。この場合の選択肢は次の 2 つです。
- Job Object を用いたカスタム実装
- VDI / 仮想マシンでの隔離(インフラ側で分離)
それぞれ見ていきます。
Job Object によるユーザー単位 CPU 上限のカスタム実装
Windows には Job Object(ジョブオブジェクト)という仕組みがあり、プロセスのグループに対してリソース制御を設定できます。
- ジョブに参加しているプロセス全体をまとめて管理できる
- CPU 使用率の上限(CPU Rate Control)を設定できる
この特性を利用して、
- ユーザーログオン時に、そのユーザーが起動するすべてのプロセスを 1 つのジョブに入れる
- そのジョブに「CPU 上限 10%」を設定する
という仕組みを作れば、理論上は「ユーザーごと CPU10%固定」を実現できます。
実装イメージ
実装方法はいくつか考えられますが、一例としては以下のような構成です。
- 各ユーザーセッションで動作する常駐ユーティリティを VB.NET / C# などで開発。
- そのユーティリティを、ログオンスクリプトやスタートアップで自動起動させる。
- ユーティリティは起動時に Win32 API を使ってジョブオブジェクトを作成し、CPU レートを設定する。
- ユーティリティ自身および、そのユーザーが起動する自社アプリ(+必要なら他のユーザープロセス)をすべてそのジョブに参加させる。
ジョブオブジェクトの API としては、例えば C# から P/Invoke で以下のような関数を叩くことになります。
// 非完全なイメージ(実際には構造体定義やエラーハンドリングが必要です)
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
static extern IntPtr CreateJobObject(IntPtr lpJobAttributes, string lpName);
[DllImport("kernel32.dll")]
static extern bool SetInformationJobObject(
IntPtr hJob,
int JobObjectInfoClass,
IntPtr lpJobObjectInfo,
uint cbJobObjectInfoLength);
[DllImport("kernel32.dll")]
static extern bool AssignProcessToJobObject(IntPtr hJob, IntPtr hProcess);
CPU 上限については JOBOBJECT_CPU_RATE_CONTROL_INFORMATION 構造体に対して、
- CPU レート制御を有効にするフラグ
- 「サーバー全体の何%まで」という値(例:10% = 10/100 * 10000 = 1000)
を設定するイメージになります。
Job Object 方式の課題と注意点
Job Object は強力ですが、実運用に乗せるにはかなりの検証が必要です。主な課題は以下の通りです。
| 課題 | 内容 |
|---|---|
| すべての対象プロセスをジョブに入れ続ける仕組み | ユーザーが新しいアプリを起動した際に、自動で AssignProcessToJobObject する仕組みが必要です。 |
| 除外対象の扱い | ウイルス対策ソフト、システムコンポーネント、RDS 関連プロセスなどを誤ってジョブに入れると障害の原因になります。 |
| 体感レスポンスの悪化 | 10%という上限は想像以上に厳しく、画面描画や入力レスポンスが「カクつく」可能性があります。 |
| OS 更新の影響 | Windows のビルド更新やパッチで Job Object の挙動が微妙に変わる可能性があり、継続的な検証が必要です。 |
また、「サーバー全体の 10%」という指定は、コア数によって体感が大きく変わることにも注意が必要です。
- 例:8 コアのサーバーなら 0.8 コア分、16 コアなら 1.6 コア分。
- IO 待ちやガベージコレクションなども絡むため、単純に「10%だから常に遅い」とは限らないが、多くの場合は体感レスポンスが落ちる。
以上から、Job Object は「どうしてもハードキャップが必要で、かつ開発・検証のコストを投資できる場合のみ検討すべき選択肢」と考えるのが現実的です。
仮想化(VDI / VM)でユーザーごとにリソースを分離する
もう一つのアプローチが、ユーザーごとに仮想マシンを割り当ててしまう方法です。Hyper-V や VMware などの仮想化基盤では、
- VM ごとに vCPU 数や CPU 上限・予約・シェアを設定する
- VM ごとにメモリ上限、ディスク IOPS 制限などを設定する
ことができます。この場合、
- 「1 人 1 VM」または「1 グループ 1 VM」という形で割り当て
- 各 VM の vCPU を少なめ(1〜2 vCPU)に抑える
- ホスト側のスケジューラで VM 間の公平性を確保
といった設計にすれば、実質的に「ユーザーごとに使える CPU を制限する」ことができます。
もちろん、
- ライセンスコスト(Windows / RDS / CAL)が増える
- VM 台数が増えることによる運用負荷の増加
といったデメリットはありますが、
- ハードキャップを OS より下のレイヤ(ハイパーバイザ)でかけられる
- SLA や契約で「ユーザー単位のリソース保証・制限」を明示しやすい
というメリットも大きく、「どうしても厳密な分離・保証が必要」な場合の最終手段と言えます。
追加で効く運用チューニングのポイント
ここまでの話に加えて、RDS 環境全体の安定性を高めるために有効な運用チューニングも整理しておきます。
自社アプリ側の同時処理数を制限する
VB.NET アプリ側で、
- 同時に並列実行するスレッド数
- 同時に処理するジョブの件数
- ディスクやネットワークへのアクセス頻度
をコントロールするだけでも、サーバー全体のピーク CPU を大きく下げられることがあります。
例えば、重いバッチ処理を行う機能があるなら、
- ユーザー操作とは切り離してサーバー側のキューに投げる
- サーバー側では一定数ずつ処理するワーカーサービスが捌く
などのアーキテクチャに変えることで、「1 ユーザーの操作が直接 CPU スパイクを生む」状態を避けることができます。
ウイルス対策ソフト(AV)の設定最適化
RDS サーバー上では、
- リアルタイムスキャン対象のフォルダ
- スケジュールスキャンの実行タイミング
によって CPU 負荷が大きく変わります。
- 自社アプリのワークフォルダや大量に一時ファイルが生成されるフォルダは、必要に応じてリアルタイムスキャンから除外する
- フルスキャンは深夜帯など、ユーザーがほぼいない時間帯に限定する
といった調整だけでも、「なぜか昼間だけ CPU が跳ね上がる」といった現象を抑制できることがあります。
監視と可視化の整備
最後に、「見えていないものは制御できない」という大原則があります。
- パフォーマンスモニタ(PerfMon)で以下のようなカウンタを収集する:
- Processor(_Total)\% Processor Time
- Process(アプリ名)\% Processor Time
- Terminal Services セッション別カウンタ
- 一定期間(例:1〜2 週間)のログを取り、どの時間帯・どのユーザーが負荷を生んでいるかを分析する
- 閾値を超えたらアラートを飛ばす仕組みを入れる(監視ツールや PowerShell スクリプトなど)
これにより、
- 「誰が、どのアプリで、どの程度 CPU を使っているのか」が分かる
- 設定変更やアプリ改修の効果を、数値で検証できる
ようになり、漫然と「なんとなく遅い」状態から脱却しやすくなります。 Linux の cgroups と Windows の違いをどう埋めるか Linux では cgroups を使って、 ユーザー / コンテナ / サービス単位で CPU・メモリ・IO を制御 ハードキャップやシェア(重み付き)を柔軟に設定 することが一般的です。一方、Windows では同等の仕組みはまだ限定的であり、 「プロセス / ジョブ単位」であれば Job Object でそれに近いことができる 「ユーザー単位」での制御は OS が直接サポートしていない という状態です。 そのため、Windows Server 2019 上で「Linux と同じことをやろう」と考えるよりも、 何を守りたいのか(公平性なのか、暴走抑止なのか、SLA なのか)を明確にする その目的に最もコストパフォーマンスの良い手段(Fair Share、プロセス制御、アプリ改修、仮想化など)を選ぶ という発想に切り替える方が、結果的に安全で現実的です。 まとめ:意思決定の指針 本記事のポイントをまとめます。 Windows Server 2019 の標準機能だけでは、「ユーザーごとに CPU を常に 10%以下に固定」することはできない。 RDS Fair Share はセッション間の CPU を「公平に」分ける機能であり、ハードキャップではない。 Process Lasso などのサードパーティ製ツールはプロセス単位制御が中心で、ユーザー単位の包括的 10%固定には運用負荷が高い。 どうしても 10%固定が必要なら、 Job Object を用いたカスタム実装(ユーザーの全プロセスをジョブに入れ CPU 上限を設定) VDI / VM によるユーザーごとの仮想マシン分離 を検討する必要がある。 一方で、実務上の落としどころとしては、 RDS Fair Share で暴走を防ぎつつ 自社アプリに限定したプロセス制御(優先度 / アフィニティ / CPU 制限)を行い アプリ側の同時処理数制御・AV 設定・監視の整備でピーク負荷をならす という組み合わせが最もバランスが良い。 「ユーザーごと CPU 10%固定」は、一見分かりやすい目標ですが、Windows Server 2019 では OS の設計思想と少しズレています。本当に守りたいもの(体感レスポンスなのか、SLA なのか、契約上の制約なのか)をもう一度整理し、ここで紹介した選択肢を組み合わせながら、現実的でメンテナンスしやすい構成を検討してみてください。

コメント