Systemd メモ書き

SysVinitおぢさんなので。

コレのメモ書き http://www.slideshare.net/enakai/linux-27872553


1. SysVinit / UpStart
1. systemd の起動処理/操作方法
1. journald のログ管理
1. Unit設定の書き方
1. Tips


## SysVinit / Upstart

BIOS が Grub を読み込んで実行 → Grub が Kernel と初期ラムをメモリに展開して Kernel 実行 → Kernel が init Script を実行


- SysVinit/Upstart は /etc/inittabを元に処理を実行。
 - rc.sysinit (システム初期化
 - rc (サービス起動)
 
   - /etc/init.d/(servise) start
 
 - migetty/prefdm ログイン受付


- UpStart
 - RHEL6 にあった
 - 実際にジョブとして実行される内容はRHEL5と同じ
- /etc/init.d/(service) のスクリプトはただのシェルスクリプト

- Systemd が目指したもの
 - システム起動時間の短縮: 並列実行ではなかったので、遅かった
 - システム構成の動的変更: システム構成の変更に応じて、動的に必要なサービスの起動・停止をしたい
 - プロセス停止処理を標準機能化: 今まではシェルスクリプトで各自実装だったのを、systemd 自身が面倒を見る形
 - デーモン実行環境の制御: サービスごとに関連するプロセスの実行環境（Cgroups によるリソース割当とかアクセス可能なディレクトリなど）を設定するようにしたい。あとログ管理も。

## Systemd

1. Unit
 1. SysVinit のスクリプトに含まれていた個々の処理を抜き出し、それらを「Unit」として定義
  1. 例: rsyslog.service, systemd-remount-fs.service, tmp.mount, proc-sys-fs-binfmt_misc.mount, ..
1. Unit のタイプ
 1. .service: サービス。対応するデーモンが起動
 1. .target: 何もしない。依存関係や順序関係を定義する時に、複数のUnitをグループ化するのに使用する
 1. .mount: 有効化するとデバイスなどのマウントをする
 1. .swap : Swap領域の有効化
 1. .device: デバイス。udevがデバイスを認識すると有効化
 1. .socket: systemd が特定のソケットをListenし、接続があると、指定のデーモンを起動してソケットを受け渡す。(xinetd みたいな機能)
1. Unitには明示的に設定するのと、自動的に作成するのがある
 1. .service, .target は設定ファイルで明示的に定義する
 1. .mount, .swap は /etc/fstab から自動作成
 1. .device は udev によって自動作成

### Unit 定義ファイルの場所

- 二箇所ある。
 1. /usr/lib/systemd/system/ : システムデフォルト
 1. /etc/systemd/system/ : 管理者がカスタマイズする場所
- 同じ名前の設定ファイルがある場合、 `/etc/systemd/system/` が優先
 - システムデフォルトの設定を変更する場合、 `/usr/lib/systemd/system/` から `/etc/systemd/system` に設定ファイルをコピーして編集
- Unit名がそのまま設定ファイル名になる
 - シンボリックリンクでUnitの別名を設定することができる
 - ディレクトリ「(Unit名).wants」は依存関係の定義に使用

### Unit の依存と順序

1. 依存関係: 「A Unit を有効化するなら、B Unit も有効化するべき」
1. 順序関係: 「A Unit を有効化する前に、B Unit を有効化するべき」

- systemd が起動すると「default.target」が有効化され、それに依存するUnitがまとめて有効化
 - default.target の実態は「multi-user.target」「graphical.target」などへのシンボリックリンク。リンク先をカエルことが「デフォルト runlevelの変更」に相当

~~ 図解 ~~

要は、 default.target のリンクが`rescue.target`に向いてる時は`runlevel1`相当になり、`multi-user.target`の時は`runlevel3`、`graphical.target`の時は`runlevel5`になる。
それぞれ、最低でも `local-fs.target`,`swap.target`,`sysinit.target`, `basic.target` が動く。

#### 依存

1. syetemd が起動すると、Unitの「依存関係」を元に、有効化されるUnit群を決める
 - ただし、起動する「順序関係」は別に定義
 - systemd は順序関係を持たないUnitに関しては、出来る限り並列実行を実施する。
1. 依存関係の設定。いずれかの方法でやる
 - Unit設定ファイルの`[Unit]`セクションで「Wants=」オプション、もしくは「Requires=」オプションで一緒に有効化するUnitを指定
 - ディレクトリ「(Unit).wants」「(Unit).requires」内に一緒に有効化するUnitの設定ファイルへのシンボリックリンクをはる
 - `Requires`は前提Unitが起動にこけると、このUnitの起動をやめる。`Wants`は前提Unitが起動にこけても、このUnitの起動処理を行う
 - 他に`Conflict=`オプションで、同時に有効化してはいけないUnitの指定ができる

#### 順序

1. 順序の設定は次の方法
 - Unit 設定ファイルの`[Unit]`セクションにおいて、`After=`オプションと`Before=`オプションで指定する
 - `After=A B C`: 自分自身は、Unit「A」「B」「C」の後に起動する
 - `Before=A B C`: 自分自身は、Unit「A」「B」「C」の前に起動する
1. Unit起動の「待ち合わせ」ポイントとして、target タイプのUnitを利用する

例えば

ネットワーク環境を準備するUnitには「Before=network.target」
ネットワーク環境を使用するUnitには「After=network.target」

- ネットワークを準備する例

	```/usr/lib/systemd/system/firewalld.service
[Unit]
Description=firewalld - dynamic firewall daemon
Before=network.target
Before=libvirtd.target
Before=NetworkManager.service
```


- ネットワークを利用する例

	```/usr/lib/systemd/system/httpd.service
[Unit]
Description=The Apache HTTP Service
After=network.target remote-fs.target nss-lookup.target
```

このように、`network.target`にかます事で、ネットワーク準備と使用のUnit依存をまとめる

- 現在有功なUnit依存、順序はコマンドで確認できる
- `# systemctl list-dependencies <Unit>`
 - 指定Unitが必要とするUnitを表示。省略時は「default.target」
 - 依存UnitがTargetタイプの時は、さらに、それが必要なUnitを再帰表示
 - --all オプションをつけると全てのUnitを再帰表示
- `# systemctl list-dependencies <Unit> --after`
 - 指定Unitより先に起動するUnitを表示
 - --all オプション有り
- `# systemctl list-dependencies <Unit> --before`
 - 指定Unitより後に起動するUnitを表示
 - --all オプション有り

### 動的生成Unit

/usr/lib/systemd/system-generators/ 以下は、システム環境に応じて、動的なUnit生成や、既存Unitの設定変更をする

例:

- systemd-cryptsetup-generator
 - /etc/crypttab を参照して systemd-cryptsetup@.service を生成
- systemd-fstab-generator:
 - /etc/fstab を参照して、 mountとswapのUnitを生成
 - mount はマウントポンとのパスで、「/」を「-」に置換したものがUnit名になる
- systemd-rc-local-generator
 - /etc/rc.d/rc.local が実行可能ファイルの場合、rc-local.service の自動機能を有効化


- 動的に生成されたUnitの設定ファイルは `/run/systemd/generator/` 以下に配置される


### 一覧表示

- `# systemctl list-unit-files`: 従来の`chkconfig --list`相当
 - enable: `WantedBy=`指定あり。自動起動が有功
 - disable: `WantedBy=`指定あり。自動起動が無効
 - static: `WantedBy=`指定なし

- `# systemctl list-utils`
 - 現在有功なUnit一覧とその状態を表示
 - --type オプションで特定タイプのUnitのみを表示
 - --type=service で service タイプのみなど。

### 基本操作

- `# systemctl enable/disable (unit)`
 - Unit の自動起動を有効化/無効化する
 - 実際には`WantedBy=`で指定されたUnitへの依存関係を設定/削除する
- `# systemctl start/stop/restart (unit)`
 - Unit をその場で 起動/停止/再起動する
 - reload は、Unit設定ファイルでreloadの動作が定義されている場合のみ使用できる
- `# systemctl status (unit)`
 - Unit の実行状態を表示
 - PID や Status や CGroup や関連するデーモンプロセスや直近ログの表示とか

- `# systemctl daemon-reload`
 - Unit設定ファイルを変更した時に、変更内容をsystemdに認識させる

- chkconfig/service のコマンドは、対応するsystemctlコマンドに変換されます
- サービス起動スクリプトで標準外オプションを使用していたのは、systemdでは使用できなくなる
 - 例: `# service postgresql initdb` → `# postgresql-setup initdb`


### CGroup の確認

(そもそもCgroupって何の略だろ…。Categoryかしら)

- systemd は Cgroup を利用してUnitごとに関連するプロセスを分類
 - `# systemd-cgls`: Cgroupによる分類を表示
 - `# systemd-cgtop`: リソース利用状況を表示。topコマンドみたいなもの

- Cgroup が同じプロセスに、まとめてシグナルを送信することができる
 - `# systemctl kill -s9 sshd.service`
 - sshd.service のグループに属するプロセスにKILLシグナルを送る
 - これにより、サービスに関連するプロセス全てを停止させることができる
 - `--kill-who=main`オプションをつけると、グループで最初に起動したプロセスのみにシグナルを送る
- `systemctl stop` で停止する時にも利用される
 1. 定義ファイルの`ExecStop=`で指定されたコマンドを実行
 2. 該当Unitのグループにプロセスが残ってる場合は、まとめて SIGTERMを。
 3. それでも残ってる場合は SIGKILL

## Journald

- rsyslogd へ出力する内容を、`systemd-journald.service`:通称「journald」に送る
 - Journald はメタ情報を追加して、 `/var/log/journal/`以下に記録
 - journaldのログはバイナリなので、journalctlコマンドを利用して、検索・表示する
- systemd では rsyslogd の設定変更が必要
 - 従来: Process → /dev/log → rsyslogd
 - systemd: Process → /dev/log → systemd → /run/systemd/journal/syslog → rsyslog


- rsyslogd には、journaldから直接ログ情報を取得するモジュール `imjournal` があり、UnixSocket `/run/systemd/journal/syslog`の代わりに、これを利用するのも可
 - RHEL7ではデフォルトで imjournalが使用
 - imjournal: http://www.rsyslog.com/doc/imjournal.html

### Journald の検索

- journalctl
 - -u (Unit): 指定ユニットに関係するログのみ表示
 - -b: 直前のサーバ起動以降のログを表示
 - -f: tail -f のように、順次表示する
 - --no-pager: less コマンドを使用しない
 - -a : 長いメッセージを省略しない
 - `--since='YYYY-MM-DD hh:mm:ss'`: 指定日以降のログ表示
 - `--until='YYYY-MM-DD hh:mm:ss'`: 指定日までのログ表示

## Unit 設定ファイル文法

- セクションわけがされている
 - [Unit]: 依存関係・順次関係など、Unitのタイプに依存しない設定を記載
 - [Install]: 「systemctl enable/disable」コマンドに関連する設定
 - [Service]: Service タイプに固有の設定項目を記載

- オプションに複数項目を記載するときは、スペース区切りか、同じオプションを複数回に分けて記載

- Unit セクションの主なオプション
 - Description: 説明
 - Documentation: ドキュメントのURI
 - Requires/Wants: 同時に有効化が必要な前提Unit
 - After: これより先に起動するべきUnit
 - Before: 後に起動するべきUnit

- Install オプション
 - `WantedBy`: enable時にこのUnitの.wantsディレクトリにシンボリックリンクをはる
 - `RequiredBy`: enable時にこのUnitの.requiredディレクトリにシンボリックリンクをはる
 - `Also`: enable/disable時に、同時に enable/disableするUnit

 - 例: syslog/network/auditdの環境が揃ってから起動する。multi-user.target の前提として有効化する。

```/usr/lib/systemd/system/sshd.service
[Unit]
Description=OpenSSH server daemon
After=syslog.target network.target auditd.service

[Service]
EnvironmentFile=/etc/sysconfig/sshd
ExecStartPre=/usr/sbin/sshd-keygen
ExecStart=/usr/sbin/sshd -D $OPTIONS
ExecReload=/bin/kill -HUP $MAINPID
KillMode=process
Restart=on-failure
RestartSec=42s

[Install]
WantedBy=multi-user.target
```

- Service タイプのオプション
 - ExecStart: サービス起動コマンド
 - ExecReload: サービス再起動コマンド？
 - ExecStop: サービス停止コマンド
 - ExecStartPre/ExecStartPost: サービス起動前後の追加コマンド/サービス起動判定には関連させたくないコマンドを記載
 - ExecStopPost: サービス停止後に実行するコマンド。サービスが以上停止した際にも実行される
 - EnvironmentFile: 環境変数を読み込むファイル
 - KillMode: ExecStopで停止せずに残ったプロセスの処理方法
 - Type: サービスプロセスの起動完了の判定方法
 - PIDFile: fork型サービスのメインプロセスのPIDファイル
 - BusName: D-Bus型サービスのbus接続名
 - Restart: サービスプロセス停止時の再起動条件（Default: no)
 - User/Group : プロセスを起動するユーザ/group
 - PrivateTmp: このサービス専用の/tmpと/var/tmpを用意する
 - ReadOnlyDirectories: 指定のディレクトリ以下をReadOnlyモードにする
 - InaccessibleDirectories: 指定ディレクトリ以下をアクセス不可にする
 - RootDirectory: 指定ディレクトリにchrootする
  - これらの機能は、ファイルシステムのネームスペースを分離することで実現している。そのため、サービスを提供するプロセスがこのネームスペース内で実施したバインドマウントは、他のプロセスからは見えなくなるという制限がある。(chroot した時に /dev/urandom ねーぞとか。)


