バッチ処理や自前デーモンが途中でクラッシュしても、systemdのRestartが使えないケースでは誰も検知できません。
この記事では、シェルスクリプトで
kill -0 を使いプロセスの死活を確認し、停止時に自動再起動するwatchdog設計の実践手順を解説します。再起動回数の上限管理、メールアラート通知、cronでの定期実行と二重起動防止を組み合わせた堅牢な実装パターンを、実機ログを交えて紹介します。
この記事のポイント
・kill -0 $PID でプロセスの存在を確認し、停止時に自動再起動できる
・再起動回数をファイルで上限管理し、連続失敗時はアラートを送って自動収束させる
・cron実行時はflock -n でwatchdog自身の二重起動を防ぐ設計が必須
・systemd Restart設定で代替できる場合は必ずそちらを優先する
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
なぜシェルスクリプトのwatchdogが必要なのか|systemdで代替できないケースとは
Linuxサーバーでプロセスを常時稼働させる手段として、systemdのUnit設定が最もシンプルです。Restart=always を記述するだけで、プロセスが終了するたびに自動再起動してくれます。しかし、次のようなケースではsystemdだけでは対応が難しく、シェルスクリプトによるwatchdogが現場で必要になります。
・サードパーティのデーモンがsystemdで管理されていない場合:古いSysVスクリプト起動のプロセスや、単純に
nohup で動かしているバッチ・プロセスが「終了」ではなく「フリーズ」する場合:プロセスは生きているが応答しない状態では、systemd Restartは発動しない
・外部ベンダーのスクリプトをラップして監視したい場合:Unitファイルを変更できない環境
・複数プロセスを横断して一括監視したい場合:1本のwatchdogスクリプトで5つのバッチを監視するなど
これらのケースでは、cronで定期的に起動するwatchdogスクリプトが有効な選択肢になります。
プロセスの死活確認を実装する|kill -0・pgrep・pidfileの使い分け
死活確認の手段はいくつかあります。それぞれの特性を理解した上で使い分けることが設計の第一歩です。1. kill -0 によるPID確認
kill -0 はシグナルを実際には送らず、「そのPIDのプロセスが存在し、かつ自分にkillできる権限があるか」だけを確認するコマンドです。# PID 12345 のプロセスが存在するか確認 kill -0 12345 2>/dev/null && echo "running" || echo "not running" # スクリプト内での使い方 is_running() { local pid pid=$(cat "${PIDFILE}" 2>/dev/null) || return 1 kill -0 "${pid}" 2>/dev/null }
2>/dev/null で「No such process」のエラーメッセージを抑制するのが定石です。2. pgrep によるプロセス名確認
PIDファイルを持たないプロセスの場合、プロセス名で死活を確認できます。# プロセス名 myapp が存在するか確認 pgrep -x myapp > /dev/null 2>&1 && echo "running" || echo "not running" # -x はプロセス名の完全一致。部分一致だと誤検知する pgrep -f "myapp --daemon" > /dev/null 2>&1
3. systemctl is-active によるサービス確認
systemd管理下のサービスであれば、より正確な確認ができます。# サービスが active かどうか確認 systemctl is-active --quiet myapp.service && echo "active" || echo "inactive" # 終了コード: 0=active, 1~3=inactive/failed/activating
4. 手法の比較表
| 手法 | 確認方法 | 適した場面 | 注意点 |
|---|---|---|---|
| kill -0 | kill -0 $PID 2>/dev/null |
PIDファイルがある場合 | 権限が必要、PID再利用リスク |
| pgrep | pgrep -x プロセス名 |
PIDファイルなし | 同名プロセス複数時に誤検知 |
| systemctl | systemctl is-active サービス名 |
systemd管理プロセス | 非systemdには使えない |
基本的なwatchdogスクリプトの設計|最小構成から始める
まずは「プロセスが落ちていたら再起動する」最小構成を作ります。#!/bin/bash # /usr/local/sbin/watchdog-myapp.sh # myapp プロセスを監視して自動再起動する watchdog スクリプト set -euo pipefail PROCESS_NAME="myapp" START_CMD="/opt/myapp/bin/myapp --daemon --config /etc/myapp/myapp.conf" PIDFILE="/var/run/myapp.pid" LOGFILE="/var/log/watchdog-myapp.log" log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" >> "${LOGFILE}" } is_running() { local pid pid=$(cat "${PIDFILE}" 2>/dev/null) || return 1 kill -0 "${pid}" 2>/dev/null } if is_running; then log "OK: ${PROCESS_NAME} is running (PID=$(cat "${PIDFILE}"))" exit 0 fi log "WARN: ${PROCESS_NAME} is not running. Restarting..." ${START_CMD} log "INFO: ${PROCESS_NAME} restart command executed."
is_running() 関数です。cat "${PIDFILE}" が失敗した場合(PIDファイルなし)は即座に return 1(停止とみなす)し、成功した場合は kill -0 でPIDのプロセスが本当に存在するか確認します。PIDファイルが残ったままプロセスが消えた「ゾンビPIDファイル」状態も、この設計ならきちんと「停止」と判定できます。
シェルスクリプト講座を見る >>
再起動回数の上限とアラート通知を実装する
最小構成では「何度でも再起動し続ける」ため、本当に壊れているプロセスがある場合に無限に再起動しようとして状況が悪化します。再起動上限とアラート通知を組み込んだ実装が現場では必須です。#!/bin/bash # /usr/local/sbin/watchdog-myapp.sh(上限・アラート付き版) set -euo pipefail PROCESS_NAME="myapp" START_CMD="/opt/myapp/bin/myapp --daemon --config /etc/myapp/myapp.conf" PIDFILE="/var/run/myapp.pid" LOGFILE="/var/log/watchdog-myapp.log" COUNTER_FILE="/tmp/watchdog-myapp-restarts" MAX_RESTARTS=3 ALERT_TO="ops-team@example.com" log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "${LOGFILE}" } send_alert() { local subject="$1" local body="$2" echo "${body}" | mail -s "${subject}" "${ALERT_TO}" 2>/dev/null || \ log "WARN: mail command failed. Install mailx or configure sendmail." } is_running() { local pid pid=$(cat "${PIDFILE}" 2>/dev/null) || return 1 kill -0 "${pid}" 2>/dev/null } if is_running; then # 正常稼働中 → カウンタをリセット echo 0 > "${COUNTER_FILE}" log "OK: ${PROCESS_NAME} is running (PID=$(cat "${PIDFILE}"))" exit 0 fi # 再起動カウンタを読み取り、インクリメント count=$(cat "${COUNTER_FILE}" 2>/dev/null || echo 0) count=$(( count + 1 )) echo "${count}" > "${COUNTER_FILE}" # 上限超過 → アラート送信して終了 if (( count > MAX_RESTARTS )); then log "ERROR: ${PROCESS_NAME} has failed ${count} times. Giving up. Manual check required." send_alert \ "[ALERT] ${PROCESS_NAME} on $(hostname) repeated failures" \ "${PROCESS_NAME} stopped and failed to recover ${count} times on $(hostname). Please check immediately." exit 1 fi log "WARN: ${PROCESS_NAME} stopped. Restart attempt ${count}/${MAX_RESTARTS}..." ${START_CMD} log "INFO: ${PROCESS_NAME} restart command executed (attempt ${count})."
・カウンタファイル(COUNTER_FILE)の使い方:プロセスが正常稼働している間はカウンタを0にリセットし続けます。再起動が必要になるたびにインクリメントし、MAX_RESTARTSを超えたら「これ以上は人間が対処すべき状態」と判断してアラートを送ります。
・アラート後のexit 1:上限超過後はスクリプト自身もエラー終了します。cronのMAILTO設定と合わせることで、cronからもメール通知が届く二重通知になります。
実機ログの出力例を示します。
# 実際のログ出力例(/var/log/watchdog-myapp.log) [2026-09-01 03:00:01] WARN: myapp stopped. Restart attempt 1/3... [2026-09-01 03:00:03] INFO: myapp restart command executed (attempt 1). [2026-09-01 03:05:01] OK: myapp is running (PID=18234) [2026-09-01 03:10:01] OK: myapp is running (PID=18234) [2026-09-01 03:15:01] WARN: myapp stopped. Restart attempt 1/3... [2026-09-01 03:15:02] INFO: myapp restart command executed (attempt 1). [2026-09-01 03:20:01] WARN: myapp stopped. Restart attempt 2/3... [2026-09-01 03:20:02] INFO: myapp restart command executed (attempt 2). [2026-09-01 03:25:01] WARN: myapp stopped. Restart attempt 3/3... [2026-09-01 03:25:02] INFO: myapp restart command executed (attempt 3). [2026-09-01 03:30:01] ERROR: myapp has failed 4 times. Giving up. Manual check required.
cronで定期実行するwatchdog構成|flock と PATH の設定
watchdogスクリプトをcronに登録する際には、次の2点に必ず対処してください。1. flock で二重起動を防ぐ
cronは「前回の実行が終わっているかどうかに関係なく」スケジュール通り起動します。watchdogスクリプト自身の実行時間が5分を超えた場合(再起動待ちで長時間かかるケースなど)、複数インスタンスが同時に走る危険があります。flock -n で防ぎます。# /etc/cron.d/watchdog-myapp # 5分ごとに watchdog を実行する MAILTO="ops-team@example.com" PATH=/usr/local/sbin:/usr/sbin:/sbin:/usr/local/bin:/usr/bin:/bin */5 * * * * root /usr/bin/flock -n /tmp/watchdog-myapp.lock /usr/local/sbin/watchdog-myapp.sh
flock -n の -n は「ロックが取れなければ即座に終了(待機しない)」オプションです。前回の実行が終わっていなければ今回の起動をスキップします。2. PATH をスクリプト内でも明示する
cronの実行環境はログインシェルと異なり、PATHが非常に短くなっています。crontabのPATH= に加えて、スクリプト冒頭でも明示しておくと確実です。#!/bin/bash # スクリプト冒頭に PATH を明示(cron 環境では /usr/local/sbin 等が通っていないことが多い) export PATH=/usr/local/sbin:/usr/sbin:/sbin:/usr/local/bin:/usr/bin:/bin set -euo pipefail # ... 以降 watchdog 本体
3. MAILTO の設定
MAILTO= の設定が空だとcronの標準出力・標準エラーがどこにも届きません。watchdogスクリプトのexit 1(異常終了)をcronメールで受け取るために、運用チームのメールアドレスを必ず設定してください。トラブルシュート|watchdogが誤動作する典型パターン
1. PIDファイルが残ったまま別のプロセスが同じPIDを使っている
Linuxカーネルはプロセス終了後、そのPIDを再利用することがあります。古いPIDファイルを持つwatchdogがkill -0 を実行すると、まったく関係ないプロセスが「生きている」と誤判定します。対策として、PIDファイルの更新タイムスタンプとプロセス起動時間を合わせて確認する方法があります。
is_running() { local pid pid=$(cat "${PIDFILE}" 2>/dev/null) || return 1 # プロセスが存在するか kill -0 "${pid}" 2>/dev/null || return 1 # プロセス名も確認(PID再利用対策) local proc_name proc_name=$(ps -p "${pid}" -o comm= 2>/dev/null) || return 1 [[ "${proc_name}" == "${PROCESS_NAME}" ]] }
2. watchdogスクリプト自体が途中でエラー終了する
set -euo pipefail を設定していると、意図しないコマンドエラーでwatchdog自身が終了することがあります。is_running() 内の return 1 は正常動作ですが、ログ書き込みに失敗した場合なども含めてテストしてください。# watchdog スクリプトを手動でデバッグ実行する bash -x /usr/local/sbin/watchdog-myapp.sh 2>&1 | head -50 # cron 経由での動作確認(run-parts 相当) /usr/bin/flock -n /tmp/watchdog-myapp.lock /usr/local/sbin/watchdog-myapp.sh echo "Exit code: $?"
3. pgrep で同名プロセスを誤検知する
監視対象とは別に同名プロセスが走っている場合、pgrep方式では正常と誤判定します。PIDファイル方式に切り替えるか、pgrep -f でコマンドライン全体(オプションを含む)をパターン指定することで誤検知を減らせます。# プロセス名の完全一致(デフォルト) pgrep -x myapp # コマンドライン全体でのパターンマッチ(-f オプション) pgrep -f "myapp --daemon --config /etc/myapp/myapp.conf"
シェルスクリプト講座を見る >>
本記事のまとめ
シェルスクリプトでwatchdogを設計するポイントは、単に「再起動する」だけでなく「いつ諦めるか」と「誰に知らせるか」を最初から設計に組み込むことです。再起動が成功しても根本原因が残っていれば何度でも繰り返すため、カウンタ上限を超えたらアラートを送って人間に判断を委ねる設計が現場での鉄則になります。| やりたいこと | コマンド・設計パターン |
|---|---|
| PIDでプロセス存在確認 | kill -0 $PID 2>/dev/null |
| プロセス名で確認 | pgrep -x プロセス名 |
| コマンドライン全体で確認 | pgrep -f "プロセス名 オプション" |
| systemdサービスの確認 | systemctl is-active --quiet サービス名 |
| 再起動回数の上限管理 | カウンタファイルをインクリメント、正常時は0にリセット |
| 連続失敗時のアラート | echo 本文 | mail -s 件名 宛先 |
| cron二重起動防止 | flock -n /tmp/lock.file スクリプト |
| cron環境のパス問題 | crontabとスクリプト冒頭の両方でPATHを明示 |
3,100名以上が実践した「型」を無料で公開中
プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら
- 前のページへ:シェルスクリプトの入力バリデーション設計|型・形式チェック関数をライブラリ化して複数スクリプトで再利用する方法
- この記事の属するカテゴリ:シェルスクリプトへ戻る

無料メルマガで学習を続ける
Linuxの実践スキルをメールで毎週お届け。
登録は30秒、解除もいつでも可。
登録無料・いつでも解除できます