概要
新しい WSL2 カーネル(6.6系)では cgroup v2 がデフォルトになったため、CentOS 7 などの古い OS を Docker コンテナで動かす際に systemd が正常に動作しない問題が発生します。
この記事では、問題の原因と複数の対策方法を解説します。
1. 問題の症状
- コンテナ内で
serviceコマンドが動作しない -
systemctlコマンドが失敗する - httpd, postgresql などのサービスが起動できない
エラー例:
Failed to get D-Bus connection: Operation not permitted
2. 原因
- CentOS 7 の systemd (バージョン 219) は cgroup v1 のみ対応
- 新しい WSL2 カーネルは cgroup v2 がデフォルト
- systemd はプロセス管理に cgroup を使用するため、バージョン不一致で動作しない
3. cgroup とは何か
cgroup (Control Groups) は Linux カーネルの機能で、プロセスをグループ化し、システムリソースの割り当てと制限を行う仕組みです。
cgroup が管理するリソース
| リソース | 説明 |
|---|---|
| CPU | CPU 時間の割り当て、使用率の制限 |
| メモリ | メモリ使用量の制限、OOM 時の優先度 |
| ブロック I/O | ディスク読み書きの帯域制限 |
| ネットワーク | ネットワーク帯域の制限 |
| デバイス | デバイスへのアクセス制御 |
| freezer | プロセスの一時停止・再開 |
cgroup の主な用途
- コンテナの隔離 - Docker や LXC がコンテナごとにリソースを分離
- システムサービス管理 - systemd がサービスごとにプロセスをグループ化
- リソース制限 - 特定のアプリケーションが使えるリソースを制限
- 優先度制御 - 重要なプロセスにより多くのリソースを割り当て
cgroup ファイルシステム
cgroup は /sys/fs/cgroup 以下に仮想ファイルシステムとしてマウントされます。
v1 の場合:
/sys/fs/cgroup/
├── cpu/ # CPU 制御
├── memory/ # メモリ制御
├── blkio/ # ブロック I/O 制御
├── devices/ # デバイス制御
└── ...
v2 の場合(単一の階層に統合):
/sys/fs/cgroup/
├── cgroup.controllers # 利用可能なコントローラ一覧
├── cgroup.procs # このグループのプロセス一覧
├── cpu.max # CPU 制限
├── memory.max # メモリ制限
└── ...
4. docker-compose.yml の cgroup 関連設定の意味
設定例
services:
dev:
image: myapp-node
volumes:
- /sys/fs/cgroup:/sys/fs/cgroup:rw
privileged: true
cgroupns: host # Docker Compose v2 以降
(A) /sys/fs/cgroup:/sys/fs/cgroup:rw
形式: <ホスト側パス>:<コンテナ側パス>:<オプション>
意味:
- ホストの
/sys/fs/cgroupをコンテナ内の同じパスにマウント -
:rwは読み書き可能(read-write)でマウント
なぜ必要か:
- systemd は
/sys/fs/cgroupを通じて cgroup を操作する - コンテナはデフォルトで cgroup ファイルシステムにアクセスできない
- このマウントにより、コンテナ内の systemd が cgroup を操作可能になる
図解:
┌─────────────────────────────────┐
│ ホスト (WSL2) │
│ │
│ /sys/fs/cgroup/ │
│ ├── cpu.max │
│ ├── memory.max │
│ └── ... │
│ │ │
│ │ マウント(bind mount)│
│ ▼ │
│ ┌─────────────────────────┐ │
│ │ Docker コンテナ │ │
│ │ │ │
│ │ /sys/fs/cgroup/ │ │
│ │ (ホストと同じ内容) │ │
│ │ │ │
│ │ systemd がここを操作 │ │
│ └─────────────────────────┘ │
└─────────────────────────────────┘
(B) cgroupns: host
意味:
- コンテナが使用する cgroup namespace をホストと共有する
- デフォルトは
private(コンテナ専用の namespace)
cgroup namespace とは:
- プロセスから見える cgroup 階層を隔離する機能
-
private: コンテナは自分の cgroup をルートとして認識 -
host: コンテナはホストの cgroup 階層全体を認識
図解:
【cgroupns: private(デフォルト)】
ホストから見た階層:
/sys/fs/cgroup/
└── docker/
└── <container-id>/ ← コンテナの cgroup
└── (プロセス)
コンテナから見た階層:
/sys/fs/cgroup/ ← 自分の cgroup がルートに見える
└── (プロセス)
→ コンテナは自分の cgroup しか見えない(隔離されている)
【cgroupns: host】
ホストから見た階層:
/sys/fs/cgroup/
└── docker/
└── <container-id>/
└── (プロセス)
コンテナから見た階層:
/sys/fs/cgroup/ ← ホストと同じ階層が見える
└── docker/
└── <container-id>/
└── (プロセス)
→ コンテナはホストの cgroup 階層全体を認識できる
なぜ必要か:
- 古い systemd は cgroup namespace を理解しない
-
hostにすることで、ホストと同じ cgroup の見え方になる - これにより古い systemd でも cgroup を正しく操作できる
注意:
- Docker Compose v1 (
docker-composeコマンド) では未サポート - Docker Compose v2 (
docker composeコマンド) で利用可能 - セキュリティ上、本番環境での使用は非推奨
(C) privileged: true
意味:
- コンテナに全ての Linux capabilities を付与
- ホストのデバイスへのフルアクセスを許可
- cgroup 操作を含む特権操作を許可
付与される権限:
- 全ての capability (CAP_SYS_ADMIN, CAP_NET_ADMIN 等)
-
/dev配下の全デバイスへのアクセス - cgroup の作成・変更
- カーネルモジュールのロード(一部)
なぜ必要か:
- systemd はサービス管理のために多くの特権操作を行う
- cgroup の作成・削除・変更には特権が必要
- 通常のコンテナはこれらの操作が禁止されている
セキュリティ上の注意:
- privileged コンテナはホストとほぼ同等の権限を持つ
- 本番環境での使用は避けるべき
- 開発環境やテスト環境での使用に限定すること
5. cgroup v1 と v2 の違い
| 項目 | cgroup v1 | cgroup v2 |
|---|---|---|
| 階層構造 | 複数(cpu, memory 等が別々) | 単一の統一された階層 |
| リソース管理 | 各サブシステムが独立 | 統合された制御 |
| 対応 systemd | 219 以降 | 232 以降(完全対応は 239 以降) |
| ステータス | レガシー(非推奨) | 現行の標準 |
6. なぜ cgroup v2 がデフォルトになったか
(1) Linux カーネルの公式方針
- cgroup v1 は「レガシー」として位置づけ
- カーネル 4.5 (2016年) で cgroup v2 が安定版に
- 5.x 系以降で v2 への移行を推進
(2) cgroup v2 の技術的優位性
- 統一された階層構造による管理の簡素化
- より正確なメモリ管理(OOM killer の改善)
- I/O とメモリの連携した制御
- Pressure Stall Information (PSI) によるリソース監視
- rootless コンテナのサポート改善
(3) コンテナエコシステムの移行
- Docker, Kubernetes, systemd が cgroup v2 対応を完了
- 主要ディストリビューション(Ubuntu, Fedora 等)が v2 をデフォルト化
(4) WSL2 での採用
- Ubuntu/Debian 系カーネル設定がベース
- これらのディストリビューションの方針に追従
7. systemd が cgroup を必要とする理由
systemd は以下の機能で cgroup を使用します。
(1) サービスのプロセス管理
- サービスに属する全プロセスをグループ化
- サービス停止時に関連プロセスを確実に終了
- fork したプロセスも同じグループで管理(「逃げ」を防止)
(2) リソース制限
- CPU、メモリ、I/O の使用量制限
- サービスごとのリソース分離
(3) 状態監視
- サービスの実行状態を追跡
- 異常終了の検知と自動再起動
(4) 依存関係の管理
- サービス間の起動順序制御
- 障害時の連鎖的な停止処理
8. 対策方法
【方法 A】WSL2 で cgroup v1 を有効にする
既存環境を変更したくない場合に推奨。
手順:
-
Windows 側で設定ファイルを作成/編集
- ファイル:
%USERPROFILE%\.wslconfig
- ファイル:
-
以下の内容を記述
[wsl2]
kernelCommandLine = systemd.unified_cgroup_hierarchy=0
- WSL を再起動
wsl --shutdown
その後 WSL を再度起動
- 確認
cat /sys/fs/cgroup/cgroup.controllers
ファイルが存在しなければ v1 モード
注意: WSL2 カーネル 6.6 系では、この設定が効かない場合があります。
【方法 B】新しい OS ベースのコンテナに移行する
長期的な解決策として推奨。
cgroup v2 対応の OS を使用します。
| OS | systemd version | cgroup v2 対応 |
|---|---|---|
| CentOS 7 | 219 | ❌ 非対応 |
| CentOS 8 | 239 | ✅ 対応 |
| Rocky Linux 8 | 239 | ✅ 対応 |
| Rocky Linux 9 | 252 | ✅ 完全対応 |
| AlmaLinux 8 | 239 | ✅ 対応 |
| AlmaLinux 9 | 252 | ✅ 完全対応 |
【方法 C】service コマンドを使わずに直接サービスを起動する
cgroup 設定を変更したくない場合に推奨。開発環境では最も手軽な方法です。
systemd を使わず、サービスを直接起動することで cgroup 問題を回避できます。
例: Apache httpd の場合
# 環境変数を設定してから直接起動
export $(cat /etc/sysconfig/myapp)
export PHPRC=/usr/local/myapp/config
mkdir -p /var/run/httpd
httpd -f /usr/local/myapp/config/httpd.conf -k start
起動スクリプトの例:
#!/bin/bash
#
# myapp-ctl - Development Container Control Script
#
set -e
DBENV_PATH=/etc/sysconfig/myapp
HTTPD_CONF=/usr/local/myapp/config/httpd.conf
PID_DIR=/var/run/httpd
# Build environment variables for httpd
build_env() {
local env_vars=""
if [ -f "$DBENV_PATH" ]; then
env_vars=$(cat "$DBENV_PATH" | tr '\n' ' ')
else
echo "Error: Environment file not found: $DBENV_PATH"
exit 1
fi
env_vars="$env_vars PHPRC=/usr/local/myapp/config HOSTNAME=${HOSTNAME}"
echo "$env_vars"
}
mkdir -p "$PID_DIR"
case "$1" in
start)
echo "Starting httpd..."
env $(build_env) httpd -f "$HTTPD_CONF" -k start
echo "httpd started."
;;
stop)
echo "Stopping httpd..."
env $(build_env) httpd -f "$HTTPD_CONF" -k stop
echo "httpd stopped."
;;
restart)
echo "Restarting httpd..."
env $(build_env) httpd -f "$HTTPD_CONF" -k restart
echo "httpd restarted."
;;
status)
if pgrep -f "httpd -f $HTTPD_CONF" > /dev/null; then
echo "httpd is running."
ps aux | grep "[h]ttpd"
else
echo "httpd is not running."
fi
;;
*)
echo "Usage: $0 {start|stop|restart|status}"
exit 1
;;
esac
exit 0
メリット:
- docker-compose.yml の
privilegedや cgroup 設定が不要 - コンテナがより軽量・セキュアになる
- WSL2 のカーネルバージョンに依存しない
デメリット:
- OS 起動時の自動起動ができない(開発環境では問題なし)
-
serviceコマンドが使えない
9. Docker Compose 設定例
CentOS 7 + cgroup v1 環境(WSL で v1 を有効にした場合)
services:
app:
image: centos:7
container_name: my-app
volumes:
- /sys/fs/cgroup:/sys/fs/cgroup:rw
tty: true
privileged: true
Rocky Linux 9 + cgroup v2 環境
services:
app:
image: rockylinux:9
container_name: my-app
volumes:
- /sys/fs/cgroup:/sys/fs/cgroup:rw
tty: true
privileged: true
方法 C を使う場合(最小権限・推奨)
services:
app:
image: centos:7
container_name: my-app
volumes:
- ./myapp:/home/user/myapp
tty: true
# privileged や cgroup 設定は不要
最小権限での設定(本番環境向け)
services:
app:
image: rockylinux:9
container_name: my-app
volumes:
- /sys/fs/cgroup:/sys/fs/cgroup:rw
tty: true
cap_add:
- SYS_ADMIN
security_opt:
- seccomp:unconfined
10. 確認コマンド
cgroup バージョンの確認
# v2 の場合、このファイルが存在する
cat /sys/fs/cgroup/cgroup.controllers
# v1 の場合、サブディレクトリが存在する
ls /sys/fs/cgroup/cpu
systemd バージョンの確認
systemctl --version
Docker の cgroup ドライバー確認
docker info | grep -i cgroup
WSL2 カーネルバージョンの確認
uname -r
まとめ
| 方法 | メリット | デメリット | 推奨ケース |
|---|---|---|---|
| A: cgroup v1 有効化 | 既存環境をそのまま使える | WSL2 カーネル依存、将来性なし | 一時的な対応 |
| B: 新 OS に移行 | 根本的解決 | 移行作業が必要 | 長期的な運用 |
| C: 直接起動 | 設定変更不要、軽量 | 自動起動不可 | 開発環境 |
開発環境であれば 方法 C が最も手軽でおすすめです。