起動しないsystemdサービスは、ほとんどの場合謎ではありません。systemctl status <unit> を実行し、続けて journalctl -u <unit> -n 50 --no-pager でそのunitの直近50行を読む——この2つで、5つの原因のどれかがほぼ名指しで書いてあります: 間違ったパス、存在しないバイナリ、誤ったパーミッション、SELinux/AppArmorの拒否、unitファイルの構文エラー。 失敗の理由はログの中にあり、以下の修正はそれに対するパターンマッチでしかありません。
systemdサービスが失敗した理由を見るには?
まずstatus、次にjournalです:
systemctl status myapp.service
journalctl -u myapp.service -n 50 --no-pager status は状態(inactive (dead)、failed (exit-code)、activating (auto-restart))と直近のログ数行をくれます。journalは全文をくれます: stdout、stderr、そしてunitに対するsystemd自身の苦情です。
過去に失敗していてその実行の記録が欲しいなら、現在ブートなら -b、今日なら --since today を足します:
journalctl -u myapp.service -b --no-pager フルツールキット——ブート、プライオリティ、出力のライブ追尾——は journalctl cheat sheet をどうぞ。同じ筋肉の記憶です。
もう1組、知っておく価値があります:
systemctl list-units --failed
systemctl reset-failed myapp.service 1つ目はボックス上の全赤unitを一覧します。2つ目は直した後にfailed状態を晴らします——見た目だけですが、ステータスページが狼少年になるのを止めます。
最も多い原因は何?
ログが症状を名指した後、原因はほぼ必ずこの5つのどれかです:
| ログの表示 | 推定原因 | 直し方 |
|---|---|---|
status=203/EXEC | ExecStart= のパス違いか、インタプリタ不在 | 絶対パス、chmod +x、shebangを確認 |
スクリプトで status=203/EXEC | スクリプトのCRLF行末か誤ったshebang | dos2unix script.sh、1行目を直す |
status=1/FAILURE、アプリのログなし | ワーキングディレクトリか環境変数の欠落 | WorkingDirectory= を設定、Environment= を追加 |
Permission denied | ユーザーがファイルを読めないか、ポートをbindできない | 所有権を直す。1024未満のポートは AmbientCapabilities=CAP_NET_BIND_SERVICE かroot |
Unit is masked | 誰かが systemctl mask を実行した | systemctl unmask myapp.service |
| unitファイル編集が「何も起きない」 | デーモンがreloadされていない | systemctl daemon-reload |
203/EXEC ファミリーは特別扱いに値します。午後を丸ごと食べるのがこいつだからです。Systemdは ExecStart= を起動するのにあなたのシェルを使いません。つまり:
# 誤り —— シェルなし、~ は展開されず、PATH検索もなし
ExecStart=~/app/run.sh
# 正しい
ExecStart=/opt/app/run.sh そしてスクリプト自体が実行可能で、本物のshebang(#!/bin/bash か #!/usr/bin/env bash)で始まる必要があります。ターミナルでは完走するのにsystemd下では203で落ちるスクリプトは、ほぼ必ず次のどれかです: 実行可能でない、CRLF行末、行き先のないshebang、相対パス。
手動では起動するのにブート時は起動しないのはなぜ?
定番の順序バグです。ログがブート直後の失敗を示すのに、手動の systemctl start では問題なく立つなら、サービスはレースに負けています——ネットワーク、マウント済みディスク、データベースを、その物が存在する前に掴みに行っています。
願う代わりに依存を宣言するのが直し方です:
[Unit]
After=network-online.target postgresql.service
Wants=network-online.target
[Service]
ExecStartPre=/usr/bin/test -f /opt/app/config.toml After= が起動順を決め、Wants= がsystemdに依存を実際に上げさせます。network-online.target はdistroでネットワーク待ちサービスが有効な時だけ機能するので、systemctl is-enabled NetworkManager-wait-online.service(またはsystemd-networkdの相当物)を確認してください。ExecStartPre= のガードは、スタックトレースの代わりに読めるメッセージで大声で落ちてくれる、安くて誠実な方法です。
亜種がもう1つ: ブートでは起動するが即座に死ぬパターン。対話環境にあってブートにないものを探します——PATH の違い、virtualenv、HOME。必要なものは明示的に設定します:
[Service]
Environment="PATH=/opt/app/venv/bin:/usr/bin"
User=appuser
WorkingDirectory=/opt/app サービスがjournalにログを出さないのはなぜ?
journalctl -u に何も出ないなら、順に3つ確認します:
- unit内の
StandardOutput=とStandardError=——journal(デフォルト)かjournal+consoleである必要があります。誰かがnullにしているかもしれません。 - アプリがstdoutではなくファイルへ書いている。Systemdが捕捉するのはstdout/stderrだけです。ログファイル書き出しはjournalを完全に迂回します。アプリをstdoutへ向けるか、ファイルを読む。
- ストレージ上限が古い行を落とした:
journalctl --disk-usage、journalが詰まっているなら/etc/systemd/journald.confのSystemMaxUse=。
起動自体のデバッグでは、ブート時コンテキストへシェルを落とすのに勝つものはありません:
[Service]
ExecStart=/bin/bash -c 'exec /opt/app/bin/server 2>&1' または、本物の謎には、unitのユーザーとしてシェルで正確な ExecStart= コマンドを実行します——環境差の大半は最初の10秒で表面化します。
クラッシュ後に自動で再起動させるには?
デフォルトポリシーは Restart=no です: 落ちたサービスは死んだまま、あなたはユーザーから知らされます。サービスごとに直しましょう:
[Service]
Restart=on-failure
RestartSec=5 | 設定 | 再起動する条件 |
|---|---|
no(デフォルト) | 決して |
on-failure | 非ゼロ終了、シグナル、タイムアウト |
always | あらゆる終了、正常終了でも |
on-watchdog | watchdogタイムアウト時のみ |
Restart=on-failure には開始レートのガードを合わせます。クラッシュループがボックスを叩き続けないように: [Unit] セクションの StartLimitIntervalSec= と StartLimitBurst=。60秒に5回の失敗はCPUを回すのではなく、人間を呼ぶべきです。
デーモンではなく定期ジョブとして組むなら、先に cron vs systemd timer を天秤に——タイマーはjournalログと依存の順序を無料でくれます。この記事が何度も手を伸ばしているのはまさにそれです。
60秒のチェックリスト
systemctl status <unit>—— 状態と直近の行を読む。journalctl -u <unit> -n 50 --no-pager—— 本物のエラーを探す。203/EXEC? パス、shebang、パーミッションを直す。Permission denied? ユーザーとファイル所有権を直す。- ブート時だけ落ちる?
After=/Wants=network-online.targetとExecStartPre=ガードを足す。 - unitファイルを変えた?
systemctl daemon-reload && systemctl restart <unit>。 Restart=on-failureを足す。次のクラッシュは隠れず、自分から名乗りを上げます。
「systemdは複雑」という瞬間の大半は、エラーは最初からjournalの中にあったことへ収束します。unitファイルを編集する前に読む。後からではありません。
— mrsaynothing
Get the next one by email
One email per post. No spam, no algorithms.
what is this?記事を楽しんでいただけましたか?私の本業はこのような構築です。 採用のご相談