- Typeはサービスの起動完了を判定するタイミングを指定
 - `Type=simple`: 指定コマンドがフォアグラウンドで実行を継続する場合。コマンドを実行したらすぐに起動完了と判定
 - `Type=forking`: 子プロセスをバックグラウンドで起動して、最初のコマンドをは終了する場合。最初のコマンドが終了したタイミングを起動完了と判断
 - `Type=oneshot`: 一度だけコマンドを実行するタイプのサービスの場合。コマンドが終了したら起動完了と判定して、サービスも終了したものと認識。（`RemainAfterExit=yes`を指定すると、コマンド終了後もサービスは起動したままと認識。）
 - `Type=notify`: systemdのライブラリ関数「sd_notify()」を使用する場合。プロセスのプログラム内部で、sd_notify()関数で起動完了を通知するように作られている必要が有ります。
 - `Type=dbus`: D-Bus(プロセス間通信用メッセージバス)を利用するサービスの場合。BusNameで指定した接続名がD-BUsに登録されると、サービス起動完了と判定

#### プロセスが異常終了したときの動作設定
- restartオプションでサービスの再起動を行うか指定
 - Restart=no: サービスの再起動は行いません
 - Restart=always: サービスの再起動を行う
- デフォルトでは10秒の間に5回以上再起動すると、次の10秒間は再起動を試みない。一般では「`StartLimitInterval`の間に`StartLimitBurst`回以上再起動すると、次の`StartLimitInterval`の間は再起動を試みない」

