Edited at

Systemd メモ書き

SysVinitおぢさんなので。

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


  1. SysVinit / UpStart

  2. systemd の起動処理/操作方法

  3. journald のログ管理

  4. Unit設定の書き方

  5. 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」として定義

    2. 例: rsyslog.service, systemd-remount-fs.service, tmp.mount, proc-sys-fs-binfmt_misc.mount, ..



  2. Unit のタイプ


    1. .service: サービス。対応するデーモンが起動

    2. .target: 何もしない。依存関係や順序関係を定義する時に、複数のUnitをグループ化するのに使用する

    3. .mount: 有効化するとデバイスなどのマウントをする

    4. .swap : Swap領域の有効化

    5. .device: デバイス。udevがデバイスを認識すると有効化

    6. .socket: systemd が特定のソケットをListenし、接続があると、指定のデーモンを起動してソケットを受け渡す。(xinetd みたいな機能)



  3. Unitには明示的に設定するのと、自動的に作成するのがある


    1. .service, .target は設定ファイルで明示的に定義する

    2. .mount, .swap は /etc/fstab から自動作成

    3. .device は udev によって自動作成




Unit 定義ファイルの場所


  • 二箇所ある。


    1. /usr/lib/systemd/system/ : システムデフォルト

    2. /etc/systemd/system/ : 管理者がカスタマイズする場所



  • 同じ名前の設定ファイルがある場合、 /etc/systemd/system/ が優先


    • システムデフォルトの設定を変更する場合、 /usr/lib/systemd/system/ から /etc/systemd/system に設定ファイルをコピーして編集



  • Unit名がそのまま設定ファイル名になる


    • シンボリックリンクでUnitの別名を設定することができる

    • ディレクトリ「(Unit名).wants」は依存関係の定義に使用




Unit の依存と順序


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

  2. 順序関係: 「A Unit を有効化する前に、B Unit を有効化するべき」


  • systemd が起動すると「default.target」が有効化され、それに依存するUnitがまとめて有効化


    • default.target の実態は「multi-user.target」「graphical.target」などへのシンボリックリンク。リンク先をカエルことが「デフォルト runlevelの変更」に相当



~~ 図解 ~~

要は、 default.target のリンクがrescue.targetに向いてる時はrunlevel1相当になり、multi-user.targetの時はrunlevel3graphical.targetの時はrunlevel5になる。

それぞれ、最低でも local-fs.target,swap.target,sysinit.target, basic.target が動く。


依存


  1. syetemd が起動すると、Unitの「依存関係」を元に、有効化されるUnit群を決める


    • ただし、起動する「順序関係」は別に定義

    • systemd は順序関係を持たないUnitに関しては、出来る限り並列実行を実施する。



  2. 依存関係の設定。いずれかの方法でやる


    • 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」の前に起動する



  2. 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



  • 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の代わりに、これを利用するのも可




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」の設定が使用される