自作したシェルスクリプトをLinuxのsystemdサービスとして起動する方法

2023-03-27Bash,CentOS,Linux,Ubuntu

はじめに

最近は Kubernetesサーバレス なしくみを用いたサービス運用が進んでいるため、Linux の Systemd を触る機会はほぼありません。

利用機会は今後どんどん減ってでしょう。

しかし、 担当しているサービスの一部で Google Compute Engine を利用しており、 Systemd を使ってシェルスクリプトをサービス化しています。

AWS EC2 を使ったサーバー構築の可能性もあり、 Systemd を使ったサーバーを構築する機会はまだ残り続けるでしょう。

そこで Systemd を使ってシェルをサービス化する方法をまとめておきます。

概要

当エントリでは Linux サーバー上が起動してたタイミングで、Systemd からシェルスクリプトを自動的に実行する方法 について紹介します。

"Systemd" とは?

Systemd は、LinuxOS の起動、管理、および終了に関するサービス管理システムです。
従来は SysVinit という仕組みが使われていました。
今は SysVinitSystemd に置き換わり、OS の起動とサービスの管理を行う仕組みとなりました。

Systemd は、LinuxOS の初期化プロセスとして起動し、サービスの管理、ネットワークの設定、ログの管理などを主に担当しています。

管理者は簡単にシステムの状態を監視し、サービスの状態をコントロールできます。
以下は、管理者が利用する Systemd の基本的なコマンドの例です。

  • systemctl start [サービス名] : サービスを開始します。
  • systemctl stop [サービス名] : サービスを停止します。
  • systemctl enable [サービス名] : システムの起動時にサービスを自動的に起動します。
  • systemctl disable [サービス名] : システムの起動時にサービスを自動的に起動しないようにします。
  • systemctl status [サービス名] : サービスの状態を確認します。

"Systemd" の実行プロセス

Systemd の実行プロセスは以下のような性質を持っています。

  • Linux の起動プロセスの 1 つ
  • 複数のアプリケーションを子プロセスとして起動
  • 常にプロセス ID が 1 ( PID=1 )

とあるサーバで、 pstree コマンドで PID=1 のプロセスの情報を表示してみました。

$ sudo pstree 1
systemd─┬─accounts-daemon─┬─{gdbus}
        │                 └─{gmain}
        ├─acpid
        ├─2*[agetty]
        ├─apache2───12*[apache2]
        ├─containerd─┬─containerd-shim─┬─mysqld───27*[{mysqld}]
        │            │                 └─10*[{containerd-shim}]
        │            ├─2*[containerd-shim─┬─apache2───10*[apache2]]
        │            │                    └─10*[{containerd-shim}]]
        │            ├─3*[containerd-shim─┬─httpd───4*[httpd───26*[{httpd}]]]
        │            │                    └─10*[{containerd-shim}]]
        │            ├─4*[containerd-shim─┬─mysqld───35*[{mysqld}]]
        │            │                    └─10*[{containerd-shim}]]
        │            ├─containerd-shim─┬─httpd───10*[httpd]
        │            │                 └─10*[{containerd-shim}]
        │            └─25*[{containerd}]
        ├─cron
        ├─dbus-daemon
        ...
        ├─systemd───(sd-pam)
        ├─systemd-journal
        ├─systemd-logind
        ├─systemd-udevd
        └─unattended-upgr───{gmain}

systemd プロセスは Linux オペレーションシステムでアプリケーションをサービスとして起動し、有効・無効を管理するためのアプリケーションです。

シェルスクリプトを Systemd にサービスとして認識させる方法

教科書のような内容が続きましたが、ここからようやくシェルスクリプトを Systemd にサービスとして認識させる方法を紹介します。

1. まずはサービスとして登録したいシェルスクリプトを作成

早速自作のシェルスクリプトを Systemd にサービスとして登録してみます。

今回作成するシェルスクリプトは、ディスクの使用情報のサマリ情報を定期的にログとして書き出すというものにします。

「監視日時」と「ディスク使用状況のサマリ」をそれぞれ 別の行で出力し、120 秒スリープしたら、まだ最初から処理を開始し直します。

#!/usr/bin/env bash

while :; do
  date >>/var/log/storage-monitor.log
  du --exclude=/proc -sch / >>/var/log/storage-monitor.log
  sleep 120
done

このスクリプトをファイルに書き出し、実行権限を付与しておきます。

$ cat <<EOF >/usr/bin/script.sh
#!/usr/bin/env bash

while :; do
  date >>/var/log/storage-monitor.log
  du --exclude=/proc -sch / >>/var/log/storage-monitor.log
  sleep 120
done
EOF

$ chmod +x /usr/bin/script.sh

2. サービスを作成する

次に、 Systemd に登録したいサービスを作成します。

「サービスを作成する」といっても、 /etc/systemd/system というディレクトリに設定ファイルを作成するだけです。

ファイル作成時は以下の点に注意しましょう。

  • ファイル名は サービス名 とする
  • ファイルの拡張子は .service とする

ファイルの中身は以下のような内容となります。

$ cat <<EOF >/etc/systemd/system/monitor-disk.service
[Unit]
Description=Disk monitoring shell script service
Documentation=
#After=networking.service

[Service]
Type=simple
User=root
Group=root
TimeoutStartSec=0
Restart=on-failure
RestartSec=30s
#ExecStartPre=
ExecStart=/usr/bin/script.sh
SyslogIdentifier=Diskutilization
#ExecStop=

[Install]
WantedBy=multi-user.target
EOF

# 内容を確認する
$ cat /etc/systemd/system/monitor-disk.service
[Unit]
Description=Disk monitoring shell script service
Documentation=
#After=networking.service

[Service]
Type=simple
User=root
Group=root
TimeoutStartSec=0
Restart=on-failure
RestartSec=30s
#ExecStartPre=
ExecStart=/usr/bin/script.sh
SyslogIdentifier=Diskutilization
#ExecStop=

[Install]
WantedBy=multi-user.target

いわゆる ini ファイル 形式のファイルです。

以下の点に注意しましょう。

  • User=root : 実行ユーザ を指定
  • Group=root : 実行グループ を指定
  • ExecStart=/usr/bin/script.sh : 実行ファイル を指定

その他のフィールドについてもかんたんに説明します。

After

今回は利用していませんが、当サービス起動前に起動させておきたいサービス名を記述しておきます。
例えば、外部通信が必要なスクリプトの場合には、 After=networking.service と記述します。

ExecStartPre

今回は利用しませんでしたが、
指定されていた場合は ExecStart の処理が実行される前にこちらの処理が実行されます。

SyslogIdentifier

サービスのログは syslog 経由で出力されます。
syslog に出力されるログの各行に設定するプレフィックスとなります。

このプレフィックスを見れば、どのサービスのログかが特定しやすくなります。

3. systemctl コマンドを使ってサービスの開始、有効化、無効化、停止、再起動を行う

「サービスの作 」が終了したら、サービスを管理する systemd プロセスに対して、開始や停止を 指示 します。

systemd プロセスに対してサービスの管理を指示するためには、 systemctl コマンドを利用します。

サービスを開始

以下のコマンドでサービスを開始します。

$ systemctl start monitor-disk.service

スクリプトが正しく実行されていれば、スクリプトで生成されたログが確認できるはずです。

$ cat /var/log/storage-monitor.log

サービスの状態を確認

以下のコマンドでサービスの状態を確認します。

$ systemctl status monitor-disk.service

サービスが実行されているかを確認したり、出力されたログを確認も可能です。

サーバ起動時に自動的にサービスを起動させる

このままではサーバ再起動が行われるたびに、手動でサービスを起動してやる必要があります。

これは面倒なので、サーバ起動時に自動的にサービスが起動されるようにしておきましょう。

$ systemctl enable monitor-disk.service

自動機能を無効にする場合は以下のコマンドを実行します。

$ systemctl disable monitor-disk.service

サービスを停止

停止は以下のコマンドです。

$ systemctl stop monitor-disk.service

サービスを再起動

再起動は以下のコマンドです。

$ systemctl reload monitor-disk.service

syslog について

Systemd はデフォルトで syslog にログを書き出します。

したがって、リアルタイムのログを確認したい場合には以下のコマンドが利用できます。

$ tail -f /var/log/syslog

サービス設定ファイル ( /etc/systemd/system/monitor-disk.service ) に SyslogIdentifier=Diskutilization という設定を記述しておいたので、
キーワードで検索すれば当スクリプトのログだけに絞り込むことが可能でう s。

$ tail -f /var/log/syslog | grep Diskutilization

ひとこと

はじめにいったとおり、 Kubernetesサーバレス なしくみを用いたサービス運用が進んでいるため、Linux の Systemd を触る機会は最近ほとんどありませんね。

2023-03-27Bash,CentOS,Linux,Ubuntu