systemdでLinuxプロセスを管理する完全ガイド:サーバー運用で使える実践Tips

Linux tutorial - IT technology blog
Linux tutorial - IT technology blog

5分でわかる — 毎日使うsystemdコマンド

Linuxサーバーを始めたばかりですか?これは意識しなくても自然に覚えてしまうほど頻繁に使う5つのコマンドです:

# サービスのステータスを確認する
systemctl status nginx

# 起動 / 停止 / 再起動
systemctl start nginx
systemctl stop nginx
systemctl restart nginx

# 起動時の自動起動を有効化 / 無効化
systemctl enable nginx
systemctl disable nginx

# 再起動せずに設定をリロード(サービスがサポートしている場合)
systemctl reload nginx

さっそくnginxや手元にあるサービスで試してみましょう。緑色のactive (running)なら正常、赤色のfailedなら問題あり — 非常に直感的で、ドキュメントを読まなくてもわかります。

systemdの仕組みを理解する

SysVinit の後継として登場したsystemdは、現在Ubuntu、Debian、CentOS/AlmaLinux、FedoraなどほぼすべてのLinuxディストリビューションでデフォルトのinitシステムになっています。旧世代との最大の違いは、並列起動、サービス間の依存関係管理、そして/var/log/に散在していたログをジャーナルに一元管理できる点です。

systemdが管理するものはすべてunitと呼ばれます。最もよく使う4種類を紹介します:

  • .service — バックグラウンドプロセス(nginx、mysql、sshdなど)
  • .timer — cronに似た定期タスクの実行
  • .socket — ソケットアクティベーション(接続があった時だけサービスを起動)
  • .mount — マウントポイントの管理

ユニットファイルは主に2か所に存在します:

  • /lib/systemd/system/ — パッケージマネージャーがインストール、直接編集は避ける
  • /etc/systemd/system/ — オーバーライドや新規作成用、ここが作業場所

アプリケーション用のカスタムsystemdサービスを作成する

これが私が最もよく使う部分です。PythonやNode.jsのスクリプトをサーバーにデプロイするたびに必ずサービスファイルを作成します — nohupscreenはもう使いません。

例:ポート8000で動くPythonアプリケーションをデプロイする場合:

sudo nano /etc/systemd/system/myapp.service
[Unit]
Description=My Python App
After=network.target

[Service]
Type=simple
User=ubuntu
WorkingDirectory=/home/ubuntu/myapp
ExecStart=/home/ubuntu/myapp/venv/bin/python app.py
Restart=on-failure
RestartSec=5
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=multi-user.target
# systemdが新しいファイルを認識するためにリロード
sudo systemctl daemon-reload

# 有効化して起動
sudo systemctl enable --now myapp

Restart=on-failureに注目してください — クラッシュ時にサービスが自動的に再起動します。先月、私のサービスが深夜2時にクラッシュしましたが、5秒以内に自動復旧し、翌朝ログを見るまで気づきませんでした。夜中にSSH接続して対応する必要はありませんでした。

よく使うRestartの値

  • no — 自動再起動しない(デフォルト)
  • on-failure — 終了コードが0以外の時に再起動
  • always — 手動でsystemctl stopした場合でも常に再起動
  • on-abnormal — シグナルでkillされた時に再起動

99%のケースではon-failureを使いましょう。alwaysは安全に聞こえますが、アプリが起動直後に失敗すると無限ループになります — systemdは制限に達するまで再起動を試みた後、failedとマークします。

サービスが失敗したときのデバッグ

サービスがfailedと表示されましたか?慌てないでください。私のデバッグ手順:

# 詳細なステータスと最後の数行のログを表示
systemctl status myapp -l

# サービスのすべてのログを表示
journalctl -u myapp

# リアルタイムでログを表示(ライブ監視)
journalctl -u myapp -f

# 今回の起動からのログのみ表示
journalctl -u myapp -b

# 最後の50行
journalctl -u myapp -n 50

journalctlはすべてのログをタイムスタンプ付きで一か所に集約し、ユニットや時間でフィルタリングできます。これに慣れてからはtail -f /var/log/...の習慣をすっかりやめました。