- systemdは起動したサービスに関連する全てのプロセスをCgroupの個別のグループに入れて管理している
- ExecStopコマンドで停止した時、グループ内にプロセスが残っている場合、KillModeの設定に応じて残プロセス処理を行う
 - `KillMode=none` 残プロセスは放置
 - `KillMode=process` メインプロセスが残っている場合、SIGTERM/SIGKILLで停止する。その他の残プロセスは放置
 - `KillMode=control-group` グループ内の全ての残プロセスを SIGTERM/SIGKILL で停止する
 - `KillMode=mixed` メインプロセスを SIGTERM/SIGKILL で停止し、続けてグループ内の全ての残プロセスを SIGKILL で停止する。


#### Type=forking?

Type=forking はメインプロセスの追跡にPIDファイルが必要になることから、systemdのサービス設定では`Type=Forking`より`Type=simple`,`Type=notify`の利用が推奨される。
- Apache httpd では mod_systemdモジュールで systemd に対応している


## Tips

### Unitの無効化

- `# systemctl mask/unmask (Unit)`
 - 指定のUnit を無効化/有効化する。`mask`を使用した場合、`disable`と異なり`systemctl start`で手動起動もできなくなります。
 - mask による無効化は、 `/etc/systemd/system`以下に /dev/null へのシンボリックリンクをはることで行われる。`/etc/systemd/system`以下に設定ファイルを作成している場合は、maskによる無効化はできない

### テンプレートタイプの設定ファイル

- hoge@.service は 「hoge@<任意の文字列>.service」という複数サービスの設定ファイルとして機能する
 - `# systemctl start hoge@huga.service`: 「hoge@huga.service」にしたがってサービス「hoge@huga.service」が起動する。この際、設定ファイルの「%I」が「<任意の文字列>」(この場合`huga`)に置換される（「%i」は「%I」内の記号文字を独自ルールでエスケープしたものになる）
 - `# systemctl enable hoge@huga.service`: .wantsディレクトリ内に、「hoge@huga.service」から「hoge@.service」へシンボリックリンクがはられます。これにより、「hoge@huga.service」を自動起動しようとしますが、実際には「hoge@.service」の設定が使用される