🐚
常駐バッチ運用で学んだ「壊れにくい」シェルスクリプトの書き方
Java の常駐バッチを運用する中で、 「プロセスが落ちたら自動で起動し、
動いているけど処理が止まっていたら異常として検知する」
ための ヘルスチェック用シェルスクリプト を実装しました。
(シェルスクリプト初心者だったため、余裕だろと舐めてたら案外苦戦、、、笑)
なんとか実装してみると、このシェルには 実務で必要な“シェルの基本”がかなり詰まっている ことに気づいたので、設計思想とポイントをまとめます!!!
目的
- Java の常駐バッチを監視する
- プロセスが存在しない場合は再起動する(起動用のシェルは別途作成)
- プロセスが存在しても、処理が止まっている場合は異常と判定する
- 異常時はログに ERROR を出力し、運用監視(メール通知等)で検知できるようにする
全体構成
-
本体バッチ
Java で動作する常駐プロセス -
起動用シェル
バッチを起動する専用スクリプト -
ヘルスチェック用シェル(今回の主役)
cron から定期実行し、状態を確認・制御する
ポイントは「起動処理」と「監視処理」を分離すること。
実装例(抜粋)
#!/bin/bash
PROCESS_NAME="{対象処理のプロセス名}"
BATCH_ID="$1"
LOG_DIR="/{ログディレクトリまでのパス}/log"
BATCH_LOG="${LOG_DIR}/{監視対象の処理のログファイル名}"
check_process() {
PROCESS_COUNT=$(ps aux | grep -v grep | grep "$PROCESS_NAME" | grep "$BATCH_ID" | wc -l)
# プロセスが存在しない場合
if [ "$PROCESS_COUNT" -eq 0 ]; then
{常用バッチ起動用のシェルの実行}
echo "[INFO] 起動コマンドをバックグラウンドで実行" >> "$BATCH_LOG"
return 0
fi
# ログ更新時刻でハング検知
if [ -f "$BATCH_LOG" ]; then
LAST_MODIFIED=$(stat -c %Y "$BATCH_LOG" 2>/dev/null)
FIVE_MIN_AGO=$(($(date +%s) - 300))
if [ "$LAST_MODIFIED" -lt "$FIVE_MIN_AGO" ]; then
echo "[ERROR] ログが5分以上更新されていないため異常と判定" >> "$BATCH_LOG"
return 1
fi
else
echo "[ERROR] ログファイルが存在しない" >> "$BATCH_LOG"
return 1
fi
return 0
}
main() {
if check_process; then
exit 0
else
exit 1
fi
}
main "$@"
設計・実装のポイント
1. シェルでは「0 = 正常、非0 = 異常」
return 0 # 正常(true)
return 1 # 異常(false)
-
if command; then ...は 終了コードが 0 のとき に then に入る - 他言語(true/false)と感覚が逆なので注意
2. return と exit の使い分け
-
return:関数の結果 -
exit:スクリプト全体の終了コード
3. ログ更新時刻で「生存確認」を行う
stat -c %Y "$BATCH_LOG"
4. 起動処理は「成功判定しない」
バックグラウンド実行後の $? は、
Java の起動成否を表さない点に注意。
5. 標準入出力をすべて制御する
>> "$BATCH_LOG" 2>&1 < /dev/null
6. テストでは「壊さない」ことを最優先
更新時間系のテストは、touchコマンドで更新日時を操作して対応
touch -d "10 minutes ago" create-batch-XXX.log
7. cron はシンプルに書く
今回は5分おきにヘルスチェック
*/5 * * * * /{シェルまでのパス}/{シェルスクリプト名} {チェック対象のバッチのID}
まとめ
本記事で紹介したシェルスクリプトには、
実務で必須となるシェルの基本要素が一通り含まれているなと実感しました。
「とりあえず動く」ではなく、
本番で壊れにくい設計を意識することが重要です。
おわりに
同じように常駐バッチや監視シェルを書くことになったけど、シェル初心者だよという方々の参考になれば幸いです!!
Discussion