それマグで!

知識はカップより、マグでゆっくり頂きます。 takuya_1stのブログ

習慣に早くから配慮した者は、 おそらく人生の実りも大きい。

dockerで systemd が動く ubuntu を作って遊ぶ

はじめに

docker ではシングル・プロセスが前提なので、複数のプロセスを起動するべきではない。

それでも、ちょっと動かしてみたいと思うのが、遊びゴコロってやつ。docker で systemd を動かしてみたらどうなるのか。

今回は、Raspi4 の高速マシンがあるから遊んでみようと思います。

前提

docker がインストールされたホストOS。

完全仮想化か物理ホスト上で試す。

仮想化中のdockerなどは権限設定が大変なので考慮しない。 docker in docker だとか、 docker in lxd とかは動作がおかしくなる可能性があるんで、今回は考慮しない。

準備 raspi に docker インストール

素の ubuntu に、 docker をインストール。

最初に、ubuntu for raspberry pi 4 を持ってきて、raspiに ubuntuをしている。この素のubuntu に docker をインストール

snap 経由で docker をインストールする。

sudo snap install docker 

snap のdocker は一般ユーザからコマンドを使えないので、グループを追加して、パーミッションを変えて一般ユーザでもdocker コマンドを扱えるようにする。

自分自身のユーザをdocker グループに追加する。 groupadd と usermod でぱぱっと

sudo groupadd docker
sudo usermod -aG docker $USER

docker の socket ファイルの権限を chown で変えておく。

sudo chown root:docker /var/run/docker.sock
sudo chown "$USER":"$USER" /home/"$USER"/.docker -R
sudo chmod g+rwx "$HOME/.docker" -R

docker サービスを再起動。

sudo reboot 
## または
systemctl restart docker.service

これで準備が完了。

ubnutu(systemd)な-docker を作る 全体の流れ

全体の流れはざっくりいうと、systemd をインストールしてイメージ化、その後に起動コマンドをsystemdの /sbin/init にして作成したイメージを起動する

  • ubuntu を起動する
  • systemd をインストール
  • イメージとして保存
  • イメージを起動(起動コマンドは /sbin/init)

イメージを作成するのがちょっとしたポイントだと思う。

systemd をdockerで使うポイント

docker で ubuntu を systemd で起動する。これをするにはいくつかのポイントがある。

  • docker ubuntu では systemd が含まれてない。
  • systemd を起動するには権限が必要
  • systemd は /sbin/init で起動する
  • docker は起動するプロセスを指定する
  • docker run で起動するプロセスは /sbin/init にする

これらのポイントをちょっとだけ見ておく。

systemd は /sbin/init の代替として /etc/init.d から upstart から 置き換えられたプロセスで PID=1としてすべてのプロセスの祖先になる。

docker は 敢えて /sbin/init を外すことでシステム起動をプロセス単位に分離している。その代わりに docker run で起動するコマンドをしていて起動する。

これらを考慮すると、何らかの形で /sbin/init をインストールしたイメージを作り、docker run で /sbin/init を指定すれば起動するのではないかと思われた。

systemd で起動する ubuntu イメージを作る。

最初にすることは systemd init がインストールされたUbuntu を作りイメージとして保存し、イメージとして取り込む。

#

最初に、ubuntu をdocker で起動して、/sbin/init をインストールしてやる。

docker run --rm  -it  ubuntu:latest

ubuntu は起動コマンドを指定しないと bash が起動する。

起動した ubuntu で、systemd をインストールする

apt update 
apt install init 

この状態で、ログインしたまま起動状態を維持ておく。

別のターミナルを立ち上げて、起動中のdocker インスタンスをイメージとして取り出す。

別ターミナルからイメージ作成

docker 起動中のコンテナIDを調べて、tar ball ファイルとして取り出す。

docker container export bcdfc201cd09 > ubuntu-init-installed.tar

イメージが作成できたら、initをインストールしたインスタンスは不要なのでbashをCtrl-Dで終了しインスタンスを消しておく。

イメージを取り込む。

イメージが作成できたら、作成したファイルをdocker image として取り込む tar のファイルを イメージとしてインポートする。

docker image import ubuntu-init-installed.tar  takuya/ubuntu-init-installed:latest

takuya/ubuntu-init-installed:latest は イメージの名前とTAG を指定してる。イメージ名がtakuya/ubuntu-init-installedで、タグがlatest である。

systemd を指定して、docker イメージを起動

インポートしたイメージを起動する。

docker は指定したプロセスを起動するのので、systemd 経由で起動するように、/sbin/init を指定して起動する。

ただし、/sbin/init はインタラクティブシェルを提供しないので、 デタッチで起動する。

また、ここで 特権コンテナ--privilegedを指定する 。特権をコンテナでないとsystemd は動いてくれない。

docker run --privileged --rm -d takuya/ubuntu-init-installed /sbin/init

起動の確認

特権をコンテナで無事に起動すると dbus などが使えるので通常通りのsystemdなubuntu とほぼ同じ状態で起動する。

