はじめに
Linuxのサービスやデーモンを管理する仕組みであるsystemdについて勉強したので、そのまとめです。
systemdはRHEL7から採用されていて、RHEL8でもそこは同じです。
それ以前はSysVinit(init)/Upstartと呼ばれる仕組みが使われていましたが、そこは本記事では触れません。
基本的にRedHat公式ドキュメント「第3章 SYSTEMD によるサービス管理」に沿っています。
環境
Shimer-System: vagrantでrhel8を試してみるを参考に、vagrant&virtualboxでrhel8を起動しました。
起動処理
Linuxの起動はざっくりと以下の4段階によって行われます。
電源投入によりBIOSが起動する。
BIOSからブートローダーが呼び出される。
ブートローダーがLinuxカーネルを起動する。
Linuxカーネルがinitプロセス(PID 1)を起動する。
出典:Developers.IO by Classmethod: systemd超入門
上記のinitプロセスが従来、/sbin/init
だったのが、systemdでは/usr/bin/systemd
となる。
[vagrant@rhel8 ~]$ ps u -p 1
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.2 0.7 179556 13924 ? Ss 07:23 0:01 /usr/lib/systemd/systemd --switched-root --system --deserialize 18
Unit
systemdでは、「Unit」という単位で処理を管理します。これまで、rc.sysinitやサービス起動スクリプトが実施していた処理の内容は、すべて、Unitとして定義されます。
出典:Systemd入門(1) - Unitの概念を理解する
RedHatのページに書いてありますが、ユニット設定ファイルは以下のディレクトリにあります。下に行くほど設定内容は優先されます。
ディレクトリ | 説明 |
---|---|
/usr/lib/systemd/system/ | インストール済みのRPMパッケージで配布された設定ファイル |
/run/systemd/system/ | ランタイム時に自動作成されたsystemd ユニットファイル |
/etc/systemd/system/ | systemctl enableで作成された systemd ユニットファイル、およびサービス拡張向けに追加されたユニットファイル |
ユニットにはタイプ(種類)があり、主なものは以下になります。
タイプ | 説明 | 備考 |
---|---|---|
service | システムサービス | 主にデーモンの起動・管理 |
target | ユニットをグループ化するためのもの | |
mount | ファイルシステムのマウントポイント | /etc/fstabから自動作成される |
automount | ファイルシステムの自動マウントポイント | |
device | デバイスファイル | udevがデバイスを認識すると 自動作成される |
socket | プロセス間の通信ソケット | systemdがsocketをListenして、接続があると指定のデーモンを起動してプロセスに受け渡す |
path | ファイルシステム内のファイルまたはディレクトリー | 指定のファイルが作成されると、指定されたサービスを起動する |
デフォルト設定
systemdのデフォルト設定は/etc/systemd/system.conf
で確認で居ます。
このデフォルト設定はコンパイル中に定義されたものであり、このファイルを編集することでデフォルト値を上書きできます。
このファイルの中にも以下のように書かれています。
Entries in this file show the compile time defaults.
You can change settings by editing this file.
Defaults can be restored by simply deleting this file.
サービスの管理
SysVinit/Upstartでは、/etc/rc.d/init.d/
ディレクトリーにあるinitスクリプト(Bashのスクリプト)がサービスの起動・管理で利用されていましたが、systemdではサービスユニットの設定ファイル(xxx.service)に置き換わります。
起動・停止系
サービスの起動・停止・再起動・実行中のみ再起動・設定再読み込み・状態確認は、それぞれ以下の通りsystemctl
コマンドで実行します。サービスに限っては、ユニットファイルの.service
は省略できます。
systemctl start|stop|restart|try-restart|reload|status(is-active) xxx.service(サービスユニットファイル)
全てのサービスの状態確認は以下のコマンドで確認できます。
systemctl list-units --type service --all
systemctl statusの表示内容
[vagrant@rhel8 ~]$ systemctl status chronyd.service
● chronyd.service - NTP client/server
Loaded: loaded (/usr/lib/systemd/system/chronyd.service; enabled; vendor preset: enabled) ←ユニット設定ファイルが読み込まれているか、そのファイルパス、有効かどうか
Active: active (running) since Sun 2021-01-31 07:24:00 UTC; 2h 7min ago ←起動状態、タイムスタンプ
Docs: man:chronyd(8)
man:chrony.conf(5)
Main PID: 839 (chronyd) ←サービスのPIDとその名前
Tasks: 1 (limit: 11394)
Memory: 2.4M
CGroup: /system.slice/chronyd.service ←コントロールグループ (cgroup) の情報
└─839 /usr/sbin/chronyd
有効化・無効化系(自動起動設定)
サービスの有効化(自動起動設定)・無効化・設定確認は、それぞれ以下のコマンドで実行します。
systemctl enable|disable|status(is-enabled) xxx.service(サービスユニットファイル)
全てのサービス設定確認・サービスの開始タイミングの依存関係(他ユニットの前/後)の確認は以下のコマンドで確認できます。
systemctl list-unit-files --type service
systemctl list-dependencies --after
systemctl list-dependencies --before
有効化すると何が起きるか
enableを行うと、選択したサービスユニット設定ファイルのの[Install]
セクションを読み取り、/etc/systemd/system/
配下(もしくはそのサブディレクトリ)に/usr/lib/systemd/system/name.service
ファイルへのシンボリックリンクを作成します。
実際、enable、disableを行うと以下の通りメッセージが出力される。
[vagrant@rhel8 ~]$ sudo systemctl disable rsyslog.service
Removed /etc/systemd/system/syslog.service.
Removed /etc/systemd/system/multi-user.target.wants/rsyslog.service.
[vagrant@rhel8 ~]$
[vagrant@rhel8 ~]$ sudo systemctl enable rsyslog.service
Created symlink /etc/systemd/system/multi-user.target.wants/rsyslog.service → /usr/lib/systemd/system/rsyslog.service.
[vagrant@rhel8 ~]$
[vagrant@rhel8 ~]$ ls -l /etc/systemd/system/multi-user.target.wants/ | grep syslog
lrwxrwxrwx. 1 root root 39 Jan 31 09:46 rsyslog.service -> /usr/lib/systemd/system/rsyslog.service
エイリアス
ユニットにはエイリアス名も設定することができ、特定のユニットのエイリアスは以下のコマンドで確認できます。
systemctl show nfs-server.service -p Names
systemdのターゲット
ターゲットユニットファイル(xxx.target)は依存関係の連鎖で他のsystemdユニットをグループ化するために使われます。
Linuxを起動する際はモードというものが数種類あり、systemd以前はその仕組みをランレベルと呼んでいました。systemdでは従来のランレベルをターゲットユニットで定義しており、互換性の観点から、以下の通りマッピングされています(これもRedHatのページに載っています)
ランレベル | ターゲットユニット | 説明 |
---|---|---|
0 | runlevel0.target, poweroff.target | システムをシャットダウンし、電源を切ります。 |
1 | runlevel1.target, rescue.target | レスキューシェルを設定します。 |
2 | runlevel2.target, multi-user.target | 非グラフィカルなマルチユーザーシステムを設定します。 |
3 | runlevel3.target, multi-user.target | 非グラフィカルなマルチユーザーシステムを設定します。 |
4 | runlevel4.target, multi-user.target | 非グラフィカルなマルチユーザーシステムを設定します。 |
5 | runlevel5.target, graphical.target | グラフィカルなマルチユーザーシステムを設定します。 |
6 | runlevel6.target, reboot.target | システムをシャットダウンして再起動します。 |
デフォルトターゲット
/etc/systemd/system/default.target
のファイル、もしくはsystemctl get-default
コマンドで確認できます。
↓の例では、multi-user.targetがデフォルトになっています。
[vagrant@rhel8 ~]$ systemctl get-default
multi-user.target
[vagrant@rhel8 ~]$ ls -l /etc/systemd/system/default.target
lrwxrwxrwx. 1 root root 41 Jan 20 22:51 /etc/systemd/system/default.target -> /usr/lib/systemd/system/multi-user.target
デフォルトターゲットはsystemctl set-default xxxx.target
コマンドで変更できます。
これによりdefault.targetのシンボリックリンクが置き換わります。
レスキューモード
レスキューモードはシステムの修復などで利用するシングルユーザー環境を提供するモードで、ネットワーク機能や他のユーザのログインは不可のまま、ローカルファイルシステムのマウントと、一部のシステムサービスのみを開始します。
現行のセッションでレスキューモードに入るには以下の通り実行します。現在ログインしているユーザにメッセージが送信されます。
# systemctl rescue
Broadcast message from root@localhost on pts/0 (Fri 2013-10-25 18:23:15 CEST):
The system is going down to rescue mode NOW!
systemdでの電源管理
systemdではsystemctl
コマンドでシステムのシャットダウン・再起動・サスペンドなども行えます。
詳細はRedHatのページを参照ください。
こちら↓も参考になりました。
haltを実行してもSystem haltedで止まり電源が落ちない(halt, shutdown, poweroffの違いまとめ)
systemdユニットファイルの設定
例として、chronyd(ntpの後継)のユニット設定ファイル(デフォルト)が以下になります。
(参考)Systemd入門(4) - serviceタイプUnitの設定ファイル
[vagrant@rhel8 ~]$ sudo cat /usr/lib/systemd/system/chronyd.service
[Unit] ←typeに依存しない一般的な設定
Description=NTP client/server ←ユニットの説明。`systemctl status`で出力される
Documentation=man:chronyd(8) man:chrony.conf(5) ←ドキュメントを参照するURI
After=ntpdate.service sntp.service ntpd.service ←起動順序。指定したユニットの後に開始する
Conflicts=ntpd.service systemd-timesyncd.service ←同時に有効化してはいけないユニット
ConditionCapability=CAP_SYS_TIME ←
##他には
##Requires: 同時に有効化するユニット。この中のいずれかが開始しないと、アクティブにならない
##Wants: 同時に有効化するユニット。Requireよりも弱い依存性で、前提Unitが失敗しても起動処理を行う
[Service] ←サービスtype固有の設定
Type=forking ←起動タイプ。forkingの場合は、ExecStartで起動するプロセスが、サービスのメインとなる子プロセスを起動する。デフォルトはsimple(ExecStartの起動プロセスがメインプロセスとなる)
PIDFile=/run/chrony/chronyd.pid ←PIDファイルのパス
EnvironmentFile=-/etc/sysconfig/chronyd ←環境設定ファイル
ExecStart=/usr/sbin/chronyd $OPTIONS ←ユニットの開始時に実行するコマンド
ExecStartPost=/usr/libexec/chrony-helper update-daemon ←サービス起動後の追加コマンド
PrivateTmp=yes ← プロセスが使う一時ディレクトリをそのプロセスにしか見えないようにする
ProtectHome=yes ←Unitのプロセスが/home, /root, /run/userに一切アクセスできなくなる
ProtectSystem=full ←trueの場合、実行するUnitのジョブから見た/usr, /bootが読み取りのみ(書き込み禁止)になる。fullだと、/etcも読み取りのみになる。
[Install] ←有効/無効化された時の設定
WantedBy=multi-user.target ←このユニットに依存するユニット
##他には
##Required: このユニットに強く依存するユニット
カスタムユニットファイルの作成
自前のカスタムデーモンを実行する場合、既存のサービスの2つ目のインスタンスを作成する場合の2パターンを試してみます。
カスタムデーモンの作成
※さくっとやりたい方は、[Systemdを使ってさくっと自作コマンドをサービス化してみる]を参照いただくと良いかもしれません。
dateコマンドの結果をファイルに書き込むだけのデーモン用scriptを作成しました。
#!/bin/bash
while true;
do
echo $(date) >> /opt/date.txt
sleep 2
done
これを使って、以下のようなUnit定義ファイルを用意します。
[Unit]
Description = Date Log(Custom daemon)
[Service]
Type = simple
ExecStart = /opt/date.sh
[Install]
WantedBy = multi-user.target
すると、以下の通りUnit一覧に追加されています。
[root@rhel8 ~]# systemctl list-unit-files --type=service | grep date.service
date.service disabled
以下のコマンドで、scriptの実行権限を追加後、新しいUnitファイルの読み込み・起動が出来ます。
# chmod a+x /opt/date.sh
# ls -l /opt/date.sh
-rwxr-xr-x. 1 root root 67 Feb 1 15:12 /opt/date.sh
# systemctl daemon-reload
# systemctl start date.service
# systemctl status date.service
● date.service - Date Log(Custom daemon)
Loaded: loaded (/etc/systemd/system/date.service; disabled; vendor preset: disabled)
Active: active (running) since Mon 2021-02-01 15:24:42 UTC; 3s ago
Main PID: 26275 (date.sh)
Tasks: 2 (limit: 11394)
Memory: 864.0K
CGroup: /system.slice/date.service
├─26275 /bin/bash /opt/date.sh
└─26279 sleep 2
Feb 01 15:24:42 rhel8.localdomain systemd[1]: Started Date Log(Custom daemon).
#
# tail -f /opt/date.txt
Mon Feb 1 15:28:53 UTC 2021
Mon Feb 1 15:28:55 UTC 2021
Mon Feb 1 15:28:57 UTC 2021
...
# ps aux | grep date
root 26851 0.0 0.1 25952 3408 ? Ss 15:33 0:00 /bin/bash /opt/date.sh
既存サービスの2つ目のインスタンス作成
こちらの「3.5.3.1. sshd サービスの 2 番目のインスタンスを使用したカスタムユニットファイルの作成」にsshd
の2つ目のサービスを起動する方法が載っています。
詳細はここでは割愛しますが、主に以下の内容を行っています。
サービスに依って必要な内容は変わると思います。
- サービスの設定ファイル、Unit定義ファイルをコピーして編集(主な変更点は↓のとおり)
- 元々のサービスと重なってしまうサービス設定(ポート番号、PIDファイルなど)の変更
- ↑の変更に伴うUnit定義ファイルの変更(設定ファイル名, Afterオプション(元々のインスタンスの後にする)など)
- 2つ目のインスタンスでは実行不要なもの(鍵の生成など)はUnit定義ファイルから削除
参考
RedHatカスタマポータル: 第3章 SYSTEMD によるサービス管理
Linux女子部 systemd徹底入門
Developers.IO by Classmethod: systemd超入門
Qiita: いまだにsysvinitの手癖が抜けない人のためのsystemd
Qiita: これからSystemd入門する
めもめも: Systemd入門(1) - Unitの概念を理解する
めもめも: Systemd入門(4) - serviceタイプUnitの設定ファイル