仮想環境(4)

Date: 2021/02/06 (initial publish), 2021/07/13 (last update)

Source: jp/note-00031.md

Previous Post Top Next Post

TOC

特権ユーザーでのシステムとしてのLXC仮想環境はほぼchrootと変わらないので それほど違和感はありませんでした。

この次に非特権ユーザー権限から立ち上げられたLXC仮想環境を実現するための 技術背景を学ぶために、ここはひとまずLXCのコマンドを離れ Linux Namespaces 周辺に着目します。Linux Namespaces は伝統的なUNIXの範疇を越えているので、 Linuxがカーネルレベルでどこがどう拡張されているのかを2021年2月の時点の Debian Bulleseye/testing pre-11 release環境で確認していきます。

Linux Namespaces

どうもLXCも含めた各種コンテナ環境は2002年以降拡充されてきた LinuxのNamespaces というカーネルの機能を使っていることがキーだと分かりました。

とりあえず、以下の関連マンページに目を通しました。

Cgroup Namespaces

Linux Namespacesの中でcgroup_namespaces(7)は、/proc/[pid]/cgroup/proc/[pid]/mountinfo から見えるプロセスのcgroups (コントロールグループ) の見え方を仮想化します。各種コンテナ環境を可能にする仮想化技術の中心的な要素 としておもしろそうだったので、自分なりに追加の状況確認をしながらtraceしたの ですがマンページの通りには行きませんでした。

「Linuxあるある」は、マンページの記載が嘘ではないけど古いスタイルの設定 前提で、現状とずれている可能性を感じました。現在はBulleseyeがtestingにあり、 リリース前です。まず現在のDebianの関連インフラ状況をまとめてみました。

Debian release Kernel version util-linux cgroup hierarchy Year
Buster 10 4.19系 2.33系 hybrid (v1+v2) 2019
Bulleseye 11 5.10系 2.36系 unified (v2 only) 2021 ?

ここで、2015年に書かれたという、 Control Group v2 によると、hybridヒエラルキーでは“/proc/$PID/cgroup” 中に複数行でプロセスの cgroupメンバーシップをリストするのに対して、unifiedヒエラルキーでは “/proc/$PID/cgroup” 中の単一行で常に“0::$PATH”だけで複数行でプロセスの cgroupメンバーシップをリストしているとのことでした。

こう見ると現行のcgroup_namespaces(7)の例はhybridヒエラルキー前提なので、 ずれている分かりました。

cgroup v2のunifiedヒエラルキー環境に合わせてでコマンドを調整をしながら cgroup_namespaces(7)の例を以下でtraceすることにします。この際に、 各環境で以下の cgck関数を定義して状況の確認に利用します。

... # declare -f cgck
cgck ()
{
    for pid in 1 $(ps --no-headers -o pid);
    do
        if [ -e "/proc/$pid/cgroup" ]; then
            ps --no-headers o pid,cmd --pid "$pid";
            cat "/proc/$pid/cgroup";
            grep cgroup2 "/proc/$pid/mountinfo";
            echo " ---";
        fi;
    done
}

