これは、なにをしたくて書いたもの?
使用しているLinux環境のcgroupがv1なのかv2なのかを確認するには?ということで。
Kubernetesのドキュメントに見分け方が書かれていたので、試してみることにしました。
cgroup v1とv2
Linuxでは、プロセスをグループに分けて管理する機能を使って、プロセスに割り当てるリソースを制限しています。
これがcgroupです。
cgroupにはv1とv2の2つがあります。
Control Groups — The Linux Kernel documentation
Control Group v2 — The Linux Kernel documentation
cgroupを使用することで、割り当てるリソースとしてCPU、メモリー、IOを制御できます。
それで、Linux環境ではどちらかのバージョンが使われていることになりますが、この見分け方はどうしたら?ということで。
答えはKubernetesのcgroup v2に関するドキュメントに書かれていました。
こちらですね。
About cgroup v2 / Identify the cgroup version on Linux Nodes
以下のコマンドで確認できるようです。
$ stat -fc %T /sys/fs/cgroup/
このコマンドの出力結果がcgroup2fs
だとcgroup v2、tmpfs
だとcgroup v1だそうです。
また、このページ内にcgroup v2の要求事項と主なLinuxディストリビューションのcgroup v2の対応が書かれていますね。
- About cgroup v2 / Using cgroup v2 / Requirements
- About cgroup v2 / Using cgroup v2 / Linux Distribution cgroup v2 support
cgroup v2の要求事項は、以下です。
- OSディストリビューションでcgroup v2が有効になっていること
- Linuxカーネルが5.8以降であること
- コンテナランタイムがcgroup v2をサポートしていること
- containerd v1.4以降
- cri-o v1.20以降
主要なLinuxディストリビューションのcgroup v2の対応状況は、以下の通りです。
- Container Optimized OS M97以降
- Ubuntu Linux 21.10以降
- 推奨は22.04以降
- Debian GNU/Linux 11 bullseye以降
- Fedora 31以降
- Arch Linux April 2021以降
- RHELおよびRHEL互換ディストリビューションの9以降
この時点で使うLinuxディストリビューションとそのバージョンでだいたい答えは出るのですが、今回はそれを明示的に確認してみようと
思います。
対象には、Ubuntu Linux 22.04 LTSと20.04 LTSを使うことにします。
確認は、cgroup v2 → cgroup v1の順で行います。
環境
今回の環境は、こちら。
Ubuntu Linux 22.04 LTS。
$ lsb_release -a No LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 22.04.3 LTS Release: 22.04 Codename: jammy $ uname -srvmpio Linux 5.15.0-83-generic #92-Ubuntu SMP Mon Aug 14 09:30:42 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux $ docker version Client: Docker Engine - Community Version: 24.0.6 API version: 1.43 Go version: go1.20.7 Git commit: ed223bc Built: Mon Sep 4 12:31:44 2023 OS/Arch: linux/amd64 Context: default Server: Docker Engine - Community Engine: Version: 24.0.6 API version: 1.43 (minimum version 1.12) Go version: go1.20.7 Git commit: 1a79695 Built: Mon Sep 4 12:31:44 2023 OS/Arch: linux/amd64 Experimental: false containerd: Version: 1.6.22 GitCommit: 8165feabfdfe38c65b599c4993d227328c231fca runc: Version: 1.1.8 GitCommit: v1.1.8-0-g82f18fe docker-init: Version: 0.19.0 GitCommit: de40ad0
Ubuntu Linux 20.04 TLS。
$ lsb_release -a No LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 20.04.6 LTS Release: 20.04 Codename: focal $ uname -srvmpio Linux 5.4.0-162-generic #179-Ubuntu SMP Mon Aug 14 08:51:31 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux $ docker version Client: Docker Engine - Community Version: 24.0.6 API version: 1.43 Go version: go1.20.7 Git commit: ed223bc Built: Mon Sep 4 12:32:12 2023 OS/Arch: linux/amd64 Context: default Server: Docker Engine - Community Engine: Version: 24.0.6 API version: 1.43 (minimum version 1.12) Go version: go1.20.7 Git commit: 1a79695 Built: Mon Sep 4 12:32:12 2023 OS/Arch: linux/amd64 Experimental: false containerd: Version: 1.6.22 GitCommit: 8165feabfdfe38c65b599c4993d227328c231fca runc: Version: 1.1.8 GitCommit: v1.1.8-0-g82f18fe docker-init: Version: 0.19.0 GitCommit: de40ad0
それぞれ、以下のVagrant Boxを使っています。
一応、Dockerまで入れておきました。
statコマンドでcgroupのバージョンを確認する
それでは、Kubernetesのドキュメントに習ってstatコマンドでcgroupのバージョンを確認してみましょう。
cgroup v2。
$ stat -fc %T /sys/fs/cgroup/ cgroup2fs
cgroup v1。
$ stat -fc %T /sys/fs/cgroup/ tmpfs
確かに、cgroup v2ではcgroup2fs
、cgroup v1ではtmpfs
となりました。
ここで、statコマンドのオプションを確認してみます。
Ubuntu Manpage: stat - display file or file system status
statは、ファイルまたはファイルシステムの状態を表示するコマンドです。
-f
がファイルシステムの状態を表示するようにするオプションで(通常はファイルの状態を表示)、-c
は出力を指定の書式にする
オプションです。
%T
は対象がキャラクター/ブロックデバイスのスペシャルファイルの場合、マイナーデバイス番号を表示します。
cgroupは、sysfs内にコントローラーをマウントするようですね。
sysfs - _The_ filesystem for exporting kernel objects — The Linux Kernel documentation
- Control Group v2 / Controllers
- cgroup v1は、 Control Groups version 1 — The Linux Kernel documentation 内の各ページを参照
cgroup v2は、cgroup2としてマウントします。
# mount -t cgroup2 none $MOUNT_POINT
Control Group v2 / Basic Operations / Mounting
cgroup v1は、tmpfsとしてマウントします。
1) mount -t tmpfs cgroup_root /sys/fs/cgroup 2) mkdir /sys/fs/cgroup/cpuset 3) mount -t cgroup -ocpuset cpuset /sys/fs/cgroup/cpuset 4) Create the new cgroup by doing mkdir's and write's (or echo's) in the /sys/fs/cgroup/cpuset virtual file system. 5) Start a task that will be the "founding father" of the new job. 6) Attach that task to the new cgroup by writing its PID to the /sys/fs/cgroup/cpuset tasks file for that cgroup. 7) fork, exec or clone the job tasks from this founding father task.
Control Groups / Control Groups / How do I use cgroups ?
cgroup v2、v1それぞれで/sys/fs/cgroup/
内を見ると、こんな感じになっています。
cgroup v2。
$ tree -L 1 /sys/fs/cgroup/ /sys/fs/cgroup/ ├── cgroup.controllers ├── cgroup.max.depth ├── cgroup.max.descendants ├── cgroup.procs ├── cgroup.stat ├── cgroup.subtree_control ├── cgroup.threads ├── cpu.pressure ├── cpu.stat ├── cpuset.cpus.effective ├── cpuset.mems.effective ├── dev-hugepages.mount ├── dev-mqueue.mount ├── init.scope ├── io.cost.model ├── io.cost.qos ├── io.pressure ├── io.prio.class ├── io.stat ├── memory.numa_stat ├── memory.pressure ├── memory.stat ├── misc.capacity ├── proc-sys-fs-binfmt_misc.mount ├── sys-fs-fuse-connections.mount ├── sys-kernel-config.mount ├── sys-kernel-debug.mount ├── sys-kernel-tracing.mount ├── system.slice └── user.slice 10 directories, 20 files
cgroup v1。
$ tree -L 1 /sys/fs/cgroup/ /sys/fs/cgroup/ ├── blkio ├── cpu -> cpu,cpuacct ├── cpu,cpuacct ├── cpuacct -> cpu,cpuacct ├── cpuset ├── devices ├── freezer ├── hugetlb ├── memory ├── net_cls -> net_cls,net_prio ├── net_cls,net_prio ├── net_prio -> net_cls,net_prio ├── perf_event ├── pids ├── rdma ├── systemd └── unified 17 directories, 0 files
これらがcgroupのコントローラーですね。
以降、もう少し違う角度で情報を見ていってみます。
mountコマンドで見てみる
cgroupのコントローラーはsysfsファイルシステム上で動作しているようなので、mountコマンドでも確認できそうです。
cgroup v2。
$ mount | grep cgroup cgroup2 on /sys/fs/cgroup type cgroup2 (rw,nosuid,nodev,noexec,relatime,nsdelegate,memory_recursiveprot)
cgroup v1。
$ mount | grep cgroup tmpfs on /sys/fs/cgroup type tmpfs (ro,nosuid,nodev,noexec,mode=755) cgroup2 on /sys/fs/cgroup/unified type cgroup2 (rw,nosuid,nodev,noexec,relatime,nsdelegate) cgroup on /sys/fs/cgroup/systemd type cgroup (rw,nosuid,nodev,noexec,relatime,xattr,name=systemd) cgroup on /sys/fs/cgroup/hugetlb type cgroup (rw,nosuid,nodev,noexec,relatime,hugetlb) cgroup on /sys/fs/cgroup/net_cls,net_prio type cgroup (rw,nosuid,nodev,noexec,relatime,net_cls,net_prio) cgroup on /sys/fs/cgroup/memory type cgroup (rw,nosuid,nodev,noexec,relatime,memory) cgroup on /sys/fs/cgroup/pids type cgroup (rw,nosuid,nodev,noexec,relatime,pids) cgroup on /sys/fs/cgroup/cpu,cpuacct type cgroup (rw,nosuid,nodev,noexec,relatime,cpu,cpuacct) cgroup on /sys/fs/cgroup/perf_event type cgroup (rw,nosuid,nodev,noexec,relatime,perf_event) cgroup on /sys/fs/cgroup/cpuset type cgroup (rw,nosuid,nodev,noexec,relatime,cpuset) cgroup on /sys/fs/cgroup/rdma type cgroup (rw,nosuid,nodev,noexec,relatime,rdma) cgroup on /sys/fs/cgroup/blkio type cgroup (rw,nosuid,nodev,noexec,relatime,blkio) cgroup on /sys/fs/cgroup/devices type cgroup (rw,nosuid,nodev,noexec,relatime,devices) cgroup on /sys/fs/cgroup/freezer type cgroup (rw,nosuid,nodev,noexec,relatime,freezer)
cgroup v2では、階層は単一になります。
Unlike v1, cgroup v2 has only single hierarchy.
Control Group v2 / Basic Operations / Mounting
別解。/proc/self/mountinfo
から。
cgroup v2。
$ cat /proc/self/mountinfo | grep cgroup 35 25 0:30 / /sys/fs/cgroup rw,nosuid,nodev,noexec,relatime shared:9 - cgroup2 cgroup2 rw,nsdelegate,memory_recursiveprot
cgroup v1。
$ cat /proc/self/mountinfo | grep cgroup 36 26 0:30 / /sys/fs/cgroup ro,nosuid,nodev,noexec shared:9 - tmpfs tmpfs ro,mode=755 37 36 0:31 / /sys/fs/cgroup/unified rw,nosuid,nodev,noexec,relatime shared:10 - cgroup2 cgroup2 rw,nsdelegate 38 36 0:32 / /sys/fs/cgroup/systemd rw,nosuid,nodev,noexec,relatime shared:11 - cgroup cgroup rw,xattr,name=systemd 41 36 0:35 / /sys/fs/cgroup/hugetlb rw,nosuid,nodev,noexec,relatime shared:15 - cgroup cgroup rw,hugetlb 42 36 0:36 / /sys/fs/cgroup/net_cls,net_prio rw,nosuid,nodev,noexec,relatime shared:16 - cgroup cgroup rw,net_cls,net_prio 43 36 0:37 / /sys/fs/cgroup/memory rw,nosuid,nodev,noexec,relatime shared:17 - cgroup cgroup rw,memory 44 36 0:38 / /sys/fs/cgroup/pids rw,nosuid,nodev,noexec,relatime shared:18 - cgroup cgroup rw,pids 45 36 0:39 / /sys/fs/cgroup/cpu,cpuacct rw,nosuid,nodev,noexec,relatime shared:19 - cgroup cgroup rw,cpu,cpuacct 46 36 0:40 / /sys/fs/cgroup/perf_event rw,nosuid,nodev,noexec,relatime shared:20 - cgroup cgroup rw,perf_event 47 36 0:41 / /sys/fs/cgroup/cpuset rw,nosuid,nodev,noexec,relatime shared:21 - cgroup cgroup rw,cpuset 48 36 0:42 / /sys/fs/cgroup/rdma rw,nosuid,nodev,noexec,relatime shared:22 - cgroup cgroup rw,rdma 49 36 0:43 / /sys/fs/cgroup/blkio rw,nosuid,nodev,noexec,relatime shared:23 - cgroup cgroup rw,blkio 50 36 0:44 / /sys/fs/cgroup/devices rw,nosuid,nodev,noexec,relatime shared:24 - cgroup cgroup rw,devices 51 36 0:45 / /sys/fs/cgroup/freezer rw,nosuid,nodev,noexec,relatime shared:25 - cgroup cgroup rw,freezer
コンテナ内でも見てみましょう。
cgroup v2。
$ docker container run -it --rm ubuntu:22.04 cat /proc/self/mountinfo | grep cgroup 618 617 0:30 / /sys/fs/cgroup ro,nosuid,nodev,noexec,relatime - cgroup2 cgroup rw,nsdelegate,memory_recursiveprot
cgroup v1。
$ docker container run -it --rm ubuntu:20.04 cat /proc/self/mountinfo | grep cgroup 702 701 0:61 / /sys/fs/cgroup rw,nosuid,nodev,noexec,relatime - tmpfs tmpfs rw,mode=755 703 702 0:32 /docker/e71d4f44ee79dd9e2704f4e7131c5db6b5268eb137db847153b29b09c79d04f5 /sys/fs/cgroup/systemd ro,nosuid,nodev,noexec,relatime master:11 - cgroup cgroup rw,xattr,name=systemd 704 702 0:35 /docker/e71d4f44ee79dd9e2704f4e7131c5db6b5268eb137db847153b29b09c79d04f5 /sys/fs/cgroup/hugetlb ro,nosuid,nodev,noexec,relatime master:15 - cgroup cgroup rw,hugetlb 705 702 0:36 /docker/e71d4f44ee79dd9e2704f4e7131c5db6b5268eb137db847153b29b09c79d04f5 /sys/fs/cgroup/net_cls,net_prio ro,nosuid,nodev,noexec,relatime master:16 - cgroup cgroup rw,net_cls,net_prio 706 702 0:37 /docker/e71d4f44ee79dd9e2704f4e7131c5db6b5268eb137db847153b29b09c79d04f5 /sys/fs/cgroup/memory ro,nosuid,nodev,noexec,relatime master:17 - cgroup cgroup rw,memory 707 702 0:38 /docker/e71d4f44ee79dd9e2704f4e7131c5db6b5268eb137db847153b29b09c79d04f5 /sys/fs/cgroup/pids ro,nosuid,nodev,noexec,relatime master:18 - cgroup cgroup rw,pids 708 702 0:39 /docker/e71d4f44ee79dd9e2704f4e7131c5db6b5268eb137db847153b29b09c79d04f5 /sys/fs/cgroup/cpu,cpuacct ro,nosuid,nodev,noexec,relatime master:19 - cgroup cgroup rw,cpu,cpuacct 709 702 0:40 /docker/e71d4f44ee79dd9e2704f4e7131c5db6b5268eb137db847153b29b09c79d04f5 /sys/fs/cgroup/perf_event ro,nosuid,nodev,noexec,relatime master:20 - cgroup cgroup rw,perf_event 710 702 0:41 /docker/e71d4f44ee79dd9e2704f4e7131c5db6b5268eb137db847153b29b09c79d04f5 /sys/fs/cgroup/cpuset ro,nosuid,nodev,noexec,relatime master:21 - cgroup cgroup rw,cpuset 711 702 0:42 /docker/e71d4f44ee79dd9e2704f4e7131c5db6b5268eb137db847153b29b09c79d04f5 /sys/fs/cgroup/rdma ro,nosuid,nodev,noexec,relatime master:22 - cgroup cgroup rw,rdma 712 702 0:43 /docker/e71d4f44ee79dd9e2704f4e7131c5db6b5268eb137db847153b29b09c79d04f5 /sys/fs/cgroup/blkio ro,nosuid,nodev,noexec,relatime master:23 - cgroup cgroup rw,blkio 713 702 0:44 /docker/e71d4f44ee79dd9e2704f4e7131c5db6b5268eb137db847153b29b09c79d04f5 /sys/fs/cgroup/devices ro,nosuid,nodev,noexec,relatime master:24 - cgroup cgroup rw,devices 714 702 0:45 /docker/e71d4f44ee79dd9e2704f4e7131c5db6b5268eb137db847153b29b09c79d04f5 /sys/fs/cgroup/freezer ro,nosuid,nodev,noexec,relatime master:25 - cgroup cgroup rw,freezer
/proc//cgroupで見てみる
最後に自分自身が所属するcgroupの情報を、/proc/self/cgroup
で見てみましょう。
cgroup v2。
$ cat /proc/self/cgroup 0::/user.slice/user-1000.slice/session-4.scope
cgroup v1。
$ cat /proc/self/cgroup 12:freezer:/ 11:devices:/user.slice 10:blkio:/user.slice 9:rdma:/ 8:cpuset:/ 7:perf_event:/ 6:cpu,cpuacct:/user.slice 5:pids:/user.slice/user-1000.slice/session-6.scope 4:memory:/user.slice/user-1000.slice/session-6.scope 3:net_cls,net_prio:/ 2:hugetlb:/ 1:name=systemd:/user.slice/user-1000.slice/session-6.scope 0::/user.slice/user-1000.slice/session-6.scope
プロセスが所属するcgroupは、/proc/<pid>/cgroup
で確認できます。自プロセスの場合は、/proc/self/cgroup
となります。
cgroupの名前空間は、/proc/<pid>/cgroup
ファイルとcgroupファイルシステム(v1の場合はtmpfsファイルシステム)で表現されることが
書かれています。
- Control Group v2 / Namespace
- Control Groups / Usage Examples and Syntax / Mounting hierarchies by name
コンテナ内で確認してみましょう。
cgroup v2。
$ docker container run -it --rm ubuntu:22.04 cat /proc/self/cgroup 0::/
cgroup v1。
$ docker container run -it --rm ubuntu:20.04 cat /proc/self/cgroup 12:freezer:/docker/149c401b108d6be632678c8325ae96dbf9a7aba8c413747b2e1d764c61a128bc 11:devices:/docker/149c401b108d6be632678c8325ae96dbf9a7aba8c413747b2e1d764c61a128bc 10:blkio:/docker/149c401b108d6be632678c8325ae96dbf9a7aba8c413747b2e1d764c61a128bc 9:rdma:/docker/149c401b108d6be632678c8325ae96dbf9a7aba8c413747b2e1d764c61a128bc 8:cpuset:/docker/149c401b108d6be632678c8325ae96dbf9a7aba8c413747b2e1d764c61a128bc 7:perf_event:/docker/149c401b108d6be632678c8325ae96dbf9a7aba8c413747b2e1d764c61a128bc 6:cpu,cpuacct:/docker/149c401b108d6be632678c8325ae96dbf9a7aba8c413747b2e1d764c61a128bc 5:pids:/docker/149c401b108d6be632678c8325ae96dbf9a7aba8c413747b2e1d764c61a128bc 4:memory:/docker/149c401b108d6be632678c8325ae96dbf9a7aba8c413747b2e1d764c61a128bc 3:net_cls,net_prio:/docker/149c401b108d6be632678c8325ae96dbf9a7aba8c413747b2e1d764c61a128bc 2:hugetlb:/docker/149c401b108d6be632678c8325ae96dbf9a7aba8c413747b2e1d764c61a128bc 1:name=systemd:/docker/149c401b108d6be632678c8325ae96dbf9a7aba8c413747b2e1d764c61a128bc 0::/docker/149c401b108d6be632678c8325ae96dbf9a7aba8c413747b2e1d764c61a128bc
cgroup v1の/docker/
の後ろに見えているのは、コンテナのidですね。
余談
cgroup v1の場合、自分自身のコンテナのidは/proc/self/cgroup
や/proc/self/mountinfo
を見ればわかることになります。
ではcgroup v2の場合はというと、/proc/self/mountinfo
のcgroupとしてマウントしている箇所以外を見ればよさそうです。
$ docker container run -it --rm ubuntu:22.04 cat /proc/self/mountinfo | grep docker 588 425 0:46 / / rw,relatime master:223 - overlay overlay rw,lowerdir=/var/lib/docker/overlay2/l/XZFSJBHY5DCBKZT3WS3UVIIMNL:/var/lib/docker/overlay2/l/EJEZWN23ZAMZZ2VSUXSWEOV3OX,upperdir=/var/lib/docker/overlay2/a6049ac9fab5259cdddbeb67d0b36f2f6313f09a07a4b79a70097d00ca4c4ce5/diff,workdir=/var/lib/docker/overlay2/a6049ac9fab5259cdddbeb67d0b36f2f6313f09a07a4b79a70097d00ca4c4ce5/work 621 588 253:0 /var/lib/docker/containers/d2454afd9c7b4c47b726c8f556e6f83b100a464b4f6440fe9f0c92fcb39c61ea/resolv.conf /etc/resolv.conf rw,relatime - ext4 /dev/mapper/ubuntu--vg-ubuntu--lv rw 622 588 253:0 /var/lib/docker/containers/d2454afd9c7b4c47b726c8f556e6f83b100a464b4f6440fe9f0c92fcb39c61ea/hostname /etc/hostname rw,relatime - ext4 /dev/mapper/ubuntu--vg-ubuntu--lv rw 623 588 253:0 /var/lib/docker/containers/d2454afd9c7b4c47b726c8f556e6f83b100a464b4f6440fe9f0c92fcb39c61ea/hosts /etc/hosts rw,relatime - ext4 /dev/mapper/ubuntu--vg-ubuntu--lv rw
こんなところでしょうか。
おわりに
使用しているLinux環境のcgroupが、v1なのかv2なのかを確認する方法をちょっと調べてみました。ちょっと脱線もしていますけど。
あまり明示的に確認する機会はない気がしますが、コンテナに関する周辺知識を見ていける感じなのでたまにこういうのを追ってみると
面白いですね。
/sys/fs/cgroup
や/proc/self/cgroup
、/proc/self/mountinfo
はランタイムやライブラリーのコンテナ対応でも出てくるので、
覚えておくとよいかもしれません。
過去に書いたエントリーは、このあたりです。
JavaがDockerコンテナ内でどのようにCPU数、メモリサイズを取得しているのかを調べてみる - CLOVER🍀
Dockerコンテナ内で動作するNode.jsが認識するCPU数、メモリサイズ(ヒープサイズ除く)は、ホスト側のものになるという話 - CLOVER🍀