はじめに
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
と内部でマウントされてしまうと目が当てられない。
参考資料