前書き
Linux 4.5から正式導入されたcgroup v2を用いてプロセス群の実メモリー総占有量を制限する手順を解説する。cgroup v2を使うメリットは、cgroup v1では設定した制限を超過するとプロセスがいきなり殺されるが、v2ならプロセスを殺さずに実メモリ占有量を抑えられることである。またcgroup v1は徐々に非推奨扱いになる。以下の手順はUbuntu 18.04とDebian buster、およびstretch-backportsからsystemd 237をインストールしたDebian stretchで確認した。systemdのバージョンは最低236以上でLinuxカーネルバージョンは最低4.5以上ではないと下記の手順はそのままでは使えないはず。
Debianはsystemdのパッケージバージョン247.2-2でcgroup v2に切り替えが行われた。Redhat 8でcgroup v2がテクノロジープレビューとして導入されている。Fedora 31からcgroup v2が標準となった。それでdockerが使えなくて困っている話を見かけるが**バージョン20.10以降ではdockerでも問題なくcgroup v2で使える**。またdocker互換のpodmanがCgroup V2対応なのでそちらを使えば問題ない。Ubuntuには公式のインストール手順があるが、それでうまくいかない場合やDebianの場合でも別の手順 でインストールできる。LXCをCGroup V2で使う手順はここ。DebianのデフォルトもCGroup V2にしようという動きがある。
cgroup v1をsystemd
にマウントさせない下準備(この手順は必須)
systemd
はデフォルトでは /sys/fs/cgroup
以下にcgroup v1とv2の両方のファイルシステムをマウントし、あるプロセスがcgroup v1で制御されているとcgroup v2では制御出来ないため、cgroup v2のファイルシステムだけマウントする。
-
/etc/default/grub
のGRUB_CMDLINE_LINUX_DEFAULT=
にsystemd.unified_cgroup_hierarchy=1
を追加する -
update-grub
を実行して再起動する -
もしcgroup v1を完全に禁止したいなら
cgroup_no_v1=all
も追加する。これはあまりお勧めしない
systemd
239のバグに対応する設定ファイル(240以降では不要)
ここに書いてあるバグ報告に書いてあるように、以下に述べることの多くはバージョン239で動かない。原因は user-$UID.slice
というsystemdのユニットがうまく初期化されていないようで、
[Unit]
Before=systemd-logind.service
[Slice]
Slice=user.slice
[Install]
WantedBy=multi-user.target
[Unit]
Before=systemd-logind.service
[Install]
WantedBy=multi-user.target
という2つのファイルを作成し、systemctl enable user.slice user-nonexistent.slice
で起動時にuser.slice
と user-nonexistent.slice
が実行されるようにすると回避できる。
一般ユーザーによる個別プロセスの実メモリ制限(他の節と独立に使える)
一般ユーザーが実メモリ(とその他の)制限を使えるようにするためのシステム設定
以下のファイルを新規に作成する
[Service]
# 次の行は以下で述べる systemd-run --user を用いたメモリ制限のために必要
Delegate=memory pids
一つのプロセスの占有実メモリを制限したいとき
個別のプロセスの占有実メモリを制限するには例えば以下のように行う。systemd-run --user
を用いるためには dbus-user-session
パッケージがインストールされていることが必要である。
$ systemd-run --user --no-block -p MemoryHigh=5G google-chrome
複数のプロセスの合計占有実メモリを制限したいとき
$ systemd-run --user --scope -p "Delegate=memory pids" -p MemoryHigh=5G bash
# 上記のコマンドでエラーになったら次のコマンドを代わりに試してください
$ systemd-run --user -t -p "Delegate=memory pids" -p MemoryHigh=5G bash
とすると新しく起動したbash
ならびにそこから起動されたすべてのプロセスの合計占有実メモリが5Gに制限された環境が出来るので、実メモリの過剰消費が起きる危険があるプログラムをそのbash
から起動する。systemd-run --user
により作られたCGroupの制御ファイルは /sys/fs/cgroup/user.slice/user-1000.slice/user@1000.service/run-u0.service
付近にあり、そこにあるファイルの所有者はsystemd-run
を起動したユーザーで書き込み権限も与えられているため、そこにあるファイルに書き込むことでCGroupによる制限を好きなように操作出来る。
systemd 237まで修正されなかったsystemd-run --user --scope
のバグ
筆者の環境(Ubuntu-Mate 18.04, systemd 237)では、GUIでMATE端末を起動してそこからsystemd-run --user --scope
すると問題なく起動するが、CTRL-ALT-F1でCUIに切り替えてそこからsystemd-run --user --scope
を起動すると失敗して、journalctl -xe
に「Failed to add PIDs to scope's control group: Permission denied」というログが残る。systemd
をcgroup v2専用にしたときにsystemd-run --user --scope
が動作しないバグは1年以上修正されていなくてsystemd 238で完全に修正された。
一般ユーザーの合計占有実メモリの制限(他の節と独立に使える)
systemd
239以降でユーザーごとに占有実メモリを制限する手順
以下のファイルを作って置くだけでよい。簡単
[Slice]
MemoryHigh=90%
systemd
237でユーザーごとに占有実メモリを制限する手順
user-.slice.d
という書き方に対応していないから以下のようにする。
[Slice]
Slice=user.slice
MemoryHigh=90%
1000
のところはユーザーIDで、ユーザーごとにファイルを作成して下さい。systemctl daemon-reload
を実行すると次回のログインから/sys/fs/cgroup/user.slice/user-1000.slice/memory.high
に設定した数値が記入され制限が反映されます。
一時的なメモリーの制限
既にログインしているユーザーの使用メモリーを一時的に制限したいなら
systemctl set-property --runtime user-1000.slice 'MemoryHigh=80%'
というコマンドをroot権限で実行すればよい。1000
は制限したいユーザーのUIDに置き換える。
さらなる情報
cgroup v2の日本語による説明として第38回 Linuxカーネルのコンテナ機能 ― cgroupの改良版cgroup v2 [2]がよいと思う。memory.high
の説明はオリジナルの英語文書が現状では一番わかりやすいのではないかしら?
またResource Control @ FB はCgroup V2とPSI (Pressure Stall Information)を組み合わせてリソース配分をうまくやる実践について紹介している。FB技術者によるウェブサイトはcgroup2: Control and maximize resource utilization
- CGroup V2 を一般(非root)ユーザーからシェルで操作する という記事を書いた。
- 一般ユーザーからCGroup V2でLXCを使う という記事を書いた。