なぜNUMAトポロジがスループットに直結するのか
マルチソケット・多コアサーバーが当たり前になった現在でも、「CPUが空いているのにレイテンシが改善しない」という状況は珍しくありません。その原因の多くはNUMA(Non-Uniform Memory Access)アーキテクチャの特性を無視したプロセス配置にあります。
NUMAでは、物理CPUソケットごとにローカルメモリが存在し、別ソケットのメモリへアクセスする際には「NUMAノード間インターコネクト(AMD Infinity FabricやIntel UPI等)」を経由するコストが発生します。このリモートアクセスのレイテンシはローカルアクセスの1.5〜3倍程度になることが多く、メモリ帯域幅を多用するデータベースやHPCワークロードでは体感できるほどスループットに影響します。
2026年現在、4ソケット以上の大規模サーバーはもちろん、2ソケットの一般的なラックサーバーでもNUMAの影響は無視できません。また、AMD EPYCシリーズのCCX(Core Complex)構造のように、同一ソケット内でも複数のNUMAノードに分割される設計が広まっており、単純な「1ソケット=1NUMAノード」という前提が崩れています。
現行環境のNUMAトポロジを把握する
プロセス配置を設計する前に、対象サーバーのNUMAトポロジを正確に把握することが不可欠です。以下のコマンドで現行構成を確認します。
# NUMAノードとCPUの対応確認
numactl --hardware
# より詳細なトポロジ確認(lstopo相当のテキスト表示)
lscpu --extended
# NUMAノード間のレイテンシ行列確認
numactl --hardware | grep -A 10 "node distances"
# cgroupv2環境でのNUMA統計確認
cat /sys/fs/cgroup/memory.numa_stat
numactl --hardwareの出力では、各NUMAノードのCPUコアセット・空きメモリ量・ノード間距離行列が一覧されます。distance値が10が最速のローカルアクセス基準で、リモートノードは20〜40程度になります。AMD EPYCの場合はL3キャッシュを共有するCCDをまたぐ場合に中間的な距離値(16〜20程度)が出ることもあり、この行列の読み取りが配置設計の出発点になります。
さらに詳細なメモリバスとPCIeデバイスの近接関係を把握したい場合は、hwlocパッケージのlstopo-no-graphicsコマンドが有用です。NICやNVMeがどのNUMAノードに近接しているかを把握することで、ネットワーク集約型ワークロードの配置方針が変わります。
numactlによるプロセス配置の実践手順
最もシンプルなアプローチがnumactlコマンドによる起動時バインディングです。新規プロセスを特定のNUMAノードに固定するには、以下のように使います。
# NUMAノード0のCPUとメモリのみで起動
numactl --cpunodebind=0 --membind=0 /usr/bin/mysqld
# 複数ノードにまたがる場合(ノード0と1)
numactl --cpunodebind=0,1 --membind=0,1 /usr/bin/redis-server
# 既存プロセスのバインディング変更(PID 12345)
taskset -cp 0-7 12345
numactl --membind=0 --pid 12345 # ※メモリバインドは既存プロセスには効果限定的
重要な注意点として、--membindは新規に確保されるメモリページにのみ適用されます。既に確保済みのメモリをリモートノードへ移動させるにはmigratepagesコマンドやmove_pagesシステムコールが必要です。本番環境では停止を伴う再起動時に適用するのが安全です。
systemdでサービスを管理している場合は、ユニットファイルに直接NUMAポリシーを記述できます。
[Service]
NUMAPolicy=bind
NUMAMask=0
CPUAffinity=0-15
systemd v246以降ではNUMAPolicyディレクティブが利用可能です。RHEL 9・Ubuntu 22.04以降の標準環境ではこの方式が推奨されます。numactlをExecStartPreで呼ぶより宣言的で管理しやすく、systemdのcgroup連携も自動で得られます。
cgroupv2とNUMAポリシーの組み合わせ設計
2026年時点では、主要ディストリビューションのデフォルトがcgroupv2(unified hierarchy)に移行しています。RHEL 9・Ubuntu 22.04・Debian 12以降はすべてcgroupv2がデフォルトであり、cgroupv1のcpusetサブシステムとは設定方法が異なります。
cgroupv2でのNUMAピン留め(cpuset設定)は以下の流れになります。
# cgroupv2のcpusetコントローラを有効化(rootcgroupに委譲が必要)
echo "+cpuset" > /sys/fs/cgroup/cgroup.subtree_control
# 専用cgroupの作成
mkdir /sys/fs/cgroup/myapp
# CPUとメモリノードの設定(NUMAノード0のみ)
echo "0-15" > /sys/fs/cgroup/myapp/cpuset.cpus
echo "0" > /sys/fs/cgroup/myapp/cpuset.mems
# プロセスをcgroupに移動
echo $PID > /sys/fs/cgroup/myapp/cgroup.procs
cgroupv2ではcpuset.cpus.partitionという機能も追加されており、rootに設定することで専用CPUリソースを分離したパーティションを作成できます。これにより、他のcgroupからのCPUスティールをほぼゼロにした上でNUMAローカリティも保証するという、より強いリソース保証が実現します。KubernetesのTopologyManagerとCPUManagerも内部的にこの仕組みを使っており、コンテナワークロードでも同じ設計原則が適用されます。
systemdのスライス・スコープ管理下でcgroupv2のcpusetを活用する場合は、systemctl set-propertyでオンラインで変更できます。
# systemd経由でcpuset制御
systemctl set-property myapp.service AllowedCPUs=0-15
systemctl set-property myapp.service AllowedMemoryNodes=0
スループット改善の検証と定量評価
配置変更の効果を定量的に確認しなければ、本番適用の判断材料になりません。NUMAバインディング前後の比較には以下の指標とツールが有用です。
- numastat:ノードごとのメモリ割り当て・ミス率を表示。リモートアクセス比率(numa_miss / numa_hit)が改善したかを確認する。
- perf stat -e numa:*:NUMAイベントカウンタを直接計測。ローカル/リモートアクセス回数の比率を見る。
- sar -B / /proc/vmstat:ページマイグレーション(pgmigrate_success)の発生頻度確認。
- turbostat / intel_gpu_top:ソケットごとのC-state・電力消費から偏りを検出する。
現場での経験では、NUMAバインディング前はnumastatのnuma_foreign(リモートノードへの割り当て)が全体の30〜50%に達しているケースがあり、バインディング適用後に10%以下まで下がるとともに、DBのクエリレイテンシが15〜25%改善した事例が報告されています。ただし改善幅はワークロードのメモリアクセスパターンに大きく依存するため、自環境での計測が前提です。
また、バランシングを完全に切ることでNUMAローカリティは上がる一方、ノード間の負荷不均衡が生じやすくなります。カーネルの自動NUMAバランシング(/proc/sys/kernel/numa_balancing)は、手動バインディングを使う場合は無効化(0に設定)することが一般的です。有効のままにすると、カーネルが定期的にページ移動を試みてバインディングと競合する可能性があります。
切り戻し手順と運用上の注意点
NUMAバインディングは柔軟性とのトレードオフです。厳格なバインディングを適用した状態でNUMAノードの物理メモリが枯渇した場合、カーネルはOOM killerを発動させる可能性があります。リモートノードへのフォールバックを許すかどうかは、numactl --preferred(ベストエフォート)と--membind(厳格)の使い分けで制御します。
systemdユニットファイルで設定した場合の切り戻しは簡単です。
# NUMAポリシー設定の削除(デフォルトに戻す)
systemctl revert myapp.service
# またはユニットファイルからNUMAPolicy行を削除してリロード
systemctl daemon-reload && systemctl restart myapp.service
cgroupv2のcpuset設定は、cgroupを削除するかプロセスを別cgroupに移動するだけで即時解除されます。運用上は変更前にnumastat -p [プロセス名]でベースラインを記録しておき、適用後との差分を比較できるよう手順書に組み込むことが推奨されます。
2026年時点での注意点として、コンテナ環境(Podman・Docker)ではNUMAバインディングがホスト設定に依存します。KubernetesではTopologyManagerのポリシーをsingle-numa-nodeに設定することで、Podレベルでのトポロジ整合性を保証できます。ただしスケジューリングが厳しくなりPendingになりやすくなるため、best-effortやrestrictedポリシーとの使い分けを検討してください。クラウドVM環境では仮想NUMAノードが提供されますが、物理NUMAとの対応関係はハイパーバイザーに依存するため、クラウドプロバイダーの仕様確認が前提となります。
「コマンドは打てる。次は”現場で通用する型”を最短で身につけたい」——そんなあなたへ。
ネットの断片的なTipsをコピペするだけでは、体系的な運用スキルはなかなか積み上がりません。リナックスマスター.JPの無料メルマガ「リナマガ」では、現場で実際に使うLinuxサーバー運用の考え方を、順を追って実務目線でお届けしています。いま登録された方には、安全なサーバー構築の「型」をまとめた『Linuxサーバー構築入門マニュアル(図解60P)』を完全無料でプレゼント中です。
>> 図解60P『Linux入門マニュアル』を無料で受け取る
(メルマガ登録は10秒・いつでも解除OK)
※ 「独学の時間がもったいない」「プロから現場の技術を最短で学びたい」という本気の方には、2日で実務レベルが身につく初心者向けハンズオンセミナーもご用意しています。
PR・広告
[試して理解]Linuxのしくみ 増補改訂版(Amazon)
プロセス・権限・ファイルシステムなどLinux内部を図解で理解できる一冊。運用判断の勘所に。
※ Amazonのアソシエイトとして、当サイトは適格販売により収入を得ています。
