🐥

Docker の中でなにかを動かすのには必ずしも systemctl/systemdは要らない。

2021/09/06に公開

qiita を検索すると「Dockerの中でsystemctlが使えない」という文脈を散見する。
しかしこれはDocker本来のコンセプトからすれば誤用である。

誤用ではあるが Docker と systemctl は広義では無関係ではない。
しかしそれこそが誤解の要因と思われるため、まずはそれを整理しておきたい。

まずDockerデーモンの話

Docker本家では明確に呼び分けてるようだが、dockerデーモンとは「docker cliを動かすための実行環境」である。これをホストOSのサービスとして起動する必要がある。

比較的新しいlinux系ホストOSでは、systemctlを使うのが一般的であり、オフィシャルガイドもそうなっている。

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

なので、linux 入門者の第多数は「linuxではsystemctl経由でサービスを起動しなければならない」という先入観を持たれてるようだ。

systemctl on Docker ....?

故に qiitaには、dockerの中でsystemctlを使う記事を散見するが、本質的には誤用である。

https://qiita.com/search?q=docker+systemctl

docker自身には、docker start/runの時に特定のプログラムを一つだけ起動する機能がもともとあり、それを使って、1コンテナと1サービスを対にするのが正しい運用である。

このことはdocker関連のドキュメントで度々語られている。例えばredhat。

Docker とは - 解説、メリット、できること | Red Hat

これまでの Linux コンテナでは、複数のプロセスを管理できる init システムが使用されていました。つまり、アプリケーション全体が 1 つになって動作します。Docker テクノロジーでは、アプリケーションを個別のプロセスに分割することが推奨され、そのためのツールも提供されます。この細分化アプローチにはメリットがあります。

これは恐らく日本語訳なので、似たようなことはオフィシャルサイトにも書いてある。はずだ。多分この辺り....

What is a Container? | App Containerization | Docker

Containers are an abstraction at the app layer that packages code and dependencies together. Multiple containers can run on the same machine and share the OS kernel with other containers, each running as isolated processes in user space. Containers take up less space than VMs (container images are typically tens of MBs in size), can handle more applications and require fewer VMs and Operating systems.

なお、dockerは仮想環境の一種ではあるが、VMではない。
このため、各種のVMよりパフォーマンスに有利なのは概ねこれが理由だ。

systemctl/systemdはサービスマネージャである。

前述のとおり、docker愛好家の間では、systemctl==プロセス管理ツールとして親しまれてるようだが、これは誤解である。
systemctlは、特権プロセスであり、systemdサービスマネージャの一部である。大雑把にいえばOSの根幹であるとも言える。

みなさんの大多数は、windowsを使っていることだろう。「Windowsサービスマネージャ」を開いたことはないだろうか?
わけのわからないプログラムがいっぱい並んでて停めたり再起動したりできる。
ああいうことをするのがサービスマネージャだ。

サービスマネージャとはなにか。

linux — 「init」と「service manager」の違いは何ですか?

initは(通常)システムによって開始される最初のプロセスです。これには、次のような特別な責任がいくつかあります(ただし、これらに限定されません)。

一方、サービスマネージャーは、特定のサービスセットが実行されていることを確認する責任を単独で負い、オプションでそれらのサービスが実行され続けることを保証します。これに対する正確なアプローチは、サービス間の依存関係を追跡するだけの基本的なスクリプトから、依存関係を自動的に管理する複雑なシステムまでさまざまです。

色々ややこしいことが書いてあるが、端的言えば、複数のサービスを適切な順序で、起動および停止させるのが主目的。

例えば、よくあるWEBサービスの場合、フロント(apache+mod_php等)より先にmysqlを落したらデータを書き込めなくなる。
逆もまた真なり。 WEBフロントが立ち上がっててもmysqlが動いてなければ動かないし、リカバリも非常に手間である。

であれば、最初から都合の良い順に起動してほしいと考えるのは摂理だろう。

systemd自身についてもほぼ同様であるが、詳細に書かれてる資料は、archlinux wikiであろう。

systemd - ArchWiki

systemd は Linux 環境の基本構成スイートであり、SysV や LSB init スクリプトと互換性のある、Linux 用のシステム・サービスマネージャです。systemd はサービスの起動を積極的に並行化します。また、ソケットや D-Bus のアクティベーションを使用してサービスを起動し、必要なデーモンの開始を行うことができ、Linux の cgroups によるプロセス管理ができます。システム状態のスナップショット作成と復元、(自動) マウントポイントの管理、煩雑な依存関係に基づいたサービスのコントロールを処理します。

随分普及したlinuxであるが、未だにサーバとしての側面が強く、複数のサービスを起動する想定がもともとある。それらを適切な順序で起動するためのサービスマネージャであり、歴史的には様々なプログラムが存在する。最新ではどうやらそれがsystemdということのようだ。

最も古典のサービスマネージャはsysvinitである。fedoraには、systemd/sysvinitの比較が乗っている。

https://fedoraproject.org/wiki/SysVinit_to_Systemd_Cheatsheet

誤用ではあるが、起動はできる。 apache httpdの場合

qiitaの彼らがやってるように起動することはもちろんできる。

2023年現在、centosはメンテナンスを停止しており、パッケージリポジトリも移動してるため、この通りに起動しないので注意されたし。

$ Docker pull centos:latest
$ Docker run --privileged -it centos /bin/bash
XXXXXX# yum install -y httpd
XXXXXX# systemctl start httpd

