TenForward

技術ブログ。はてなダイアリーから移転しました

Linux 4.14 で導入された Namespaced file capabilities(1)

Hosting Casual Talks #5

本題に入る前に、このブログエントリのネタを発表してきた勉強会の感想を。

ホスティング業界に関わるエンジニアが カジュアルに 情報交換をするという、全員発表型の勉強会 "Hosting Casual Talks #5" に参加してきました。飲みながら技術について情報交換できる カジュアルな 勉強会ではありますが、内容が全く カジュアルじゃない!!

connpass.com

ひとりあたり10 or 15分の LT サイズの時間割り当てなのですが、皆さんちゃんと解決したい問題があり、仮説を立て、検証して、考察すると言ったストーリー性のあるスライドになっていたり、問題を解決するためにコード書きました、こういうの作りましたというような、とても LT サイズに収まるものではなく、全員持ち時間はどこへやら、ガッツリと 6 時間、ガチな内容がひたすら続くなかなか内容の濃い勉強会になりました。

で、そこで真面目に 10 分サイズで軽めに作ったネタがコレでした。一発芸でオチもなく、Linux カーネルの機能を紹介してきました。次々とすごいガチな発表がされるので、こんなネタで発表するのが徐々に辛くなって…😅


4.14 kernel はいくつかコンテナ関連の機能が追加されているのですが、その中のひとつである "Namespaced file capabilities" の紹介です。

"Namespaced file capabilities" 機能についてはこのあたりが詳しいです。

コミットはこちら

File Capability

File Capability については色々と参考になる情報がありますので、そちらをどうぞ。

ここでは File Capability の説明あるあるの ping コマンドに cap_net_raw を与えて色々試してみます。

File Capability お試し

まずは普通に。ここでは Linux 4.13.16 上の Plamo Linux 7.0 で試しています。libcap は 2.26。

通常は setuid されている ping をコピーすると setuid は落とされて ping は一般ユーザーでは実行できません。

$ id -u
1000 (一般ユーザーです)
$ ls -l /bin/ping
-rwsr-sr-x 1 root root 56,920  2月 12日  20:31 /bin/ping* (setuid されています)
$ cp /bin/ping . (コピーする)
$ ls -l ./ping   (setuid は落ちています)
-rwxr-xr-x 1 karma users 56,920  3月 23日  23:35 ./ping*
$ ./ping www.google.com (setuid されていないので権限がなく実行できません)
ping: socket: Operation not permitted

ここで file capability を設定すると一般ユーザーでも限定的な権限が与えられ、実行できるようになります。

$ sudo setcap cap_net_raw+ep ./ping (cap_net_raw を与えます)
$ sudo getcap -n ./ping (cap_net_raw が設定されています)
./ping = cap_net_raw+ep
$ sudo getfattr -d -m "security" ./ping (拡張属性領域の確認。File Capability はここに保存されています)
# file: ping
security.capability=0sAQAAAgAgAAAAAAAAAAAAAAAAAAA=

これで一般ユーザーでも ping が実行できます。

$ ./ping -c 1 www.google.com
PING www.google.com(kix05s01-in-x04.1e100.net (2404:6800:400a:808::2004)) 56 data bytes
64 bytes from kix05s01-in-x04.1e100.net (2404:6800:400a:808::2004): icmp_seq=1 ttl=51 time=7.08 ms

--- www.google.com ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 7.087/7.087/7.087/0.000 ms

コンテナ内で File Capability を設定してみる(4.13.16 kernel)

これと同じ操作を一般ユーザー権限で実行しているコンテナ内で行ってみます。次のように root が uid: 200000 のユーザーにマッピングされているコンテナがあります。

karma@plamo71:~$ lxc-info c3
Name:           c3
State:          RUNNING
PID:            20569
IP:             10.0.3.122
CPU use:        0.17 seconds
BlkIO use:      12.45 MiB
Memory use:     16.59 MiB
KMem use:       1.89 MiB
Link:           vethBXNXMY
 TX bytes:      1.55 KiB
 RX bytes:      9.86 KiB
 Total bytes:   11.41 KiB
karma@plamo71:~$ sudo cat /proc/20569/{u,g}id_map
         0     200000      65536
         0     200000      65536
karma@plamo71:~$ ps auxf 20569
  : (snip)
200000   20569  0.0  0.0   4500  1600 pts/1    Ss+  18:38   0:00  \_ init [3]
200000   20772  0.0  0.1 172404  2488 ?        Ssl  18:38   0:00      \_ /sbin/rsyslogd -i /run/rsyslogd.pid
  : (snip)

このコンテナ内に入り、一般ユーザー権限で ping をコピーしてみます。当然実行はできません。

$ lxc-attach c3
# su - karma
$ id
uid=1000(karma) gid=100(users) groups=100(users),10(wheel),26(audio),28(dialout),29(video),32(cdrom),36(kvm),38(pulse),39(pulse-access),44(mlocate),47(libvirt),50(input)
$ uname -a
Linux c3 4.13.16-plamo64 #1 SMP PREEMPT Wed Dec 5 15:00:28 JST 2018 x86_64 GNU/Linux
$ cp /bin/ping .
$ ls -l ./ping 
-rwxr-xr-x 1 karma users 56,920  2月 20日  19:02 ./ping*
$ ./ping -c 1 www.google.com
ping: socket: Operation not permitted

ここで capability を設定してみましょう。

$ sudo setcap cap_net_raw+ep ./ping 
Failed to set capabilities on file `./ping' (Operation not permitted)
usage: setcap [-q] [-v] [-n <rootid>] (-r|-|<caps>) <filename> [ ... (-r|-|<capsN>) <filenameN> ]

 Note <filename> must be a regular (non-symlink) file.

エラーで setcap コマンドが実行できません。

4.14 以降のカーネルの コンテナ内で File Capability を設定してみる(4.20.10 kernel)

勉強会のときは 5.0.0 カーネルで試したデモを披露しましたが、ここでは 4.20.10 で試した結果です。

ちなみに 4.14 より前のカーネルを試そうと思って Ubuntu 16.04 の 4.4 kernel なら試せる!と思ったりすると、普通にこの機能はバックポートされていますのでご注意ください。つまりUbuntu 16.04 の 4.4 kernel でもこの機能を試せます。(←ハマった人)(2019-03-24 追記)

$ uname -a
Linux plamo71 4.20.10-plamo64 #5 SMP PREEMPT Mon Feb 18 16:16:45 JST 2019 x86_64 GNU/Linux
$ lxc-start c3
$ lxc-ls -f c3
NAME STATE   AUTOSTART GROUPS IPV4       IPV6 UNPRIVILEGED 
c3   RUNNING 0         -      10.0.3.122 -    true         
$ lxc-info c3
Name:           c3
State:          RUNNING
PID:            4817
IP:             10.0.3.122
CPU use:        0.17 seconds
BlkIO use:      12.55 MiB
Memory use:     16.97 MiB
KMem use:       2.16 MiB
Link:           vethH3LNCN
 TX bytes:      1.28 KiB
 RX bytes:      9.31 KiB
 Total bytes:   10.59 KiB
$ sudo cat /proc/4817/{u,g}id_map
         0     200000      65536
         0     200000      65536
$ ps auxf | less
  : (snip)
200000    4817  0.0  0.0   4500  1632 pts/1    Ss+  16:24   0:00  \_ init [3]
200000    5019  0.0  0.1 172404  2472 ?        Ssl  16:24   0:00      \_ /sbin/rsyslogd -i /run/rsyslogd.pid
  : (snip)

カーネルのバージョンが違う以外は同じコンテナを実行しています。

$ lxc-attach c3
# su - karma
$ id
uid=1000(karma) gid=100(users) groups=100(users),10(wheel),26(audio),28(dialout),29(video),32(cdrom),36(kvm),38(pulse),39(pulse-access),44(mlocate),47(libvirt),50(input)
$ uname -a
Linux c3 4.20.10-plamo64 #5 SMP PREEMPT Mon Feb 18 16:16:45 JST 2019 x86_64 GNU/Linux
$ cp /bin/ping .
$ ls -l ./ping
-rwxr-xr-x 1 karma users 56,920  2月 21日  16:30 ./ping*
$ ./ping www.google.com
ping: socket: Operation not permitted

当然ですが、ここまでは 4.13 カーネルのときと同じです。ここで 4.13 では失敗した setcap を試してみましょう。

$ sudo setcap cap_net_raw+ep ./ping
$ sudo getcap -n ./ping 
./ping = cap_net_raw+ep [rootid=200000]
$ sudo getfattr -d -m "security" ./ping 
# file: ping
security.capability=0sAQAAAwAgAAAAAAAAAAAAAAAAAABADQMA

4.13 では失敗した setcap が成功しています。ここで getcap-n を与えている所がミソで、これは結果のうち [rootid=200000] を表示させるために使っています。この 200000 は、コンテナの rootマッピングされている uid ですね。

つまり user namespace 内で setcap を実行すると、user namespace 内の root の uid の情報が設定されます。そして、その user namespace 内でのみ、その File capability が有効になり、権限が与えられた状態でコマンドが実行できます。

$ ./ping -c 1 www.google.com
PING www.google.com (216.58.197.4) 56(84) bytes of data.
64 bytes from kix06s02-in-f4.1e100.net (216.58.197.4): icmp_seq=1 ttl=48 time=1.30 ms

--- www.google.com ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 1.303/1.303/1.303/0.000 ms

問題なく実行できていますね。

上記のファイルをコンテナの外で確認してみましょう。

$ id
uid=1000(karma) gid=100(users) groups=100(users),10(wheel),26(audio),28(dialout),29(video),32(cdrom),36(kvm),38(pulse),39(pulse-access),44(mlocate),47(libvirt),50(input)
$ sudo getcap -n ~/.local/share/lxc/c3/rootfs/home/karma/ping
/home/karma/.local/share/lxc/c3/rootfs/home/karma/ping = cap_net_raw+ep [rootid=200000]
$ ~/.local/share/lxc/c3/rootfs/home/karma/ping -c 1 www.google.com
ping: socket: Operation not permitted

コンテナ外で確認しても getcap の情報は変わりません。そしてコンテナ(user namespace)外から実行すると、設定したはずの capability は有効ではなく、ping は実行できません。

(つづく)

tenforward.hatenablog.com

Linux 4.14 で導入された cpuset の cgroup v2 mode

(2019-02-23 少し追記しています)

4.14 kernel から cgroup の cpuset コントローラーに cpuset_v2_mode というオプションが使えるようになりました。

しばらく見ないうちに cpuset コントローラーをマウントするとファイルがたくさん増えていますが、このオプションを設定すると、従来からある cpuset.{cpus,mems} ファイルの動きが変わるようです。そしていつの間にか増えている cpuset.effective_{cpus,mems} ファイルと合わせて使って cpuset の制限を行うようです。

このオプションを設定したときの動きが、cgroup v2 の cpuset の動きになるようです(v2 の cpuset は現時点ではまだマージされていません)。

現在使える cpu や memory が effective で表示されており、それと cpuset.{cpus,mems} のマスクを取って、使える cpu, memory を導き出すようですね。

従来の cpuset(v1 mode)

# mount -t tmpfs cgroup /sys/fs/cgroup/
# mkdir /sys/fs/cgroup/cpuset
# mount -t cgroup -o cpuset cgroup /sys/fs/cgroup/cpuset/
# cd /sys/fs/cgroup/cpuset/
# mkdir test01
# cd test01
# echo "0-1" > cpuset.cpus
# echo "0" > cpuset.mems

この状態で test01 cgroup 内のファイルは、

# for f in $(ls cpuset.*cpus); do echo -n "$f: "; cat $f; done
cpuset.cpus: 0-1
cpuset.effective_cpus: 0-1

となっています。ここでおもむろに cpu をひとつオフラインにします。cpu1 です。

# echo 0 > /sys/devices/system/cpu/cpu1/online

すると、

# for f in $(ls cpuset.*cpus); do echo -n "$f: "; cat $f; done
cpuset.cpus: 0
cpuset.effective_cpus: 0

cpuset.cpuscpuset.effective_cpus0 になります。

ここで再度 cpu1 をオンラインにすると、

echo 1 > /sys/devices/system/cpu/cpu1/online
# cat /sys/devices/system/cpu/cpu1/online
1
# for f in $(ls cpuset.*cpus); do echo -n "$f: "; cat $f; done
cpuset.cpus: 0
cpuset.effective_cpus: 0

特に変化はありません。

cpuset_v2_mode 付きマウント(v2 mode)

マウントオプションとして cpuset_v2_mode を与えます。

# mount -t cgroup -o cpuset,cpuset_v2_mode cgroup /sys/fs/cgroup/cpuset/
# cat /proc/self/mounts | grep cpuset
cgroup /sys/fs/cgroup/cpuset cgroup rw,relatime,cpuset,cpuset_v2_mode 0 0

この状態では、さきほどと特に変化はありません。

# for f in $(ls cpuset.*cpus); do echo -n "$f: "; cat $f; done
cpuset.cpus: 0-1
cpuset.effective_cpus: 0-1

ここで cpu1 をオフラインにします。

# echo 0 > /sys/devices/system/cpu/cpu1/online

すると、

# for f in $(ls cpuset.*cpus); do echo -n "$f: "; cat $f; done
cpuset.cpus: 0-1
cpuset.effective_cpus: 0

cpuset.cpus はそのままで実際の cpuset.effective_cpus のみ値が変わります。

ここまで来ると動きは予想できますね。再度 cpu1 をオンラインにしてみます。

# echo 1 > /sys/devices/system/cpu/cpu1/online
# for f in $(ls cpuset.*cpus); do echo -n "$f: "; cat $f; done
cpuset.cpus: 0-1
cpuset.effective_cpus: 0-1

cgroup作成後にCPUが増えた時(2019-02-23 追記)

cgroup作成後にcpuが減って、また戻った場合の動きは見ましたが、cgroup作成後にonline の cpu が増えた場合にどうなるのかが気になったので試してみました。

# echo 0 > /sys/devices/system/cpu/cpu2/online
# mkdir test01
# cd test01
# cat cpuset.cpus

# cat cpuset.effective_cpus 
0-1
# echo "0-1" > cpuset.cpus 
# echo "0" > cpuset.mems 
# for f in $(ls cpuset.*cpus); do echo -n "$f: "; cat $f; done
cpuset.cpus: 0-1
cpuset.effective_cpus: 0-1
# echo 1 > /sys/devices/system/cpu/cpu2/online
# for f in $(ls cpuset.*cpus); do echo -n "$f: "; cat $f; done
cpuset.cpus: 0-1
cpuset.effective_cpus: 0-1

上の例のように CPU をひとつ(cpu2) offline にしておいて、cgroup を作成後、cpu2 を online にしても cpuset.effective_cpus の値は変化しません。

まとめ

CPU/メモリーの online, offline を切り替えると:

  • v1 mode: cpuset.{cpus,mems}cpuset.effective_{cpus,mems} の値も変化するが、その後再度状態が変わってもそのまま
  • v2 mode: cpuset.{cpus,mems} の値は変化しない。cpuset.effective_{cpus,mems} の値は実際に合わせて変化する

ただし、cpuset.effective_* の復元は cgroup 作成時の値まで。

おまけ(2019-02-23 追記)

ちなみにこのオプションが使えるようになったコミットは次のものですが、これはオプションの追加と、そのオプションが指定されているときの条件分岐を変更しているだけですので、処理自体はこの前にあらかじめ実装されているようですね。

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

これで freezermemoryコントローラー用の 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

ちゃんと起動していますね。pidsdevicessystemd 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 の変化とともに動きが変わるかもしれません。

Linux 4.20 で導入の PSI 機能(2)

前回 はとりあえず /proc/pressure の中身を見てみましたが、PSI 機能は cgroup v2 とも連携しています。というわけで、今回は cgroup v2 をマウントしてみましょう(前回以上に中身なし)。

まずは cgroup v2 をマウントして、コントローラーを子 cgroup で使えるように登録します。このあたりは連載 をどうぞ。

$ sudo mount -t cgroup2 -o nsdelegate cgroup2 /sys/fs/cgroup/
$ echo "+io +memory +cpu +pids" | tee /sys/fs/cgroup/cgroup.subtree_control
+io +memory +cpu +pids
$ cat /sys/fs/cgroup/cgroup.subtree_control
cpu io memory pids

子 cgroup を作って中身を覗いてみましょう。

$ sudo mkdir test01
$ ls
cgroup.controllers      cgroup.stat             cpu.pressure     io.max          memory.high       memory.pressure      pids.current
cgroup.events           cgroup.subtree_control  cpu.stat         io.pressure     memory.low        memory.stat          pids.events
cgroup.max.depth        cgroup.threads          cpu.weight       io.stat         memory.max        memory.swap.current  pids.max
cgroup.max.descendants  cgroup.type             cpu.weight.nice  memory.current  memory.min        memory.swap.events
cgroup.procs            cpu.max                 io.latency       memory.events   memory.oom.group  memory.swap.max

"pressure" という名前が付くファイルが登場していますね。

$ for f in $(ls *.pressure); do echo "$f: "; cat $f; done
cpu.pressure: 
some avg10=0.00 avg60=0.00 avg300=0.00 total=0
io.pressure: 
some avg10=0.00 avg60=0.00 avg300=0.00 total=0
full avg10=0.00 avg60=0.00 avg300=0.00 total=0
memory.pressure: 
some avg10=0.00 avg60=0.00 avg300=0.00 total=0
full avg10=0.00 avg60=0.00 avg300=0.00 total=0

はい。前回の /proc/pressure 以下に登場したファイルと同じような中身ですね。

Linux 4.20 で導入の PSI 機能(1)

以前から Facebook のスライドなんかでは見かけた PSI という機能が 4.20 カーネルで入ったようですね。

CONFIG_PSI:

Collect metrics that indicate how overcommitted the CPU, memory,
and IO capacity are in the system.

If you say Y here, the kernel will create /proc/pressure/ with the
pressure statistics files cpu, memory, and io. These will indicate
the share of walltime in which some or all tasks in the system are
delayed due to contention of the respective resource.

In kernels with cgroup support, cgroups (cgroup2 only) will
have cpu.pressure, memory.pressure, and io.pressure files,
which aggregate pressure stalls for the grouped tasks only.

For more details see Documentation/accounting/psi.txt.

これを有効にすれば良さそう。さっそく Y で 4.20 を make。/proc/pressure なんてディレクトリが現れて、そこにファイルが 3 つほど置かれていました。

$ uname -a
Linux discovery 4.20.0-plamo64 #2 SMP PREEMPT Mon Jan 7 16:31:47 JST 2019 x86_64 GNU/Linux
$ for f in $(ls /proc/pressure/); do echo "$f: "; cat /proc/pressure/$f; done
cpu: 
some avg10=0.00 avg60=0.00 avg300=0.00 total=60048550
io: 
some avg10=0.00 avg60=0.00 avg300=0.00 total=30250330
full avg10=0.00 avg60=0.00 avg300=0.00 total=28401529
memory: 
some avg10=0.00 avg60=0.00 avg300=0.00 total=146509
full avg10=0.00 avg60=0.00 avg300=0.00 total=100482

cgroup2 をマウントすれば、cgroup にも同じようなファイルが現れるらしいがとりあえず今日はここまで(内容なさすぎ)。