ちゃんと/sbin/init がPID=1で起動しているのがわかる。

 docker exec -it 356f77ee1bb1 ps auxf
USER         PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root          51  0.0  0.0   5468  2452 pts/0    Rs+  03:29   0:00 ps auxf
root           1  3.6  0.1 165800  9244 ?        Ss   03:29   0:00 /sbin/init
root          21  1.4  0.1  34648 10488 ?        S<s  03:29   0:00 /lib/systemd/
systemd+      33  0.9  0.0  20964  6240 ?        Ss   03:29   0:00 /lib/systemd/
systemd+      34  0.9  0.0  89940  3704 ?        Ssl  03:29   0:00 /lib/systemd/
message+      37  0.1  0.0   7948  3596 ?        Ss   03:29   0:00 /usr/bin/dbus
root          39  1.6  0.2  26124 16820 ?        Ss   03:29   0:00 /usr/bin/pyth
root          41  0.6  0.0  16028  6124 ?        Ss   03:29   0:00 /lib/systemd/
root          45  0.0  0.0   2344  1484 ?        Ss   03:29   0:00 /sbin/agetty

あっさりと起動した。ずいぶんと拍子抜けである。 docker ではマルチなプロセスを管理するべきではないと言われて久しいが、動くものは動くのであるな。

nginx / ssh が有効になったイメージを作ってみる。

nginx や ssh が有効になったイメージを作ってみる。

systemd がインストールされた ubuntu を作れたので nginx / ssh をインストールして 起動と同時に ssh と nginx が起動するイメージの作成を試みる。

systemdなubuntuを起動して

docker run --rm --privileged -d takuya/ubuntu-init-installed  /sbin/init
docker exec -it 8f72eab2c5be  apt install openssh-server nginx

イメージとして取り出してインポート

docker export 8f72eab2c5be > ubuntu-init-installed-with-ssh-nginx.tar
docker image import ubuntu-init-installed-with-ssh-nginx.tar  takuya/ubuntu-init-installed:ssh
docker run --rm --privileged  -d takuya/ubuntu-init-installed:ssh  /sbin/init

チェックすると。問題なく動きますね。

 docker exec a2ef6147fdc systemctl status nginx
● nginx.service - A high performance web server and a reverse proxy server
     Loaded: loaded (/lib/systemd/system/nginx.service; enabled; vendor preset: enabled)
     Active: active (running) since Tue 2021-05-25 03:54:03 JST; 22s ago
       Docs: man:nginx(8)
    Process: 40 ExecStartPre=/usr/sbin/nginx -t -q -g daemon on; master_process on; (code=exited, status=0/SUCCESS)
    Process: 48 ExecStart=/usr/sbin/nginx -g daemon on; master_process on; (code=exited, status=0/SUCCESS)
   Main PID: 52 (nginx)
      Tasks: 5 (limit: 9257)
     CGroup: /docker/a2ef6147fdcc209a31cd1774234b461c20a85c9540e29b910089e5a44935fe20/system.slice/nginx.service
             ├─52 nginx: master process /usr/sbin/nginx -g daemon on; master_process on;
             ├─53 nginx: worker process
             ├─54 nginx: worker process
             ├─55 nginx: worker process
             └─56 nginx: worker process

May 25 03:54:03 a2ef6147fdcc systemd[1]: Starting A high performance web server and a reverse proxy server...
May 25 03:54:03 a2ef6147fdcc systemd[1]: Started A high performance web server and a reverse proxy server.

あっさり動いてしまうんですね。

特権コンテナに注意。

特権コンテナは docker in docker のような特殊な用途であったりする目的のためにある。

あれこれ細かいところは省きますが、特権コンテナを使うので公開サーバーに利用は控えること。

用途

docker で apt を試したいときや、仮想マシンを起動するほどもでもない開発環境で export してぱぱっと渡したいとき。 初心者が多い開発チームなど、トラブルを回避するために一時的に export して import させてぱぱっと開発環境を再現させたり。とか便利そう。

ちょっと開発環境でインストールを試したいときとかそういうときにも便利そうですね。

dockerfile

動作の仕組みさえわかれば、dockerfile で systemd を書くことも不可能ではなさそう。私は開発環境でちょっと試したいとかなのでdocker export で満足しているので試さなかった。

まとめ

/sbin/init をインストールした状態で privileged で起動すると動かせる。

raspi4 8GB はマジ高性能なので実験環境に最適だった。

ただし、arm(aarch64)なraspiからx86_64 なamdは相互にexport/import出来ないはずなのでそのへんはちょっと注意。

特権コンテナには十分に注意しないといけない。特権コンテナは通常の物理ホストと何ら変わりなくdocker の仕組み上プロセスやファイルが分離されているようなことはない わかりやすいトラブルとして、 このコンテナが乗っ取られたときに、 docker 内部 docker をインストールされてしまい、docker run -v /:/real-hostと内部でマウントされてしまうと目が当てられない。

参考資料