VPSのCPU steal timeとnoisy neighbourの見分け方
VPSのCPU steal timeは、実行可能なのに他のゲストへCPUを奪われた時間です。vmstatのst列を読み、自身の過負荷とnoisy neighbourを切り分けます。
CPU steal time が実際に測定するもの
CPU steal time は、仮想 CPU が実行可能な状態で待機すべき処理もなく、ハイパーバイザーが物理コアを別のゲストに割り当てていた時間の割合です。処理はキューに入っていました。コアは別の場所で使われていました。Linux はこのサイクルを個別にカウントし、st として報告します。これにより、「サーバーがビジーである」状態と「実行の順番を待っている」状態を区別できます。
この違いが、カウンターが存在する理由です。プロセス自身が CPU 上で消費した時間は、us (user) または sy (system) として報告されます。タスクがストレージ処理でブロックされている時間は、wa (I/O wait) として報告されます。vCPU (virtual CPU) が実行可能で、run queue 上にあり、I/O の処理待ちもなく、それでも実行されていない場合は、st として報告されます。スケジューリングの判断は、ホスト上の、あなたのサーバーより 1 層下で行われるため、サーバー内部の処理ではこの状態を解消できません。
これは、1 台の物理マシンを VPS 間で共有する仕組みから直接生じます。通常の原因は同居する別のゲストです。同じノード上の別のゲストが高負荷で動作しているため、ホストがコアを複数のゲストに分配します。見落とされやすい原因もあります。多くのプロバイダーは、共有 vCPU に物理コアの一部だけを使用できる上限を設定しています。複数のハイパーバイザーでは、その制限による待機時間がゲスト内で steal として計上されます。したがって、st の値が高い場合、コアが自分に割り当てられていなかったことは分かります。ただし、誰がコアを使用していたかまでは常に分かりません。
steal の値はどこから来るのか
カーネルはホストを認識できないため、steal を単独で測定できません。ハイパーバイザーが値を通知します。KVM では、ホストが vCPU ごとのカウンターをゲストと共有するページに書き込みます。ゲストは、カーネルが CONFIG_PARAVIRT_TIME_ACCOUNTING を有効にしてビルドされている場合に、その値を合計します。これはすべてのディストリビューションカーネルで有効です。Xen も runstate area を通じて同じ値を報告します。合計値は、次の 1 箇所から userspace に渡されます。
head -1 /proc/statこの cpu 行には、ブート後の USER_HZ ティック単位のカウンターが 10 個、次の順序で格納されています。user、nice、system、idle、iowait、irq、softirq、steal、guest、guest_nice です。steal はラベルの後ろから数えて 8 番目の値です。以下のすべてのツール、vmstat、top、mpstat、および Prometheus exporter は同じフィールドを読み取り、2 つのサンプルから割合を計算します。
最も重要な点は、ハイパーバイザーがこのカウンターを提供しない場合、そのフィールドが永久に 0 のままになることです。その場合、その値を使うすべてのツールは、ホストが過負荷でも正常な 0.0 を報告します。KVM と Xen はこの値を提供します。VMware と Hyper-V のゲストでは、通常は 0 のままです。0 を信頼する前に、プラットフォームを確認してください。
systemd-detect-virtこのコマンドは、kvm、xen、vmware、microsoft などのプラットフォーム名を表示します。ベアメタルでは none と表示されます。コンテナ内では、lxc、docker、podman など、コンテナのランタイムが表示されます。これはコンテナについての情報であり、その下で動作するマシンについての情報ではありません。kvm では、0 はホストが適切にリソースを割り当てていることを示す実際の証拠です。フィールドに一度も値を設定しないプラットフォームでは、0 からは何も判断できません。その場合は、実際の処理の実行時間を測定して競合を判断する必要があります。
VPS で CPU steal time を確認する方法
vmstat は procps パッケージに含まれています。ほぼすべての Ubuntu および Debian VPS イメージに存在しますが、一部の最小構成のコンテナイメージには含まれていません。そのため、依存する前にインストールしてください。
sudo apt-get update
sudo apt-get install -y procps
vmstat --version
vmstat 1 5vmstat --version を実行すると、vmstat from procps-ng 4.0.4 のような行が表示されます。これが表示されれば、ツールはインストール済みで、実際のカーネルカウンターを読み取っています。続いて vmstat 1 5 は、1 秒間隔で 5 回サンプルを取得します。
procs -----------memory---------- ---swap-- -----io---- -system-- -------cpu-------
r b swpd free buff cache si so bi bo in cs us sy id wa st gu
1 0 0 3216484 98304 1284360 0 0 4 18 62 110 3 1 96 0 0 0
2 0 0 3216232 98304 1284360 0 0 0 0 248 431 6 2 84 0 8 0右側の cpu ブロックで、st 列を確認してください。現在の procps-ng ビルドでは、その右側に KVM ゲスト時間用の gu 列が表示されるため、st は最後から 2 番目です。リリースによってこの位置が変わっているため、列の位置ではなくヘッダー名で確認してください。
読み取りを正確にするには、2 つの点に注意します。最初のデータ行は起動後の平均値なので無視し、その後の行を読み取ってください。また、steal は断続的に発生するため、1 回のサンプルだけでは測定になりません。vmstat 1 60 を実行し、結論を出す前に 1 分間すべてのサンプルを確認してください。
top でも同じ値を %Cpu(s) サマリー行の st フィールドで確認できます。
%Cpu(s): 6.2 us, 2.1 sy, 0.0 ni, 83.9 id, 0.0 wa, 0.0 hi, 0.3 si, 7.5 stCPU コアごとの詳細を確認するには、sysstat を追加してください。
sudo apt-get install -y sysstat
mpstat -P ALL 1 5mpstat は CPU ごとに 1 行を出力し、%steal 列を表示します。これにより、すべての vCPU が影響を受けているのか、1 つだけが影響を受けているのかを確認できます。サポートチケットに必要な履歴を残すには、画面で読み取るのではなく、サンプルを保存してください。
date -u | tee -a ~/steal.log
mpstat -P ALL 1 5 | tee -a ~/steal.log原因が疑われる時間帯に cron から実行してください。そうすれば、そのファイルを使って、プロバイダーに「昨夜は遅く感じた」と伝えるのではなく、正確な 10 分間のデータを提示できます。
steal の値は何を意味しますか?
- 一定した
0.0。 正常です。または、プラットフォームが steal をまったく報告していません。安心する前にsystemd-detect-virtで確認してください。 - 数秒間続く数パーセントのスパイク。 共有ノードでは通常の範囲です。隣接するテナントでビルドが開始されたか、ホストがバックアップを実行しています。
- 共有プランで継続する 1 to 5 percent。 想定内です。料金には CPU の共有が反映されています。
- 継続する 5 to 10 percent。 測定可能な速度低下です。証拠の記録を開始し、数日間にわたって同じ時間帯を比較してください。
- 一度に数時間、10 percent を超える状態。 そのワークロードに対してノードの収容過多が起きています。サポートチケットの起票や移行を検討する水準です。
これらの範囲は仕様ではなく、読み取りの目安として扱ってください。共有プランでは、どのプロバイダーも steal の保証を公開していないためです。実行している処理に照らして判断してください。夜間のバッチジョブであれば、15 percent の steal が発生しても気付かない場合があります。レイテンシーに敏感なサービスでは、平均値が警戒すべき水準に達するよりはるか前に p99 に現れます。そのため、トレーディングボットなどのレイテンシーに敏感なワークロードは専用コア上で実行してください。
スティール時間のコストはどの程度ですか?
計算は簡単です。CPU 時間のうち s の割合が奪われると、一定量の CPU を必要とするジョブは、実時間で 1 / (1 - s) 倍の時間がかかります。60 秒の CPU 時間を必要とするジョブの場合:
The data behind this chart
[
{
"steal_percent": 0,
"wall_clock_seconds": "60.0"
},
{
"steal_percent": 3,
"wall_clock_seconds": "61.9"
},
{
"steal_percent": 8,
"wall_clock_seconds": "65.2"
},
{
"steal_percent": 15,
"wall_clock_seconds": "70.6"
},
{
"steal_percent": 25,
"wall_clock_seconds": "80.0"
},
{
"steal_percent": 40,
"wall_clock_seconds": "100.0"
}
]3 パーセントの場合、一般的な共有プランで見られる値ですが、そのジョブにかかる時間は 61.9 秒です。60.0 秒ではありません。この程度でチケットを起票する人はいません。8 パーセントでは 65.2 秒です。40 パーセントになると、同じジョブに 100.0 秒かかり、以前は処理できていたキューが逆に増え始めます。
これらは測定値ではなく、計算値です。このモデルでは、実行可能なスレッドが 1 つだけあり、区間全体にわたってスティール時間が均等に発生すると仮定しています。実際のサービスでは、この曲線より深刻に感じることがよくあります。リクエストの処理中に CPU 時間を奪われると、その遅延を、そのリクエストの完了を待つすべての処理が再び受けるためです。計算式ではなく自分の値を得るには、静かな時間帯と負荷の高い時間帯に VPS のベンチマークを実行し、両方の時間帯について st を記録します。
これは steal か、それとも別の現象か
steal は、ほかの症状と混同しやすい指標です。同じ vmstat 行にあるカウンターをまとめて確認してください。
stが高く、rとusが低いままの場合、ホストがコアを割り当てていません。これが steal です。rが vCPU 数を大きく上回り、usが高く、stがほぼ 0 の場合、自分の CPU で処理できる量を超える作業を実行しています。rとnprocの出力を比較してください。これは隣接するテナントが原因ではなく、自分の環境での過剰なオーバーサブスクリプションです。waが高く、stがほぼ 0 の場合、タスクがストレージ処理でブロックされています。これは別の問題であり、対処方法も異なります。- ロードアベレージが高い一方で
stとusがどちらも低い場合、ロード値には割り込み不能なタスクも含まれます。そのため通常は CPU ではなく、停止したデバイスやハングしたネットワークマウントが原因です。
バースト可能なプランについては、別途確認が必要です。アイドル時にクレジット残高が増え、負荷が高いときに減少します。残高を使い切ると、プロバイダーはベースラインの速度に制限します。プラットフォームによっては、このスロットリングが steal として報告されます。別のプラットフォームでは、ゲスト OS 内から確認できず、単に 1 秒あたりに割り当てられる CPU サイクルが少なくなります。隣接するテナントが原因だと判断する前に、プランの説明を確認してください。
コンテナで steal time が表示されない理由
Steal は仮想マシンの特性であり、その内部で実行されるコンテナの特性ではありません。自分の VPS 上の Docker コンテナはホストの /proc を共有するため、コンテナ内で読み取った st の値は VPS の steal です。これは必要な値です。VPS として提供されるコンテナベースの仮想化は、動作が異なります。lxcfs が設定されている場合、コンテナ内の /proc/stat は cgroup のアカウンティングから合成されるため、steal は仕様上 0 になります。コンテナ内部だけからメトリクスを収集する監視基盤では、物理マシンが下層でリソース不足に陥っていても、変化のない穏やかな 0 として表示されることがあります。
コンテナ内で同じ意味を持つカウンターは、CPU quota によるスロットリングです。cgroup v2 では次のようになります。
cat /sys/fs/cgroup/cpu.statnr_throttled は、グループが CPU quota に到達した強制適用期間の回数を数え、throttled_usec はその間に停止していた時間の合計を示します。nr_throttled の増加は、プロセスが実行可能でありながら実行されていないことを意味します。これは steal と同じ状態ですが、自分で設定した制限が原因です。ホストを疑う前に、自分の制限を確認してください。特に、compose ファイルで CPU limits を設定して VPS 上の Docker でサービスを実行している場合は注意が必要です。階層化された仮想化では、時間が失われる場所がさらに増えます。VPS 内の VM は、自身の steal に加えて、独自のスケジューリング遅延も受けるためです。VPS 上で nested virtualisation を実行している場合は、この点を考慮してください。
継続的な steal への対処
guest 内の設定では steal を解消できません。判断を行う scheduler は guest の外部で動作しているためです。kernel をアップグレードしても状況は変わりません。Linux kernel 7.2 で追加された cache aware scheduling は、実際に割り当てられた core 間でタスクを再配置するだけです。隣接する guest がすでに使用した CPU サイクルを取り戻すことはできません。実際に有効な対策は4つです。
まず証拠を収集します。 UTC の timestamp、各エピソードの継続時間、再発頻度、mpstat で影響を受けている vCPU が1つだけか、すべてかを記録します。1週間分のログ済みサンプルは、スクリーンショットより有用です。
そのデータを添えて ticket を作成します。 次の2点を直接確認します。この時間帯に node が oversubscribed になっているか。instance を移動できるか。vmstat の出力と正確な時刻を貼り付けます。provider は再現可能な時間帯に基づいて対応します。「server が遅い」とだけ書いた ticket には、その時間帯を求める返信が来ます。この作業をどこまで委任できるかは、managed VPS と unmanaged VPS の実務上の違いの1つです。
migration を依頼します。 guest を負荷の低い node に移動することは、provider にとって通常の作業です。通常は短時間の reboot で済みます。費用のかからない対策であり、1つの node に複数の高負荷な隣接 guest が同時に配置されているという、よくあるケースを解決できます。
専有によって競合をなくします。 dedicated vCPU plan では、物理 core が instance 用に予約されるため、counter は0のまま維持されます。月額費用は高くなりますが、変動を許容できない workload に対する正直な答えです。それでも不十分な場合や、memory bandwidth も専有したい場合は、VPS ではなく dedicated server が次の選択肢です。
いずれかの対応を待つ間も、steal の影響を減らします。vCPU 数より少ない worker thread 数で実行します。core を取得できない thread は context switch を増やすだけだからです。batch 処理を node が静かな時間帯へ移します。現在のログから、その時間帯を判断できます。その後、同じ時間帯に同じ command で再度測定します。推測ではなく、変更の効果を判断できます。
FAQ
VPS の通常の CPU steal time はどの程度ですか?
共有プランでは、短時間の急上昇や、継続して 5 パーセント未満の値は通常の範囲です。共有 CPU では、ホストが物理コアを複数のゲスト間で分配するためです。数時間にわたって 2 桁の値が続く場合は通常ではなく、サポートへ問い合わせる価値があります。専用 vCPU プランで期待される値は 0.0 です。そのため、それ以外の値であれば障害として報告してください。値は自身のワークロードと照らし合わせて判断します。夜間バッチは steal time の影響を受けても、レイテンシーに敏感な API では許容できない場合があります。
プランを大きくすれば高い steal time は解消しますか?
それだけでは解消しません。同じ共有ノード上の vCPU を増やしても、競合している物理コアを奪い合う仮想 CPU が増えるだけです。そのため、パーセンテージは変わらない場合があります。steal time をなくすには、専用 CPU の割り当てを受けるか、負荷の低いノードへ移行する必要があります。負荷の高いマシンで割り当てが増えても、負荷の高いマシンの一部であることに変わりはありません。
VPS が明らかに遅いのに steal time が 0 になるのはなぜですか?
主な理由は 2 つあります。ハイパーバイザーがカウンターを提供していない可能性があります。VMware と Hyper-V の環境では一般的で、その場合はホストの状態に関係なく値が 0 のままです。systemd-detect-virt を実行して、使用中のプラットフォームを確認してください。それ以外の場合は、ボトルネックが別の場所にあります。ストレージ待ちについては wa を確認し、自身の過負荷については r と nproc を比較してください。コンテナ内では /sys/fs/cgroup/cpu.stat を確認し、クォータによるスロットリングの有無を調べます。
サーバー内部から steal time を減らせますか?
ゲスト内部からホストのスケジューリングを変更することはできません。できるのは、影響を小さくすることだけです。vCPU 数より少ないワーカースレッドを実行してください。そうすれば、割り当てられる見込みのないコアを待つ処理が run queue に滞留しにくくなります。バッチ処理は、ノードの負荷が低い時間帯へ移してください。結果をキャッシュし、CPU を必要とするリクエスト自体を減らしてください。steal time を実際に解消する変更、つまり別のノードへの移行や専用コアの割り当ては、プロバイダー側で実施します。