Posted at

いまだにsysvinitの手癖が抜けない人のためのsystemd

More than 1 year has passed since last update.

Linuxが起動すると、デーモンプロセスが順次立ち上がるわけですが、これを実際に処理する機能は、init (sysvinit) からUpstartを経てSystemdへと迷走発展してきました。そのため、Systemdへの移行を果たしたはずのUbuntu 16でも、過去のsysvinitの残骸互換性維持のためのファイルが一部残っており、ときどき混乱をもたらします。というわけで、何を今さらという感じではありますが、Systemdを取り上げたいと思います。対象LinuxディストリはUbuntu 16.04LTS!ではレッツゴー。


用語の定義

本稿では、以下の用語を使います。


  • 一時的

    何かを一時的に変更するとは、その変更は直ちに反映されるが、システムを再起動すると元に戻ってしまうということです。


  • 恒久的

    恒久的な変更とは、システムを再起動しても、変更された状態が続くということです。


  • 現在

    特に断らない限り、現在の状態とは、恒久的な状態ではなく一時的な状態を指します。


恒久的な変更操作を行っても、一時的には変更されない場合があります。その場合は、再起動するか、一時的に変更する操作を手動で行う必要があります。


ランレベル

systemdのランレベル相当機能はターゲットです。sysvinitランレベルとsystemdターゲットの対応関係は以下の通りです。

sysvinit ランレベル
systemd ターゲット

0
poweroff

1
rescue

2, 3, 4
multi-user

5
graphical

6
reboot


現在のランレベルを取得する

sysvinitでは、現在のランレベルは/var/run/utmpに書き込まれ、runlevelコマンドで閲覧できました。

systemdでは、ターゲットに到達すると/lib/systemd/systemd-update-utmp runlevelが実行されて/var/run/utmpが書き換わります。これをsysvinit互換のrunlevelコマンドで読み取ります。

これでユーザーからの見え方はほぼsysvinitと同じになりますが、一つ違いがあって、systemdのmulti-userターゲットはランレベル2, 3, 4の区別がありませんので、multi-userは一律ランレベル3と扱われます。そのため、ランレベル2や4に遷移したはずなのに現在のランレベルは3という現象が起こります。

$ sudo init 2

$ runlevel
5 3
$ sudo init 4
$ runlevel
5 3


ランレベルを一時的に変更する

sysvinit互換の/sbin/initコマンドが用意されていますので、これを利用してランレベルを変更できます。


ランレベルを恒久的に変更する

sysvinitでは/etc/inittabを書き換えればよかったのですが、systemdにそんなものはありませんので、systemctlコマンドで書き換えます。例えば以下のコマンドラインで、GUIなしのマルチユーザーモードになります。

$ sudo systemctl set-default multi-user

内部的には、/etc/systemd/system/default.target/lib/systemd/system/multi-user.targetへのシンボリックリンクになります。そんなわけで、systemctlを使わなくてもln -sを駆使して手動でシンボリックリンクを張り替えても同じ効果が得られます。


個別デーモンの起動・停止

systemdは/etc/init.d/の下の起動スクリプトを使いません。/lib/systemd/system//usr/lib/systemd/system//etc/systemd/system/の下にある*.serviceファイルを見て、その設定どおりに起動・停止を行います。


現在の起動状態の取得

service --status-allも一応動きますが、Systemdネイティブにやるならsystemctl list-unitsです。また、全一覧ではなく個別ユニットの詳細を知りたい場合は、systemctl status mysql.serviceとかで出ます。


一時的な起動・停止

Ubutuには昔ながらの起動スクリプトも残っており、/etc/init.d/mysql startとやる方式も、一応使えるには使えます。Systemdネイティブに行きたいなら、systemctl start mysql.serviceとなります。新旧方式を混ぜて使っても一応大丈夫らしく、systemctl start mysql.serviceで起動し、/etc/init.d/mysql stopで停止するといったことも可能です。この場合でも、上述の起動状態は正確な応答が返ってきます。

serviceコマンドは、systemdがあればそれを使い、なければ/etc/init.d/の起動スクリプトを呼ぶようになっています。


デーモン起動・停止の恒久的な設定

sysvinitでは/etc/rc*.d/の下に起動スクリプトへのシンボリックリンクがあり、各デーモンの起動・停止とランレベルの紐づけをそこで行っていました。また、シンボリックリンクの命名によって、デーモンの起動順序を設定できました。

Systemdは、sysvinitのこの仕組みをまるっきり無視します。Systemdを搭載するCentOS7やUbuntu16にも/etc/rc*.dフォルダはありますが、実際のデーモンプロセスの起動・停止を決定するのは、これではありません。Systemdは、例えばmulti-userターゲットなら、/etc/systemd/system/multi-user.target.wants/の下のシンボリックリンクを見ています。このシンボリックリンクはsystemctlコマンドで張ります。

$ sudo systemctl enable mysql.server  # ← 起動する

$ sudo systemctl disable mysql.server # ← 起動しない

Ubuntuに収録されているsysv-rc-confやRed Hat系のchkconfigは既に旧式化しており、Systemd下では実際のデーモンの動作を反映しません。Systemdネイティブに完全移行するしかないようです。


Q&A

Q. なぜUbuntuはさっさとinit.d起動スクリプトを全廃しないのか?

A. 分かりませんが、想像するにSystemdの天下がいつまで続くか分からないからだと思います。Upstartが三日天下で終わってしまったので、今ここでSystemdがないと動かないパッケージを作ってしまうと、後々梯子を外される可能性があります。/etc/init.d起動スクリプトは、スクリプトなだけに、特別なソフトの支援がなくてもshがあれば動きますから、将来Linux界にまた政変が生じても、最低限は動きます。

また、/etc/init.dスクリプトは、渡せる引数がstartとstopとrestartとreloadに限定されません。force-reloadとかgracefulとかstatusとか多彩でフリーダムです。それに対してSystemdでは、Systemdが対応している引数しか渡せないという制約があります。そのため、特殊な起動状態を持つデーモンの場合は、/etc/init.dスクリプトで何とかするしかないという状態が今後も続くかもしれません。

Q. なぜ君は今頃になってこの記事を書こうと思ったのか。

A. Zabbix公式サイトに置いてあるUbuntu版Zabbix ServerとZabbix Agent (3.2.2-1+xenial) は、自分でsystemctl enableしないと自動的には立ち上がりません。Ubuntuの公式ではないパッケージでは、こういうケースがあるようで、要注意だと思いました。

以上!Systemdのユニット定義ファイルの記述は、これまた職人芸的なのですが、本稿ではそこには踏み込みません。参考文献を参照してください。幸運を祈る。


参考文献

Linux女子部 systemd徹底入門

http://www.slideshare.net/enakai/linux-27872553

Red Hat Enterprise Linux 7 システム管理者のガイド

第8章 systemd によるサービス管理

https://access.redhat.com/documentation/ja-JP/Red_Hat_Enterprise_Linux/7/html/System_Administrators_Guide/chap-Managing_Services_with_systemd.html