春の入門連載2024 の4本目です。
はじめに
こんにちは、最近寒暖差が激しくて体調崩しがちなTIGの森です。
入門向け記事としてLinuxの systemd における service unit の起動と停止のフローについて説明します。
service unit とは
service unit とは systemd の設定単位の1つで、Linuxにおけるサービスの振る舞いを定義する設定をまとめたものです。主にデーモンやその他のシステムプロセスといった、起動や停止を含む管理を行います。
サービス起動の大まかな流れ
サービスの起動のおおまかな流れを説明します。
ここでは話を簡単にするため、Typeディレクティブ(後述)がデフォルトの simple に指定されているとします。
サービス起動の流れ
systemctl start [サービス名].serviceを実行すると、systemdは指定されたservice unitを起動するsystemdはservice unitの設定ファイル(.service)を確認し、サービスが有効かどうかなどを調べるsystemdがfork()を実行し、成功した時点でservice unitがactiveになる。この新しく生成されたプロセスはサービスを起動するためのプロセスとなる- この新しいプロセスが必要な環境設定(環境変数の設定など)を行った後、
ExecStartで指定されたプログラムをexecve()で実行する - サービスのプログラムが実行され、サービスが開始する
以上がざっとした流れです。もちろんLinuxにおけるサービス管理全てを網羅しているということではありません。初期化だけを実施するiptablesなどの例外もあります。
サービス起動・終了時の前後の処理に関して
サービス実行の主要なコマンドの前に環境変数の設定や通信路の準備や初期化などを実施する場合、ExecStartの前処理として実行されるExecStartPreや、activeとなった時点で実行されるExecStartPost、また終了時に実行されるExecStopPostなどのディレクトリが用意されています。
サービス起動・終了に関連するディレクティブは下記です。
| 名称 | 概要 |
|---|---|
ExecStartPre |
依存関係を満たすためのリソースの確保、前段処理を実施する |
ExecStart |
環境変数を読み込み、サービス処理のプログラムを実行する |
ExecStartPost |
activeになる前のタイミングで実施する後処理を実施する |
ExecStop |
サービス停止のプログラムを実施する |
ExecStopPost |
サービス終了後の後処理を実施する。inactiveになる前の実施する |
ExecReload |
サービスのリロードを実施する |
サンプル
例として、ExecStartPreで実際にどのような前処理が実施できるか試してみましょう。
EC2のインスタンスのメタデータから自身のリージョンを取得し、それをサービスから呼び出して出力させます。
手順
環境変数の設定ファイル
/etc/environmentに下記の行を追加して、デフォルトのリージョンを指定/etc/environment Region=default
ExecStartPreで叩かれる事によって自分のリージョンを取得するシェル/etc/setEnvConf.shを作成/etc/setEnvConf.sh
REGION=$(curl -s http://169.254.169.254/latest/meta-data/placement/availability-zone | sed -e 's/.$//')
sudo sed -i "/^Region=/c\Region=$REGION" /etc/environment/etc/systemd/system配下にtest.serviceを作成test.service [Unit]
Description=Test Service
After=network.target
[Service]
EnvironmentFile=/etc/environment
ExecStartPre=/bin/bash /etc/setEnvConf.sh
ExecStart=/bin/bash -c 'echo "The region of this instance is ${Region}" > /tmp/output.txt'
[Install]
WantedBy=multi-user.targetサービスファイルを読み込んで起動
sudo systemctl daemon-reload
sudo systemctl start test
以上が手順になります。
サービスが無事起動したら/tmp/output.txtを見てみましょう。
The region of this instance is ap-northeast-1 |
ちゃんと自身のインスタンスのリージョンが取得できています。
災対環境などでAMIからインスタンスを起動する時に自身のリージョンを取得して、処理に繋げる際などに応用できるでしょう。
注意点
今回は触れませんでしたがunit がactiveやinactiveになるタイミングは Type ディレクティブの指定によって微妙に変化します。
この記事ではデフォルトのsimpleを用いていますが、これは代表プロセスがfork()の実行に成功したときにactiveになります。つまり、ExecStartが失敗しようがその前段階の環境変数の設定やExecStartPreができていればactiveにはなってくれるということです。
一方で明示的にTypeをexecに指定した場合、ExecStartで指定したプログラムからexecve()の実行が成功したときにactiveになります。他にも様々な Type ディレクティブがありますが、active になるタイミングがいつなのかはしっかり意識して前処置や後処理を運用していくことが運用上求められるでしょう。
まとめ
システムを安定させる上で重要なのは、サービスがactiveになるタイミングやその前後の処理の流れを正確に理解することです。
また、[Unit]セクションにAfterを記述し、異なるサービス間で起動順序を制御することも一般的です。したがって、これらの順序をより一層意識した運用が求められるでしょう。