Docker Desktop 4.3.0以降できなくなったCentOS7コンテナでのsystemdの利用をlima+dockerで使える環境を用意した

前回記事でlimaのセットアップをしたので、これを利用してlimaインスタンス上でCentOS7コンテナのsystemdを利用することにした。

なんでlima+dockerでやるの?

自分は今までDocker Desktopを使っており、CentOS7コンテナのsystemdは普通に利用してきた(--privilegedつけたり/sbin/initでdocker runしたりするあれ)。 しかし、 Docker Desktop 4.3.0以降はこれができなくなった

Docker Desktop for Mac release notes | Docker Documentation

  • Docker Desktop now uses cgroupv2. If you need to run systemd in a container then:
    • Ensure your version of systemd supports cgroupv2. It must be at least systemd 247. Consider upgrading any centos:7 images to centos:8.
    • Containers running systemd need the following options: --privileged --cgroupns=host -v /sys/fs/cgroup:/sys/fs/cgroup:rw.

Docker Desktopでcgroup v2を使うようにした関係で、cgroup v1を使っているとsystemdを実行することができなくなっている。 CentOS7は cgroup v1なのでこれに引っかかって動かない。 上記でも「cgroup v2をサポートしているsystemd 247以上を使う必要あり。centos:7はcentos:8などcgroup v2が使えるOSイメージへ移行して--privileged --cgroupns=host -v /sys/fs/cgroup:/sys/fs/cgroup:rwをつけてコンテナを起動すること。」と書かれている。

自分の場合、CentOS7イメージでsystemdを起動できるコンテナを使う場面がまだ必要で、Docker Desktop以外で利用する手段がほしいところ。 というわけで前回記事でlimaを使えるようにしたことを活かして、cgroup v1が使えるlimaインスタンスを構築してCentOS7コンテナのsystemdを使えるようにした。

先にMacOS環境にdocker clientをインストールしておく

Docker Desktopはアンインストールして、docker clientをインストールしておく。

ref : Install Docker Engine from binaries | Docker Documentation

簡単にlimaインスタンスを構築できるyamlファイルを用意した

やったことをいろいろ説明する前に成果物を見せちゃおう。 limactl startインスタンスを作成するときにいい感じにprovisionしてくれるyamlを作成した。 私のリポジトリで公開しているのでどうぞ。

github.com

yamlファイルを手元にもってきてlimactl startのときに指定するだけでインスタンスが立ち上がり、dockerデーモンのセットアップもしてくれる。

$ limactl start /path/to/docker-vm.yaml

あとは環境変数DOCKER_HOSTを設定してcentos7のコンテナをdocker runするだけ。 これでsystemdを利用できる。

$ export DOCKER_HOST='tcp://127.0.0.1:2375'

$ docker run -itd --privileged --name centos7 centos:7 /sbin/init
cadb0f349dc55d7d398b51398f3a411d8e94fcadf8b4499b9d2fb4050d2602d1

$ docker exec -it centos7 systemctl status
● cadb0f349dc5
    State: starting
     Jobs: 6 queued
   Failed: 0 units
    Since: Tue 2022-01-18 02:56:21 UTC; 10s ago
   CGroup: /docker/cadb0f349dc55d7d398b51398f3a411d8e94fcadf8b4499b9d2fb4050d260
2d1
           ├─1 /sbin/init
           └─system.slice
             ├─systemd-udevd.service
             │ └─32 /usr/lib/systemd/systemd-udevd
             ├─systemd-journald.service
             │ └─21 /usr/lib/systemd/systemd-journald
             ├─dbus.service
             │ └─60 /usr/bin/dbus-daemon --system --address=systemd: --nofork --nop
idfile --systemd-activation
             ├─system-getty.slice
             │ └─getty@tty1.service
             │   └─69 (agetty)
             └─systemd-logind.service
               └─65 /usr/lib/systemd/systemd-logind

作成したyamlファイルのポイント

limaではlimactl startするだけでインスタンスが起動してくれるが、デフォルトの設定をそのまま使っても求める環境は構築できない。 以下ポイントを2点ほどまとめた。

ポイント1:limaインスタンスのOSはRocky Linux 8.5を採用

デフォルトで起動するインスタンスUbuntu 21.10だが、cgroup v2が使われているので今回のケースだとNG。

<user>@lima-default:~$ grep PRETTY_NAME /etc/os-release
PRETTY_NAME="Ubuntu 21.10"
<user>@lima-default:~$
<user>@lima-default:~$ journalctl --version
systemd 248 (248.3-1ubuntu8.2)
+PAM +AUDIT +SELINUX +APPARMOR +IMA +SMACK +SECCOMP +GCRYPT +GNUTLS -OPENSSL +ACL +BLKID +CURL +ELFUTILS -FIDO2 +IDN2 -IDN +IPTC +KMOD +LIBCRYPTSETUP -LIBFDISK +PCRE2 -PWQUALITY -P11KIT -QRENCODE +BZIP2 +LZ4 +XZ +ZLIB +ZSTD -XKBCOMMON +UTMP +SYSVINIT default-hierarchy=unified
<user>@lima-default:~$
<user>@lima-default:~$
<user>@lima-default:~$ mount | grep cgroup
cgroup2 on /sys/fs/cgroup type cgroup2 (rw,nosuid,nodev,noexec,relatime,nsdelegate,memory_recursiveprot)
<user>@lima-default:~$

