はじめに
自宅サーバーやクラウド上のLinux環境でアプリを動かしていると、こんな悩みに出会うことがあります。
- ターミナルを閉じたらアプリが止まってしまう
- サーバーを再起動したら、また手動でアプリを起動し直さないといけない
- アプリが落ちていたとき、原因を調べるログがどこにあるかわからない
これらをまとめて解決してくれるのが systemd という仕組みです。本記事では、systemdを構成する要素のうち特に登場頻度の高い「サービスファイル」「systemctl」「journalctl」の3つをまとめて解説します。
systemdとは
まず前提となるsystemdについて簡単に触れておきます。
- Linuxのinitシステム。OS起動時に最初に起動するプロセス(PID 1)で、以後起動する全プロセスの管理元になる
- 従来の
/etc/init.d配下のシェルスクリプトで起動処理を書く方式(SysVinit)に代わり、Debian/Ubuntu/RHEL系/Fedoraなど現在の主要ディストリビューションで標準採用されている - サービスの起動・停止・依存関係の解決・並列起動・ログの一元管理などをまとめて担う
systemdが管理する対象は「ユニット(Unit)」と呼ばれ、常駐プログラムを扱うものが本記事のテーマであるサービスユニットです。
サービスファイル(Unitファイル)とは
サービスユニットの実体は、.serviceという拡張子を持つ設定用のテキストファイルです。「サービスファイル」「unitファイル」などと呼ばれます。
主な設置場所
| パス | 用途 |
|---|---|
/usr/lib/systemd/system/ |
パッケージ管理(apt/dnf等)でインストールされたサービスの標準定義 |
/etc/systemd/system/ |
管理者が独自に追加・上書きするサービス定義。標準定義より優先される |
自作のアプリをサービス化する場合は、基本的に/etc/systemd/system/配下に作成します。
基本構造
サービスファイルは[Unit] [Service] [Install]という3つのセクションで構成されるのが基本形です。FastAPIアプリを例にすると、以下のようなイメージになります。
[Unit]
Description=Sample FastAPI Application
After=network.target
[Service]
Type=simple
User=appuser
WorkingDirectory=/opt/sample-app
ExecStart=/opt/sample-app/venv/bin/uvicorn app.main:app --host 0.0.0.0 --port 8000
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
主なディレクティブは次の通りです。
| セクション | ディレクティブ | 内容 |
|---|---|---|
| [Unit] | Description | サービスの説明文 |
| [Unit] | After | 起動順序の制御(このユニットの後に起動、が指定した対象より後になる) |
| [Unit] | Requires / Wants | 依存するユニットの指定(Requiresは必須依存、Wantsは緩い依存) |
| [Service] | Type | プロセスの起動形態(後述) |
| [Service] | ExecStart | サービス起動時に実行するコマンド |
| [Service] | ExecStop | サービス停止時に実行するコマンド(省略可) |
| [Service] | Restart | 異常終了時などの再起動ポリシー(no/on-failure/always等) |
| [Service] | User / Group | 実行ユーザー・グループの指定 |
| [Service] | WorkingDirectory | 作業ディレクトリ |
| [Service] | Environment / EnvironmentFile | 環境変数の指定 |
| [Install] | WantedBy |
enable実行時にどのターゲット(起動段階)に紐づけるか |
Typeには主に以下の種類があります。
| Type | 内容 |
|---|---|
| simple | ExecStartで起動したプロセスがそのままメインプロセスになる(デフォルト) |
| forking | 起動時に一度フォークして親プロセスが終了する、従来型デーモン向け |
| oneshot | 一度実行して終了する処理向け(常駐しない) |
| notify | プロセス自身がsystemdに起動完了を通知する方式 |
サービスファイルを新規作成・変更した後は、systemctl daemon-reloadを実行しないと変更内容がsystemdに反映されません。編集後にstartやrestartをしても反映されない場合、まずこのコマンドを忘れていないか確認するのがセオリーです。
systemctlとは
systemctlは、systemdが管理するユニットを操作するためのコマンドです。サービスの起動・停止から自動起動設定、状態確認まで、日常の運用操作はほぼこのコマンド一つでまかなえます。
主なサブコマンド
| コマンド | 内容 |
|---|---|
systemctl start <unit> |
今すぐ起動する |
systemctl stop <unit> |
今すぐ停止する |
systemctl restart <unit> |
再起動する |
systemctl reload <unit> |
プロセスを再起動せずに設定だけ再読み込みする(対応サービスのみ) |
systemctl status <unit> |
状態・直近ログを確認する |
systemctl enable <unit> |
OS起動時に自動起動するよう設定する |
systemctl disable <unit> |
自動起動設定を解除する |
systemctl enable --now <unit> |
自動起動の有効化と今すぐの起動を同時に行う |
systemctl is-enabled <unit> |
自動起動設定になっているか確認する |
systemctl is-active <unit> |
現在稼働中かどうか確認する |
systemctl list-units --type=service |
実行中のサービス一覧を表示する |
systemctl daemon-reload |
サービスファイルの変更をsystemdに反映する |
「起動する」と「自動起動を設定する」は別物
見落としやすいポイントですが、startとenableは役割が異なります。
-
start:今この瞬間サービスを起動する。次回OS再起動時には引き継がれない -
enable:OS起動時に自動的に起動されるよう登録する。ただし今すぐは起動しない
そのため、常駐させたいアプリではsystemctl enable --now sample-app.serviceのように両方をまとめて実行するのが定石です。
statusで見る主な状態
systemctl status <unit>を実行すると、Active:欄に稼働状態が表示されます。
| 表示 | 意味 |
|---|---|
| active (running) | プロセスが常駐して稼働中 |
| active (exited) | 正常に実行され、すでにプロセス自体は終了している状態(Type=oneshotなどで見られる) |
| inactive (dead) | 停止している |
| failed | 異常終了した |
| activating | 起動処理中 |
active (exited)は一見「動いていない」ように見えますが、異常ではありません。ディストリビューションによっては、実体となる個別サービス(例:バージョン番号付きの子サービス)を起動するためだけの「ラッパー用ユニット」が用意されており、役目を終えると正常にexitする設計になっていることがあります。「exited=異常」と早合点せず、実際に目的のプロセスが起動しているかはpsコマンドや該当の個別サービスの状態で確認するのが確実です。
journalctlとは
journalctlは、systemdのログ収集の仕組みであるjournaldが集めたログを閲覧するためのコマンドです。
従来のLinuxでは、/var/log/syslogや/var/log/auth.logのようにログの種類ごとにテキストファイルが分散していました。journald環境下では、各サービスの標準出力・標準エラー出力を含め、ログがバイナリ形式で一元的に記録されます(/var/log/journal/配下)。バイナリのため直接catでは読めず、journalctl経由で検索・整形して閲覧します。
主なオプション
| オプション | 内容 |
|---|---|
journalctl -u <unit> |
特定のサービスのログのみ表示する |
journalctl -f |
tail -fのようにリアルタイムで追尾表示する |
journalctl -b |
直近の起動以降のログのみ表示する |
journalctl --since "10 min ago" |
相対/絶対時刻を指定して絞り込む |
journalctl -p err |
優先度(緊急度)で絞り込む(指定レベル以上) |
journalctl -r |
新しい順(降順)に表示する |
journalctl -n 50 |
末尾50行のみ表示する |
journalctl --disk-usage |
ジャーナルが使用しているディスク容量を確認する |
journalctl --vacuum-time=7d |
7日より古いログを削除して容量を整理する |
-pで指定できる優先度は、syslogの重大度レベルに準拠しています。
| レベル | 名称 | 内容 |
|---|---|---|
| 0 | emerg | システム利用不能 |
| 1 | alert | 直ちに対応が必要 |
| 2 | crit | 重大な状態 |
| 3 | err | エラー |
| 4 | warning | 警告 |
| 5 | notice | 正常だが注意すべき事象 |
| 6 | info | 情報 |
| 7 | debug | デバッグ情報 |
自作サービスのログをリアルタイムで追いたいときは、次のように組み合わせるのが定番です。
journalctl -u sample-app.service -f
3つの関係性
ここまでの内容を一連の流れとして整理すると、次のようになります。
-
サービスファイルを
/etc/systemd/system/に作成し、起動コマンドや再起動ポリシーを定義する -
systemctl daemon-reloadで変更をsystemdに反映する -
systemctl enable --now <unit>で自動起動を設定しつつ起動する - 動作がおかしいときは
systemctl status <unit>で状態を確認し、詳細な原因はjournalctl -u <unit>でログを追う
この4ステップを押さえておけば、自作アプリを「落ちても自動的に立ち上がり直す常駐サービス」として運用するための基本は一通りカバーできます。
まとめ
| 要素 | 役割 |
|---|---|
| サービスファイル | サービスの起動方法・再起動ポリシーなどを定義する設定ファイル |
| systemctl | サービスの起動・停止・自動起動設定・状態確認を行う操作コマンド |
| journalctl | systemdが一元管理するログを検索・閲覧するコマンド |