最もよくある間違い:.serviceファイルを編集した後にdaemon-reloadを実行し忘れることです。systemdはキャッシュ内の古いバージョンを使い続けるため、変更が反映されません。ファイルを編集するたびに必ずリロードしてください — 例外はありません。

応用編 — 元のファイルを変更せずにサービスをオーバーライドする

実際のシナリオ:nginxのファイルディスクリプタ上限を65536に増やしたい(デフォルトは1024)が、apt upgradeで上書きされてしまうため/lib/systemd/system/nginx.serviceを直接編集したくない場合。

# editコマンドを使用 — オーバーライドディレクトリを自動作成
sudo systemctl edit nginx

上記のコマンドはエディタを開き、/etc/systemd/system/nginx.service.d/override.confにファイルを作成します。変更したい部分だけ記述すれば十分です:

[Service]
LimitNOFILE=65536
Environment="EXTRA_OPTS=-g 'worker_processes 4;'"

すべてのオーバーライドを結合した最終結果を確認したい場合:

systemctl cat nginx

cgroupsでリソースを制限する

cgroupsはあまり知られていませんが非常に便利な機能です — systemdに統合されており、各サービスのCPUとRAMを制限できます:

[Service]
# 最大512MB RAMに制限
MemoryMax=512M
# CPU使用率を50%に制限
CPUQuota=50%
# CPU優先度を下げる(デフォルト100)
CPUWeight=50

2GB RAMのVPSで5〜6個のサービスを同時に動かしている環境では、これが安心して眠れる理由です — 一つのサービスがリソースを食い尽くしてシステム全体を道連れにする心配がありません。

systemdタイマー — cronより効率的な代替手段

私のcronジョブのほとんどはsystemdタイマーに移行しました。理由はシンプル:デバッグがはるかに簡単です。ログはジャーナルに記録され、最後の実行日時と次の実行日時も確認できます — cronのように/var/log/syslogを探し回る必要がありません。

毎日午前2時にバックアップスクリプトを実行する例:

sudo nano /etc/systemd/system/backup.service
[Unit]
Description=Daily backup script

[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.sh
sudo nano /etc/systemd/system/backup.timer
[Unit]
Description=Run backup daily at 2am

[Timer]
OnCalendar=*-*-* 02:00:00
Persistent=true

[Install]
WantedBy=timers.target
sudo systemctl enable --now backup.timer

# すべてのタイマーとステータスを表示
systemctl list-timers

Persistent=trueは、サーバーが午前2時に停止していてスケジュールを逃した場合に対処します — 翌夜まで待つのではなく、サーバーが再起動したらすぐにタイマーが補完実行されます。

運用経験から得た実践的なTips

1. --nowを使って有効化と起動を同時に行う

systemctl enable --now myapp
# 同等:
systemctl enable myapp && systemctl start myapp

2. 失敗中のすべてのサービスを確認する

systemctl --failed

サーバーリブート後に最初に実行するコマンドです。失敗しているサービスがあればすぐにわかります — 一つひとつ確認する必要がありません。

3. 起動時間を分析する

systemd-analyze blame | head -20

起動に最も時間がかかるサービスを一覧表示します。私の開発マシンでは、snapdが30秒以上かかっていました — 無効にしたことで、起動時間が45秒から12秒に短縮されました。

4. 専用ユーザーでサービスを実行する(セキュリティ)

必要でない限り、rootでサービスを実行しないでください。専用ユーザーを作成します:

sudo useradd -r -s /bin/false myappuser
[Service]
User=myappuser
Group=myappuser
# 必要に応じてサンドボックスを追加
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true

5. ExecStartPre / ExecStartPost

サービス起動前にコマンドを実行します — 例えば、データベースが起動中ではなく本当に準備完了するまで待つ場合:

[Service]
ExecStartPre=/bin/sh -c 'until pg_isready -h localhost; do sleep 1; done'
ExecStart=/home/ubuntu/myapp/venv/bin/python app.py

この方法はAfter=postgresql.serviceより優れています。Afterは起動順序を保証するだけで、postgresが接続を受け付けられる状態かは保証しないからです。以前、アプリが先に起動してしまい、postgresがまだウォームアップ中で最初から接続が失敗するというエラーを経験したことがあります。

Share: