なぜ今、cgroup v2への再設計が必要なのか
Linux カーネルにおけるコントロールグループ(cgroup)は、プロセスへのCPU・メモリ・I/Oリソース割り当てを制御する根幹機構です。v1 は長年にわたって広く使われてきましたが、コントローラごとに独立した階層を持つ設計は複雑な競合を生みやすく、コンテナランタイムやsystemdとの統合においても一貫性の欠如が問題視されてきました。
cgroup v2 は単一の統合階層(unified hierarchy)を採用し、リソース制御の見通しを大幅に改善しました。2026年時点では、Fedora 31 以降・Ubuntu 21.10 以降・RHEL 9 以降・Debian 11 以降で v2 がデフォルトとなっており、新規構築環境でv1を選択する理由はほぼなくなっています。問題は既存サービスがv1前提の設定を保持したまま稼働しているケースです。カーネルアップグレードやディストリビューション移行を機に動作不全が発生する事例は、現場で繰り返し報告されています。
本記事では、稼働中の環境でv1依存設定を洗い出し、v2の階層モデルに即した形へ再設計する手順を、検証・切り戻しまで含めて解説します。
現在の cgroup マウント構成を確認する
作業の起点は現状把握です。まず動作中のシステムがv1・v2・あるいは混在(hybrid)のどれかを確認します。
mount | grep cgroup
stat -f /sys/fs/cgroup
stat -f の出力で Type: cgroup2fs が返れば純粋なv2環境です。tmpfs が返り、配下に /sys/fs/cgroup/memory や /sys/fs/cgroup/cpu が存在する場合はv1またはhybridです。hybridモードでは /sys/fs/cgroup/unified にv2ツリーが並列で存在します。
systemd が認識しているモードは次で確認できます。
systemctl show --property=DefaultMemoryAccounting,DefaultCPUAccounting
また、カーネルブートパラメータに systemd.unified_cgroup_hierarchy=0 や cgroup_no_v1= が残っていないかも確認が必要です。
cat /proc/cmdline
これらのパラメータが明示的に設定されている環境では、grub 設定の変更が移行の前提作業になります。
v1 依存設定の洗い出し
v2 環境で問題になるのは、主に以下のパターンです。
- systemd ユニットファイルの非推奨ディレクティブ(
CPUShares=・MemoryLimit=・BlockIOWeight=等) - Docker / containerd の
--cgroup-parentとv1 コントローラ直接参照 - アプリ独自の cgroupfs 直書き(
/sys/fs/cgroup/memory/<name>/memory.limit_in_bytesなど) - libvirt・cgmanager 等のv1前提デーモン
systemd ユニットに絞った洗い出しは、以下のコマンドで機械的に抽出できます。
grep -rE 'CPUShares|MemoryLimit|BlockIOWeight|BlockIOReadBandwidth|BlockIOWriteBandwidth' \
/etc/systemd/system/ /usr/lib/systemd/system/ 2>/dev/null
これらはv2では無視されるか、警告なしに落ちる場合があります。systemd-analyze verify を各ユニットに対して実行すると、非推奨ディレクティブの警告を確認できます。
systemd-analyze verify /etc/systemd/system/yourservice.service
コンテナ環境では、docker inspect の CgroupParent フィールドや Compose ファイルの cgroup_parent キーを確認します。また、/etc/docker/daemon.json に "exec-opts": ["native.cgroupdriver=cgroupfs"] が残っている場合、v2環境でのコンテナ起動が不安定になるため、systemd ドライバへの切り替えが必要です。
v2 階層モデルへの再設計:ポリシーの考え方
v2 の最大の概念的変化は、すべてのリソース制御が単一のツリー(/sys/fs/cgroup)に統合されている点です。v1では各コントローラ(cpu, memory, blkio)が独立したツリーを持っていたため、あるプロセスをcpuツリーの「グループA」に属させつつmemoryツリーの「グループB」に属させるといった分割が可能でしたが、v2ではプロセスはツリー上の1箇所にしか属せません。
実務設計の原則として、サービス単位のスライス(slice)を明示的に定義し、その下に関連サービスをまとめる構成が推奨されます。たとえば、Webアプリ・DBの2サービスを同一ホストで動かす場合、次のような階層を設計します。
system.slice
├── webapp.service → MemoryMax=2G, CPUWeight=200
└── database.service → MemoryMax=8G, CPUWeight=500
custom-prod.slice (追加スライス)
└── batch.service → MemoryMax=1G, CPUWeight=50, IOWeight=10
v2 対応のディレクティブ対応表は以下の通りです(v1 → v2)。
CPUShares=N→CPUWeight=N(スケールは1〜10000、デフォルト100)MemoryLimit=N→MemoryMax=N(上限。MemoryHigh=でソフト上限も設定可)BlockIOWeight=N→IOWeight=NBlockIOReadBandwidth=→IOReadBandwidthMax=BlockIOWriteBandwidth=→IOWriteBandwidthMax=
特に MemoryHigh はv2の新機能で、ここで設定した値を超えるとカーネルが積極的にスワップ・回収を促しつつ、MemoryMax に達するまでは強制終了しません。多くの運用者がOOMキルを避けながらメモリ圧力をコントロールするために活用しています。
移行手順:段階的適用と検証
ステップ1:ユニットファイルの書き換え
既存ユニットファイルを直接編集するのではなく、systemctl edit yourservice でドロップインファイルを作成して上書きします。これにより元ファイルが保持され、切り戻しが容易になります。
[Service]
CPUWeight=200
MemoryHigh=1800M
MemoryMax=2G
ドロップインは /etc/systemd/system/yourservice.service.d/override.conf に保存されます。
ステップ2:カスタムスライスの定義
バッチや低優先サービスを分離したい場合は、スライスユニットを作成します。
# /etc/systemd/system/custom-prod.slice
[Unit]
Description=Production workload slice
[Slice]
CPUWeight=300
MemoryMax=12G
サービス側では Slice=custom-prod.slice を指定します。
ステップ3:設定の反映と稼働確認
systemctl daemon-reload
systemctl restart yourservice
systemctl status yourservice
実際にv2ツリーに反映されているかは systemd-cgls と systemd-cgtop で確認します。
systemd-cgls /system.slice/yourservice.service
cat /sys/fs/cgroup/system.slice/yourservice.service/memory.max
memory.max の値(バイト)が設定したものと一致していれば、v2コントローラが正しく適用されています。
切り戻し手順と 2026 年の現行環境差分
切り戻し
ドロップインを使っていれば、削除するだけで元の設定に戻ります。
rm /etc/systemd/system/yourservice.service.d/override.conf
systemctl daemon-reload
systemctl restart yourservice
v2自体を無効化してv1に戻す場合は、grub に systemd.unified_cgroup_hierarchy=0 を追記して再起動しますが、これはあくまで暫定措置です。多くのコンテナランタイムはv2を前提とした最適化を進めており、v1への逆戻しは長期的に維持できない選択肢になっています。
2026 年の環境差分と注意点
RHEL 9 / Rocky Linux 9 / AlmaLinux 9 は v2 専用で、v1 へのフォールバック機能は提供されていません。カーネル 6.x 系では memory.oom.group が安定し、サービス配下の全プロセスを一括OOMキルする設定が使えるようになっています。コンテナランタイムでは containerd 2.x と crun がv2のフルサポートを前提とした設計に移行しており、runc は後方互換を保持していますが、v2機能(memory.high 等)を活用するにはランタイム設定の明示的な有効化が必要です。
Kubernetes 環境では、kubelet の --cgroup-driver=systemd と Node の v2 対応が揃っていない場合にポッド起動の不安定が発生するケースが確認されています。クラスタ構築時にノードOSとkubeletのcgroupドライバ設定が一致しているか、必ず事前確認する習慣が現場では定着しています。
cgroup v2 への移行は一度の作業で完結するものではなく、サービス構成の変化に合わせてスライス階層を見直す継続的な運用設計です。洗い出し・置き換え・検証のサイクルを小さく回すことが、本番環境での影響を最小化する現実的なアプローチです。
「コマンドは打てる。次は”現場で通用する型”を最短で身につけたい」——そんなあなたへ。
ネットの断片的なTipsをコピペするだけでは、体系的な運用スキルはなかなか積み上がりません。リナックスマスター.JPの無料メルマガ「リナマガ」では、現場で実際に使うLinuxサーバー運用の考え方を、順を追って実務目線でお届けしています。いま登録された方には、安全なサーバー構築の「型」をまとめた『Linuxサーバー構築入門マニュアル(図解60P)』を完全無料でプレゼント中です。
>> 図解60P『Linux入門マニュアル』を無料で受け取る
(メルマガ登録は10秒・いつでも解除OK)
※ 「独学の時間がもったいない」「プロから現場の技術を最短で学びたい」という本気の方には、2日で実務レベルが身につく初心者向けハンズオンセミナーもご用意しています。
PR・広告
コマンド操作から基本運用までを手を動かして学べる定番書。記事で触れた技術の土台固めに。
※ Amazonのアソシエイトとして、当サイトは適格販売により収入を得ています。
