コンテナ内でもcronとかをsystemctlを使って起動したい!
でも普通にコンテナイメージを作るとsystemctlがエラーが出て動かない!
(あとなんとなくCentOS Stream 9を使ってみたい!)
困った!
という人用の記事です。
※今回の内容には注意点もありますので、よく読んだ上でご利用は自己責任でお願いいたします。
結論
【注意点】
以下の手順のdocker run時に --privilegedオプションをつけていますが、
これは危険性のあるオプション です。
このオプションはドキュメントでは、 「潜在的な危険のある操作はデフォルトではできないようになっているが、--privilegedオプションによってそれが可能となる」 というようなことが書かれています。
したがってこの記事の内容を利用する際は、利用ケースをよく考えて使っていただければと思います。
【systemctlの使えるイメージの作り方、コンテナ起動の仕方】
1. 以下のDockerfileを用意して、
FROM quay.io/centos/centos:stream9
ENV container docker
RUN dnf update && \
dnf -y install cronie && \
dnf -y install systemd && \
dnf clean all && \
systemctl enable crond.service
CMD ["/usr/sbin/init"]
2. コンテナイメージをビルドして
docker build -t local/cs9-systemd:0.5 .
3. コンテナをバックグラウンドで起動して
docker run --privileged --name cs9-systemd -v /sys/fs/cgroup:/sys/fs/cgroup:ro -d local/cs9-systemd:0.5
4. あとはコンテナ内でbashを起動して手元のターミナルとつなげる
docker exec -it cs9-systemd /bin/bash
上記1~4の手順を実行すると、コンテナ内でsystemctlが使えます。
上記の例ではコンテナ起動時にcronのデーモンを動かす設定をしており、
crontab -e
でジョブを設定してあげると自動実行されることが確認できると思います。
追加でなにかデーモンを起動したい場合は、通常のCentOSのようにコンテナ内でsystemctl start
を使うことでできます。
試しにsystemctl list-unitsを使ったところ。ちゃんと動いている。 |
---|
解説
そもそもなぜsystemctlが動かないのか?
それはsystemdはPIDが1のときでないとエラーを返して起動しないようになっているから。
これはsystemdやsystemctlを実行したときのエラー
System has not been booted with systemd as init system (PID 1). Can't operate.
にもそう書いてありますね。
PIDはプロセスのIDのことであり、PID1は一番最初に起動したプロセスに割り当てられるようです。
普通にlinuxを起動した際はsystemdが一番最初に起動するプロセスになるのですが、
dockerの場合はdocker runをした際に指定されたプログラムがPID1になります。
例えばdocker run [コンテナ識別子] /bin/bash
とした際は、bashがPID1として起動します。
また、プログラムの指定を省略した際は少々動きが特殊で、
Dockerfileに書かれたCMDの内容があればその内容、
なければ/bin/bash
がデフォルトとして指定されるようです。
ともかくこのdocker独特の挙動によってsystemdが動かないわけですね。
対策
今回の場合はdocker runを実行したときに/usr/sbin/initが実行されるようにする!
/usr/sbin/initの中でsystemdが起動され、systemdのPIDが1になるようです。
上記Dockerfile内のCMD ["/usr/sbin/init"]
がそのための記述ですね。
上記の手順3docker run
ではコンテナ起動時に動かす(PID1になる)プログラムを指定していないので、
CMDに書いた/usr/sbin/init
が実行されます。
なお、CMDに書くべきものはディストリビューションによって異なるようです。
その他Dockerfileの解説
dnf install -y cronie
: cronieという名前のcronのパッケージをインストール
dnf -y install systemd
: systemdのパッケージをインストール(実際には一緒にインストールされるsystemctlのコマンドを使います)
dnf clean all
: パッケージインストール時にできたキャッシュ情報を削除
systemctl enable crond.service
: systemctlの実行テストとして起動時にcronのデーモンを起動するようにお試しで設定
まとめ
(問題点がないわけではありませんが)コンテナ内でも一応systemctlを動かすことができました。
ここまで読んでいただきありがとうございました。
参考
- Centos - Official Image | Docker Hub:https://hub.docker.com/_/centos/
- 旧CentOSのDockerhub
- Example systemd enabled app containerの項目を参考にした
- Official CentOS systemd docker container:https://hub.docker.com/r/centos/systemd/
- systemdが使える旧CentOSのコンテナイメージの実物がある
- CentOS : https://quay.io/repository/centos/centos?tab=tags
- CentOSの後継、CentOS Streamのコンテナイメージのレポジトリ