docker clientのinfoからも確認できる。

$ docker info | grep -E "Cgroup Driver:|Cgroup Version:|Operating System:"
 Cgroup Driver: systemd
 Cgroup Version: 2
 Operating System: Ubuntu 21.10

Rocky Linux 8.5はcgroup v1なのでOK。 limaのリポジトリRocky Linuxのサンプルyamlが公開されていたのでこれを利用することにした。 cgroup v1が使われているOSならRocky Linuxじゃなくてもいけるはず。

[<user>@lima-docker-vm ~]$ cat /etc/redhat-release
Rocky Linux release 8.5 (Green Obsidian)
[<user>@lima-docker-vm ~]$
[<user>@lima-docker-vm ~]$ journalctl --version
systemd 239 (239-51.el8)
+PAM +AUDIT +SELINUX +IMA -APPARMOR +SMACK +SYSVINIT +UTMP +LIBCRYPTSETUP +GCRYPT +GNUTLS +ACL +XZ +LZ4 +SECCOMP +BLKID +ELFUTILS +KMOD +IDN2 -IDN +PCRE2 default-hierarchy=legacy
[<user>@lima-docker-vm ~]$
[<user>@lima-docker-vm ~]$
[root@base000 current]# mount | grep cgroup
tmpfs on /sys/fs/cgroup type tmpfs (ro,nosuid,nodev,noexec,seclabel,mode=755)
cgroup on /sys/fs/cgroup/systemd type cgroup (rw,nosuid,nodev,noexec,relatime,seclabel,xattr,release_agent=/usr/lib/systemd/systemd-cgroups-agent,name=systemd)

...

[<user>@lima-docker-vm ~]$

docker clientのinfoからも確認できる。

$ docker info | grep -E "Cgroup Driver:|Cgroup Version:|Operating System:"
 Cgroup Driver: cgroupfs
 Cgroup Version: 1
 Operating System: Rocky Linux 8.5 (Green Obsidian)

上記を踏まえてRocky Linux 8.5を採用した。 yamlの該当部分はこんなかんじ。 まずはローカルにOSイメージが無いかを探して、なければ指定されたURLからOSイメージをダウンロードするようになっている。

images:
# Try to use a local image first.
- location: "~/Downloads/Rocky-8-GenericCloud-8.5-20211114.2.x86_64.qcow2"
  arch: "x86_64"
- location: "~/Downloads/Rocky-8-GenericCloud-8.5.20211114.1.aarch64.qcow2"
  arch: "aarch64"


# Download the file from the internet when the local file is missing.
# Hint: run `limactl prune` to invalidate the "current" cache
- location: "https://dl.rockylinux.org/pub/rocky/8.5/images/Rocky-8-GenericCloud-8.5-20211114.2.x86_64.qcow2"
  arch: "x86_64"
  digest: "sha256:c23f58f26f73fb9ae92bfb4cf881993c23fdce1bbcfd2881a5831f90373ce0c8"
- location: "https://dl.rockylinux.org/pub/rocky/8.5/images/Rocky-8-GenericCloud-8.5.20211114.1.aarch64.qcow2"
  arch: "aarch64"
  digest: "sha256:f13cfa7b5e449cc165181a1efbea5b1cdce73ef6a5d6bb24c22b50f67f1f8fe2"

ポイント2:provisionを活用する

インスタンスただ作成するだけではMacOSからlimaインスタンス上のdockerデーモンは利用できない。 というかデフォルトだとlimaインスタンスにdockerは入ってない。 さらにdockerのデーモンはlimaで構築したインスタンス上で動いているので、ただインストールするだけではMacOS側のdocker clientを実行してもエラーがでる。 docker clientからlima上のdockerデーモンを利用できるように更に設定を追加する必要がある。

ref : https://docs.docker.jp/config/daemon/daemon.html

インスタンスを構築した後にいちいち設定するのは面倒なのでprovisionを利用した。

provision:
# `system` is executed with the root privilege
  - mode: system
    script: |
      #!/bin/bash
      yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
      yum install -y docker-ce docker-ce-cli containerd.io
      mkdir -p /etc/systemd/system/docker.service.d/
      cat <<EOF > /etc/systemd/system/docker.service.d/docker.conf
      [Service]
      ExecStart=
      ExecStart=/usr/bin/dockerd -H tcp://127.0.0.1:2375 -H unix:///var/run/docker.sock
      EOF
      systemctl daemon-reload
      systemctl start docker