起動して、ホストOSからプロセス群を眺めると次のようになってるはずである。小生のarchlinux ホストの場合を示す。

 170608 ?        Sl     0:00 /usr/bin/containerd-shim-runc-v2 ***
 170630 ?        Ss     0:00  \_ /sbin/init
 170672 ?        Ss     0:00      \_ /usr/lib/systemd/systemd-journald
 170685 ?        Ss     0:00      \_ /usr/lib/systemd/systemd-udevd
 170754 ?        Ss     0:00      \_ /usr/sbin/httpd -DFOREGROUND
 170831 ?        S      0:00      |   \_ /usr/sbin/httpd -DFOREGROUND
 170832 ?        Sl     0:00      |   \_ /usr/sbin/httpd -DFOREGROUND
 170834 ?        Sl     0:00      |   \_ /usr/sbin/httpd -DFOREGROUND
 170835 ?        Sl     0:00      |   \_ /usr/sbin/httpd -DFOREGROUND
 170758 ?        Ss     0:00      \_ /usr/bin/dbus-daemon --system --address=systemd: --nofork --nopidfile --systemd-activation --syslo

httpdは/sbin/initの子プロセスとしてぶらさがっており、同時にsystemd-* も起動しているのがわかるだろう。

これらの中で、 init および systemd-* dbus-daemon が今回の肝だ。前述の通り、彼らは特権プロセスであり privilegedが必要なのはこいつらだ。(これらが悪いわけではないが、本質的な権限要求とは言い難い)

--privileges と capabilities

archlinuxのwikiページの解説の引用で、dbusについて言及してることに注目してほしい。カーネル特権を必要とするのはこれだ。

初心者は混乱するところだと思われるが、ここでいうカーネル特権とは、root実行のことではない。

そもそもDockerはrootでの実行をエミュレーションしてるため、多くのroot想定のサービスを起動できる。

特権とは、もっとlinuxカーネルについての専門的な操作を意味している。
しかもDockerの場合、カーネル操作は、ホストOSのカーネルに影響してしまう。

Dockerドキュメントのケーパビリティの項を参照されたい。

https://docs.docker.jp/engine/reference/run.html#runtime-privilege-linux-capabilities

大抵のひとには、何のこっちゃわからない話が並んでるのではないだろうか?

Dockerとして、コマンドラインにprivileged を指定させるようになってるのは「ゲストOS上でカーネル特権を使うのは危険だよ。もちろんわかってるやってるんだよね?」という確認のためだ。

しかし今一度問う。たいていのひとにはケーパビリティはなんのことかわからなかったはずだ。
何のこっちゃわからないのに、特権を与えてしまって良いのだろうか?

基本的に apache httpd の実行にはカーネル特権は必須ではない。 それがmysql/mariadbであったとしても同様である。
また、apache httpd上で動かす、俗にいうWebアプリケーションのほとんども、カーネル操作はしないはずだ。

にもかかわらず、systemctlの使用のためにprivilegedを使うのは誤用といわざるをえない。

肝心のhttpdはinitの下にぶらさがっている。-DFOREGROUNDというパラメタに注目。次に出てくる。

dbus-daemon について

https://ja.wikipedia.org/wiki/D-Bus

dbus-daemon について調べると、「メッセージングシステムです」という解説が見当たると思われる。
すごく大事そうに見えるので、起動しなきゃならないような気がするかもしれないが、少なくとも apache httpd では dbus を使うことはない。

D-Busを使ってるのは、本稿で言及している範囲であれば、systemctlだけである。つまりsystemctlを使わないなら dbus も不要である。

dockerから直接単一のプログラム起動をする

章題の通りではあるが。次のDockerfileを作成してbuild/runする。

FROM centos:latest
RUN yum install -y httpd
CMD /usr/sbin/httpd -DFOREGROUND

2023年現在、centosはメンテナンスを停止しており、パッケージリポジトリも移動してるため、この通りに起動しないので注意されたし。

CMDは、Docker run/startの起動時に、イメージ上の起動プログラムを設定するものだ。

dockerはこのCMDに指定したプログラムが稼働してるのか常に監視しており、特に指定がなければプログラムの終了とともに、dockerコンテナを停止するようになってる。(これはデフォルト動作で、再起動してもらうこともできる)

-DFOREGROUNDが、httpdをうまく起動させるためのお呪いであり、前述ではsystemdが変わりにやってくれていたものだ。

この方法にはprivilegedはいらない。

   4669 ?        Sl     0:00 /usr/bin/containerd-shim-runc-v2 *******
   4691 ?        Ss     0:00  \_ /usr/sbin/httpd -DFOREGROUND
   4720 ?        S      0:00      \_ /usr/sbin/httpd -DFOREGROUND
   4721 ?        Sl     0:00      \_ /usr/sbin/httpd -DFOREGROUND
   4722 ?        Sl     0:00      \_ /usr/sbin/httpd -DFOREGROUND
   4723 ?        Sl     0:00      \_ /usr/sbin/httpd -DFOREGROUND

init, systemd-* dbus-daemon-* を伴わずとも起動できてるのがわかるだろう。

....ではあるが、httpdならオフィシャル Docker httpd のイメージを使ったほうが早い

centosゲストにこだわらないのであれば、オフィシャルイメージを使ったほうが遥かにはやい。

https://hub.Docker.com/_/httpd/

各バージョンをクリックすると、githubのDockerfileにたどり着ける。研究させてもらうと良いだろう。内容はほぼオーソドックスなソースビルド手順ではあるが。

レンタルサーバの練習?

amazon EC2等のレンタルサーバの操作練習にdockerを使う場合も見受けられるが、これもやや誤用である。

  • Webアプリケーションのデプロイの練習に使うならアリ。これは小生もたまに使っている。
  • だが、各種ミドルウエア(mysql,apache,等々)設定の練習に使うならやや難あり。VMware、Virtulbox等の、本物のVMを使ったほうがよい。

Discussion