CGroup V2 を一般（非root）ユーザーからシェルで操作する

Fedora 31のようにCGroup V2 (Unified CGroup Hierarchy) で起動されsystemdが動いているシステムの上で、一般ユーザーがシェルでCGroupのツリーを構成しリソース制限を掛ける手順の例です。CGroup V2の日本語のウェブ上の記事としては

* [Linuxカーネルのコンテナ機能 ― cgroupの改良版cgroup v2](https://gihyo.jp/admin/serial/01/linux_containers/0038)
* [cgroup v2 の nsdelegate オプション（2）〜 cgroup namespace 作成後の cgroup root を権限委譲の境界として扱う](https://tenforward.hatenablog.com/entry/2018/09/07/014905)

などがあります。またリソース制限を掛けるだけなら[Linux cgroup v2で実メモリ占有量を制限する](https://qiita.com/kakinaguru_zo/items/cb18bd8f7aa92c11f6bf)にある手順で十分です。以下の手順を実行する前準備として[Linux cgroup v2で実メモリ占有量を制限する](https://qiita.com/kakinaguru_zo/items/cb18bd8f7aa92c11f6bf)を用いて`systemd-run --user --scope /bin/bash`が使えるようにしてください。

# 一般ユーザーが書き込み（操作）できるCGroupツリーを作成する
```shell-session
$ export CGROUPDIR=/sys/fs/cgroup/user.slice/user-$UID.slice/user@$UID.service/myshell.scope
```

ディレクトリ名が長いから名前を付ける。

```shell-session
$ systemd-run --unit=myshell --user --scope -p "Delegate=cpu io memory pids" /bin/bash
Running scope as unit: myshell.scope
```

これで `/sys/fs/cgroup/user.slice/user-$UID.slice/user@$UID.service/myshell.scope` が書き込み可能なCGroupツリーとして作成されて、そこに所属するbashのプロンプトが現れる。

```
$ systemctl --user status myshell.scope
● myshell.scope - /bin/bash
   Loaded: loaded (/run/user/1000/systemd/transient/myshell.scope; transient)
Transient: yes
   Active: active (running) since Sat 2019-12-07 12:38:53 JST; 1min 24s ago
    Tasks: 2 (limit: 4915)
   Memory: 2.9M
      CPU: 45ms
   CGroup: /user.slice/user-1000.slice/user@1000.service/myshell.scope
           ├─10574 /bin/bash
           └─10621 systemctl --user status myshell.scope
```
CGroupツリーの様子は上記の通り


#サブツリー１を作るを作りbashをそこに移動する
シェル変数`$$`はbashのプロセスID (PID)を表す

```shell-session
$ mkdir $CGROUPDIR/tree1
$ echo $$ >$CGROUPDIR/tree1/cgroup.procs
$ systemctl --user status myshell.scope
● myshell.scope - /bin/bash
   Loaded: loaded (/run/user/1000/systemd/transient/myshell.scope; transient)
Transient: yes
   Active: active (running) since Sat 2019-12-07 12:38:53 JST; 4min 25s ago
    Tasks: 2 (limit: 4915)
   Memory: 3.0M
      CPU: 58ms
   CGroup: /user.slice/user-1000.slice/user@1000.service/myshell.scope
           └─tree1
             ├─10574 /bin/bash
             └─10645 systemctl --user status myshell.scope
```

#サブツリー２を作りそこで新たなプロセスを作成する

```shell-session
$ mkdir $CGROUPDIR/tree2
$ bash -c 'echo $$ >$CGROUPDIR/tree2/cgroup.procs; sleep 100000' &
[1] 10672
$ systemctl --user status myshell.scope
● myshell.scope - /bin/bash
   Loaded: loaded (/run/user/1000/systemd/transient/myshell.scope; transient)
Transient: yes
   Active: active (running) since Sat 2019-12-07 12:38:53 JST; 9min ago
    Tasks: 4 (limit: 4915)
   Memory: 3.6M
      CPU: 74ms
   CGroup: /user.slice/user-1000.slice/user@1000.service/myshell.scope
           ├─tree1
           │ ├─10574 /bin/bash
           │ └─10674 systemctl --user status myshell.scope
           └─tree2
             ├─10672 bash -c echo $$ >$CGROUPDIR/tree2/cgroup.procs; sleep 1000…
             └─10673 sleep 100000
```

#ツリー１とツリー２にCGroupコントローラーを追加しリソース制限を書き加える

```shell-session
$ echo +pids +memory >$CGROUPDIR/cgroup.subtree_control
$ cat $CGROUPDIR/tree2/cgroup.controllers
memory pids
$ echo 2G >$CGROUPDIR/tree2/memory.high
$ cat $CGROUPDIR/tree2/memory.high
2147483648
```

#`systemd-run --user` で起動した`bash`が終了してもCGroupは存続する

```shell-session
$ exit
$ systemctl --user status myshell.scope
● myshell.scope - /bin/bash
   Loaded: loaded (/run/user/1000/systemd/transient/myshell.scope; transient)
Transient: yes
   Active: active (running) since Sat 2019-12-07 12:38:53 JST; 17min ago
    Tasks: 2 (limit: 4915)
   Memory: 3.1M
      CPU: 173ms
   CGroup: /user.slice/user-1000.slice/user@1000.service/myshell.scope
           └─tree2
             ├─10672 bash -c echo $$ >$CGROUPDIR/tree2/cgroup.procs; sleep 1000…
             └─10673 sleep 100000
```

# 注意点
* ディレクトリ/cgroup.procs が空でないときにはそのディレクトリの`cgroup.subtree_control`にコントローラーを追加・削除できない
* 子ディレクトリを持たないディレクトリの`cgroup.procs`がプロセスを持てる原則なので、あるCGroupの下に子ディレクトリを掘りたいときには、そのCGroupにいるプロセスを子ディレクトリに速やかに移動する