Plamo 7.0 上で非特権な systemd コンテナを起動する
Plamo 7.0 では cgroupfs_mount パッケージと lxc パッケージを提供していますが、非特権コンテナを起動するための調整は特に行っていませんでした。
cgroupfs_mount パッケージは、Plamo 7.0 リリース直後は Ubuntu 14.04 あたりで cgroupfs をマウントするのに使われていた cgroup-lite パッケージを元にした cgroupfs-mount をそのまま入れていました。これはシェルスクリプトで、システム上でサポートされているコントローラに対応したディレクトリを /sys/fs/cgroup
に作成し、それぞれコントローラをオプションに指定してマウントするだけのシンプルなスクリプトです。
これで非特権コンテナは起動しますが、コンテナ内の systemd がちゃんと起動しません。今回はコンテナは Ubuntu bionic のイメージを使っています。
誰得?な記事ですが、非 systemd 環境でコンテナを起動する場合は参考になるかもしれません(←やっぱり誰得😂)
非特権コンテナを起動する準備
非特権の lxc コンテナを起動するためには、連載の第17回 で書いた方法と特に変わっていません。しかし一部の設定項目名が変わったりしています。
まずはコンテナを起動したいユーザが使えるサブIDを登録します。ここでは plamo
ユーザーとします。
$ sudo usermod -v 200000-299999 -w 200000-299999 plamo
記事では、システム上で使う 65536 個のサブIDを使えるようにしていましたが、今の実装ではちょうどだと「足りない」とエラーが出るようです(詳しい原因は調べてません)。これは LXD のときも同じです。ですので実際に使う以上に余裕を持ってサブIDを確保しておきましょう。
非特権コンテナで veth
インターフェースを使うために /etc/lxc/lxc-usernet
ファイルにエントリを作成します。これはエディタで直接編集。
$ cat /etc/lxc/lxc-usernet
plamo veth lxcbr0 10
個人用の lxc 設定をコピーします。そして非特権コンテナ用のIDマッピングの設定を追加します。
$ mkdir -p ~/.config/lxc $ cp /etc/lxc/default.conf ~/.config/lxc/ $ (編集) $ cat ~/.config/lxc/default.conf lxc.net.0.type = veth lxc.net.0.link = lxcbr0 lxc.net.0.flags = up lxc.net.0.hwaddr = 00:16:3e:xx:xx:xx # これ以降が追加した行 lxc.idmap = u 0 200000 65536 lxc.idmap = g 0 200000 65536
記事では lxc.id_map
でしたが、現在のバージョンである 3.x 系では lxc.idmap
になっていますので注意が必要です。
pam_cgfs
記事の時点では、ユーザーが書き込み可能な cgroup をシェルスクリプトで作成し、各 cgroup に PID を登録していました。
今では lxc に pam_cgfs
という PAM モジュールが付属していますので、これを使うとログイン時にユーザー権限の cgroup を作成してくれます。
/etc/pam.d/system-session
ファイルの最後に次のように追加します。-c
オプションの後にコントローラー名をカンマ区切りで指定するか、all
を指定します。all
だとシステム上で使えるすべてのコントローラーにユーザー用の cgroup を使います。
session optional pam_cgfs.so -c all
一部のコントローラーだけ必要であれば、次のように書きます。Ubuntu なんかでは次のように書かれています。
session optional pam_cgfs.so -c freezer,memory,name=systemd
これで freezer
、memory
コントローラー用の cgroup 配下と、systemd が作る systemd
という cgroup 配下にユーザー用の cgroup を作ります。
Plamo の場合、ssh でアクセスしたときは system-session
ファイルは使いませんので、/etc/pam.d/sshd
ファイルの末尾に上記の行を追加してください。
これでログインすると、こんな感じにユーザー用の cgroup が作られ、ログインしたシェルが登録されます。
$ cat /proc/self/cgroup 14:rdma:/user/plamo/0 13:pids:/user/plamo/0 12:hugetlb:/user/plamo/0 11:net_prio:/user/plamo/0 10:perf_event:/user/plamo/0 9:net_cls:/user/plamo/0 8:freezer:/user/plamo/0 7:devices:/user/plamo/0 6:memory:/user/plamo/0 5:blkio:/user/plamo/0 4:cpuacct:/user/plamo/0 3:cpu:/user/plamo/0 2:cpuset:/user/plamo/0 1:name=systemd:/user/plamo/0
各コントローラー用の cgroup 配下に /user/
ディレクトリを挟んでユーザー名のディレクトリ(= cgroup)が作られ、さらにはセッションID名のcgroupができています。自身のシェルプロセスの /proc/self/cgroup
ファイルを見ると、上記のようにそのディレクトリにプロセスが登録されているのがわかります。
これで準備は OK のはずなので、Ubuntu コンテナを作成し、起動してみます。
$ lxc-create -t download c2 -- -d ubuntu -r bionic -a amd64 $ lxc-start c2 $ lxc-attach c2 $ lxc-ls -f NAME STATE AUTOSTART GROUPS IPV4 IPV6 UNPRIVILEGED c2 RUNNING 0 - - - true
起動しましたが、待っていてもアドレスは割り振られません。中で確認してみます。
$ lxc-attach c2 root@c2:/# ps aux USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND root 1 0.0 0.2 76408 5408 ? Ss 11:15 0:00 /sbin/init root 7 0.0 0.1 21444 3328 pts/2 Ss 11:16 0:00 /bin/bash root 10 0.0 0.1 37336 2856 pts/2 R+ 11:16 0:00 ps aux
というように init
とシェルしかプロセスがありません。
どうやら systemd でホスト側に systemd が作成した cgroup が必要のようです(ちゃんと調べたわけではないですが)。
ホスト側で必要な systemd 用 cgroup を作成
そこで systemd が作る cgroup を自分で作ってみます。
$ sudo mkdir -p /sys/fs/cgroup/systemd/user.slice/user-$(id -u plamo).slice $ sudo chown $(id -u plamo):$(id -g plamo) /sys/fs/cgroup/systemd/user.slice/user-$(id -u plamo).slice $ sudo chown $(id -u plamo):$(id -g plamo) /sys/fs/cgroup/systemd/user.slice/user-$(id -u plamo).slice/{cgroup.procs,tasks}
各コントローラー用には次のようなスクリプトをでっち上げます。
UNPRIV_USERS="plamo" mkdir -p /sys/fs/cgroup/systemd mount -t cgroup -o none,name=systemd cgroup /sys/fs/cgroup/systemd mkdir -p /sys/fs/cgroup/systemd/user.slice for u in $UNPRIV_USERS do USER_CG="/sys/fs/cgroup/systemd/user.slice/user-$(id -u $u).slice" mkdir -p $USER_CG chown $(id -u $u):$(id -g $u) $USER_CG chown $(id -u $u):$(id -g $u) $USER_CG/{cgroup.procs,tasks} done for sys in $(awk '!/^#/ { if ($4 == 1) print $1 }' /proc/cgroups); do mkdir -p $sys if ! mountpoint -q $sys; then if ! mount -n -t cgroup -o $sys cgroup $sys; then rmdir $sys || true else echo 1 > /sys/fs/cgroup/$sys/cgroup.clone_children for u in $UNPRIV_USERS do USER_CG="$sys/user.slice/user-$(id -u $u).slice" mkdir -p $USER_CG chown $(id -u $u):$(id -g $u) $USER_CG chown $(id -u $u):$(id -g $u) $USER_CG/{cgroup.procs,tasks} echo 1 > $USER_CG/cgroup.clone_children if [ $sys = "cpuset" ]; then chown $(id -u $u):$(id -g $u) $USER_CG/cpuset.{cpus,mems} echo 0 > $USER_CG/cpuset.cpus echo 0 > $USER_CG/cpuset.mems fi done fi fi done
つまり各 cgroup 配下に user.slice/user-$(id).slice
という cgroup を作って、ユーザー権限を与えるわけですね。
次のようにディレクトリができます。
$ find /sys/fs/cgroup/memory/ | grep user.slice /sys/fs/cgroup/memory/user.slice /sys/fs/cgroup/memory/user.slice/memory.memsw.usage_in_bytes /sys/fs/cgroup/memory/user.slice/memory.use_hierarchy :(snip) /sys/fs/cgroup/memory/user.slice/user-1000.slice /sys/fs/cgroup/memory/user.slice/user-1000.slice/memory.memsw.usage_in_bytes /sys/fs/cgroup/memory/user.slice/user-1000.slice/memory.use_hierarchy /sys/fs/cgroup/memory/user.slice/user-1000.slice/memory.kmem.tcp.max_usage_in_bytes /sys/fs/cgroup/memory/user.slice/user-1000.slice/memory.kmem.slabinfo
これでコンテナを起動させてみます。
$ lxc-start c2 $ lxc-ls -f NAME STATE AUTOSTART GROUPS IPV4 IPV6 UNPRIVILEGED c2 RUNNING 0 - 10.0.3.223 - true $ lxc-attach c2 root@c2:/# ps aux USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND root 1 0.8 0.4 77040 8312 ? Ss 11:58 0:00 /sbin/init root 17 0.0 0.4 78312 9700 ? Ss 11:58 0:00 /lib/systemd/systemd-journald root 26 0.0 0.1 42104 3408 ? Ss 11:58 0:00 /lib/systemd/systemd-udevd systemd+ 31 0.0 0.2 80028 5148 ? Ss 11:58 0:00 /lib/systemd/systemd-networkd systemd+ 50 0.0 0.2 70616 5128 ? Ss 11:58 0:00 /lib/systemd/systemd-resolved root 54 0.0 0.1 31292 3036 ? Ss 11:58 0:00 /usr/sbin/cron -f root 55 0.6 0.8 170360 16912 ? Ssl 11:58 0:00 /usr/bin/python3 /usr/bin/networkd-dispatcher --run-startup-triggers root 56 0.0 0.2 62008 5508 ? Ss 11:58 0:00 /lib/systemd/systemd-logind message+ 57 0.0 0.2 49928 4196 ? Ss 11:58 0:00 /usr/bin/dbus-daemon --system --address=systemd: --nofork --nopidfile --systemd-activation --syslog-only syslog 58 0.0 0.1 193400 4040 ? Ssl 11:58 0:00 /usr/sbin/rsyslogd -n root 59 0.0 0.1 61816 2956 ? Ss 11:58 0:00 /lib/systemd/systemd-hostnamed root 63 0.0 0.1 15956 2392 pts/3 Ss+ 11:58 0:00 /sbin/agetty -o -p -- \u --noclear --keep-baud pts/3 115200,38400,9600 vt220 root 64 0.0 0.1 15956 2324 pts/1 Ss+ 11:58 0:00 /sbin/agetty -o -p -- \u --noclear --keep-baud pts/1 115200,38400,9600 vt220 root 65 0.0 0.1 15956 2308 pts/1 Ss+ 11:58 0:00 /sbin/agetty -o -p -- \u --noclear --keep-baud console 115200,38400,9600 linux root 66 0.0 0.1 15956 2264 pts/2 Ss+ 11:58 0:00 /sbin/agetty -o -p -- \u --noclear --keep-baud pts/2 115200,38400,9600 vt220 root 67 0.0 0.1 15956 2296 pts/0 Ss+ 11:58 0:00 /sbin/agetty -o -p -- \u --noclear --keep-baud pts/0 115200,38400,9600 vt220 root 70 0.0 0.1 21444 3264 pts/2 Ss 11:58 0:00 /bin/bash root 73 0.0 0.1 37336 2996 pts/2 R+ 11:58 0:00 ps aux
ちゃんと起動していますね。pids
、devices
、systemd
cgroup 配下に色々と systemd が cgroup を作っているので、この辺りがホストにも必要だったということでしょう。
Plamo 7.x の現時点の最新の cgroupfs_mount パッケージ 1.8 では、上のような処理を反映させたスクリプトにしてありますので、これをインストールして、/etc/sysconfig/cgroupfs-mount
ファイル内で非特権コンテナを起動したいユーザーを指定すれば OK です。
$ cat /etc/sysconfig/cgroupfs-mount # List of non-root users who want to create owned cgroups UNPRIV_USERS="plamo"
まとめ
以上のようにダミーで systemd が作る cgroup ツリーを作成すれば、systemd 採用のコンテナは起動しました。
今回はコンテナとして Ubuntu bionic を使っていますが、もう少し古い systemd だと /sys/fs/cgroup/systemd
という cgroup ツリーさえあれば起動していた気がします。今後も systemd の変化とともに動きが変わるかもしれません。