ここでプロセスを取り巻く状況をモニターしている/proc/$pid/*ファイルの内容 の意味は、proc(5)に説明されています。

まずroot権限のBash shellを環境設定抜きで立ち上げ、バックグラウンド プロセスも立ち上げます。

$ PS1='sh1\$ ' sudo bash --norc
sh1# sleep 1000000 &
[1] 397732

この状況を上記のcgck関数を定義して確認します。

sh1# cgck
      1 /sbin/init
0::/init.scope
32 22 0:27 / /sys/fs/cgroup rw,nosuid,nodev,noexec,relatime shared:9 - cgroup2 cgroup2 rw
 ---
 392577 sudo bash --norc
0::/user.slice/user-1000.slice/user@1000.service/app.slice/app-org.gnome.Terminal.slice/vte-spawn-445f3600-af41-459a-9ac3-1e1c5633a188.scope
32 22 0:27 / /sys/fs/cgroup rw,nosuid,nodev,noexec,relatime shared:9 - cgroup2 cgroup2 rw
 ---
 392578 bash --norc
0::/user.slice/user-1000.slice/user@1000.service/app.slice/app-org.gnome.Terminal.slice/vte-spawn-445f3600-af41-459a-9ac3-1e1c5633a188.scope
32 22 0:27 / /sys/fs/cgroup rw,nosuid,nodev,noexec,relatime shared:9 - cgroup2 cgroup2 rw
 ---
 397732 sleep 1000000
0::/user.slice/user-1000.slice/user@1000.service/app.slice/app-org.gnome.Terminal.slice/vte-spawn-445f3600-af41-459a-9ac3-1e1c5633a188.scope
32 22 0:27 / /sys/fs/cgroup rw,nosuid,nodev,noexec,relatime shared:9 - cgroup2 cgroup2 rw
 ---

気になるプロセスのcgroup PATHやcgroupの ファイルシステムのmount point を抜き出して表にします。Systemd+GUI環境のconsoleからなので少々 cgroup PATHが長めです。

command bash --norc sleep 1000000
PID 392578 397732
cgroup PATH /user.slice/...a188.scope /user.slice/...a188.scope
cgroup2-fs mount point / /

さてこれらの気になるプロセスのcgroup帰属を変更します。

sh1# echo $$
392578
sh1# mkdir -p /sys/fs/cgroup/freezer/sub
sh1# echo $$ >/sys/fs/cgroup/freezer/sub/cgroup.procs
sh1# echo $!
397732
sh1# mkdir -p /sys/fs/cgroup/freezer/sub2
sh1# echo $! >/sys/fs/cgroup/freezer/sub2/cgroup.procs
sh1# cgck
      1 /sbin/init
0::/init.scope
32 22 0:27 / /sys/fs/cgroup rw,nosuid,nodev,noexec,relatime shared:9 - cgroup2 cgroup2 rw
 ---
 392577 sudo bash --norc
0::/user.slice/user-1000.slice/user@1000.service/app.slice/app-org.gnome.Terminal.slice/vte-spawn-445f3600-af41-459a-9ac3-1e1c5633a188.scope
32 22 0:27 / /sys/fs/cgroup rw,nosuid,nodev,noexec,relatime shared:9 - cgroup2 cgroup2 rw
 ---
 392578 bash --norc
0::/freezer/sub
32 22 0:27 / /sys/fs/cgroup rw,nosuid,nodev,noexec,relatime shared:9 - cgroup2 cgroup2 rw
 ---
 397732 sleep 1000000
0::/freezer/sub2
32 22 0:27 / /sys/fs/cgroup rw,nosuid,nodev,noexec,relatime shared:9 - cgroup2 cgroup2 rw
 ---

ここでcgroupのPATHが新規作成した場所に変わっています。

command bash --norc sleep 1000000
PID 392578 397732
cgroup PATH /freezer/sub /freezer/sub2
cgroup2-fs mount point / /

さて、unshareを用いて新規のcgroupやmountのnamespaces内に新規のshellを 立ち上げましょう。

sh1# PS1='sh2\$ ' unshare --cgroup --mount bash --norc

さてこの状況をcgck関数を定義してそれを使って確認します。

sh2# cgck
      1 /sbin/init
0::/../../init.scope
32 22 0:27 /../.. /sys/fs/cgroup rw,nosuid,nodev,noexec,relatime shared:9 - cgroup2 cgroup2 rw
 ---
 392577 sudo bash --norc
0::/../../user.slice/user-1000.slice/user@1000.service/app.slice/app-org.gnome.Terminal.slice/vte-spawn-445f3600-af41-459a-9ac3-1e1c5633a188.scope
32 22 0:27 /../.. /sys/fs/cgroup rw,nosuid,nodev,noexec,relatime shared:9 - cgroup2 cgroup2 rw
 ---
 392578 bash --norc
0::/
32 22 0:27 /../.. /sys/fs/cgroup rw,nosuid,nodev,noexec,relatime shared:9 - cgroup2 cgroup2 rw
 ---
 397732 sleep 1000000
0::/../sub2
32 22 0:27 /../.. /sys/fs/cgroup rw,nosuid,nodev,noexec,relatime shared:9 - cgroup2 cgroup2 rw
 ---
 425474 bash --norc
0::/
503 501 0:27 /../.. /sys/fs/cgroup rw,nosuid,nodev,noexec,relatime - cgroup2 cgroup2 rw
 ---
sh2# echo $$
425474

unsharebash --norc として立ち上がった新規のshellから見たから見える各プロセスの cgroupのPATHは、/sys/fs/cgroup/freeze/subからの相対になるので変わります。 cgroup2のファイルシステムの mount pointも、/sys/fs/cgroup/freeze/subからの 相対になるので変わります。

command bash --norc sleep 1000000 bash --norc
PID 392578 397732 425474
cgroup PATH / /../sub2 /
cgroup2-fs mount point /../.. /../.. /../..

ただこうして作成された新規のshell (PID=425474) のcgroup2-fs mount pointは/で あるべきでが、/../..となっています。これを直します。

sh2# mount --make-rslave /
sh2# cgck
      1 /sbin/init
0::/../../init.scope
32 22 0:27 /../.. /sys/fs/cgroup rw,nosuid,nodev,noexec,relatime shared:9 - cgroup2 cgroup2 rw
 ---
 392577 sudo bash --norc
0::/../../user.slice/user-1000.slice/user@1000.service/app.slice/app-org.gnome.Terminal.slice/vte-spawn-445f3600-af41-459a-9ac3-1e1c5633a188.scope
32 22 0:27 /../.. /sys/fs/cgroup rw,nosuid,nodev,noexec,relatime shared:9 - cgroup2 cgroup2 rw
 ---
 392578 bash --norc
0::/
32 22 0:27 /../.. /sys/fs/cgroup rw,nosuid,nodev,noexec,relatime shared:9 - cgroup2 cgroup2 rw
 ---
 397732 sleep 1000000
0::/../sub2
32 22 0:27 /../.. /sys/fs/cgroup rw,nosuid,nodev,noexec,relatime shared:9 - cgroup2 cgroup2 rw
 ---
 425474 bash --norc
0::/
503 501 0:27 /../.. /sys/fs/cgroup rw,nosuid,nodev,noexec,relatime - cgroup2 cgroup2 rw
 ---
sh2# umount /sys/fs/cgroup
sh2# cgck
      1 /sbin/init
0::/../../init.scope
32 22 0:27 /../.. /sys/fs/cgroup rw,nosuid,nodev,noexec,relatime shared:9 - cgroup2 cgroup2 rw
 ---
 392577 sudo bash --norc
0::/../../user.slice/user-1000.slice/user@1000.service/app.slice/app-org.gnome.Terminal.slice/vte-spawn-445f3600-af41-459a-9ac3-1e1c5633a188.scope
32 22 0:27 /../.. /sys/fs/cgroup rw,nosuid,nodev,noexec,relatime shared:9 - cgroup2 cgroup2 rw
 ---
 392578 bash --norc
0::/
32 22 0:27 /../.. /sys/fs/cgroup rw,nosuid,nodev,noexec,relatime shared:9 - cgroup2 cgroup2 rw
 ---
 397732 sleep 1000000
0::/../sub2
32 22 0:27 /../.. /sys/fs/cgroup rw,nosuid,nodev,noexec,relatime shared:9 - cgroup2 cgroup2 rw
 ---
 425474 bash --norc
0::/
 ---

マウントイベントが他のnamespacesに漏れないようにした上で、一度マウントをはずしました。

command bash --norc sleep 1000000 bash --norc
PID 392578 397732 425474
cgroup PATH / /../sub2 /
cgroup2-fs mount point /../.. /../.. 無し
sh2# mount -t cgroup2  cgroup2 /sys/fs/cgroup/
sh2# cgck
      1 /sbin/init
0::/../../init.scope
32 22 0:27 /../.. /sys/fs/cgroup rw,nosuid,nodev,noexec,relatime shared:9 - cgroup2 cgroup2 rw
 ---
 392577 sudo bash --norc
0::/../../user.slice/user-1000.slice/user@1000.service/app.slice/app-org.gnome.Terminal.slice/vte-spawn-445f3600-af41-459a-9ac3-1e1c5633a188.scope
32 22 0:27 /../.. /sys/fs/cgroup rw,nosuid,nodev,noexec,relatime shared:9 - cgroup2 cgroup2 rw
 ---
 392578 bash --norc
0::/
32 22 0:27 /../.. /sys/fs/cgroup rw,nosuid,nodev,noexec,relatime shared:9 - cgroup2 cgroup2 rw
 ---
 397732 sleep 1000000
0::/../sub2
32 22 0:27 /../.. /sys/fs/cgroup rw,nosuid,nodev,noexec,relatime shared:9 - cgroup2 cgroup2 rw
 ---
 425474 bash --norc
0::/
503 501 0:27 / /sys/fs/cgroup rw,relatime - cgroup2 cgroup2 rw
 ---

cgroup2-fsをマウントしました。

command bash --norc sleep 1000000 bash --norc
PID 392578 397732 425474
cgroup PATH / /../sub2 /
cgroup2-fs mount point /../.. /../.. /

これで新規のshell (PID=425474) のcgroup2-fs mount pointが/となりました。

もう、このPID=425474のプロセスのシェルから、PID=392578やPID=397732の プロセスと同じcgroupのnamespacesに属させる操作はできません。なぜなら /freezer/sub/freezer/sub2/sys/fs/cgroup 以下に無くなっ ているからです。

sh2# tree -L 2 /sys/fs/cgroup
/sys/fs/cgroup
├── cgroup.controllers
├── cgroup.events
├── cgroup.freeze
├── cgroup.max.depth
├── cgroup.max.descendants
├── cgroup.procs
├── cgroup.stat
├── cgroup.subtree_control
├── cgroup.threads
├── cgroup.type
├── cpu.pressure
├── cpu.stat
├── io.pressure
└── memory.pressure

0 directories, 14 files

util-linux

Linux Namespaces関連のunshare(1)やnsenter(1)等は、 util-linux が提供するutil-linuxのソース中にあり、 Debianではutil-linuxバイナリーパッケージの中で提供されています。

util-linuxのソースはmount(8)コマンド等も提供します。

Previous Post Top Next Post