【Bash】サーバー死活監視&Slack通知スクリプト自作ガイド|curlとsystemctl連携による自動再起動と無人運用

スクリプト設計実務レシピ

年末年始の長期休暇や冬期休業、ゴールデンウィークなどの連休前になると、インフラエンジニアやWebマスター、社内情シス担当者の頭を悩ませるのが 「休暇中のサーバー無人運用における障害対策」 です。

「休暇中にWebサーバー(Nginx / Apache)がサイレントダウンして気付けない」「夜間や休日にアクセス集中やメモリリークでプロセスが落ちたのに、休み明けに出社するまで誰も知らなかった」――このような事態は、企業の信頼性低下や機会損失に直結するインフラ現場最大の悪夢です。

もちろん、Datadog、New Relic、Mackerel、Zabbixなどの本格的な監視SaaSや監視サーバーを導入できれば理想的です。しかし、「ステージング環境や社内ツール、受託開発の小規模サーバーに有償SaaSを入れる予算がない」「エージェントの導入や監視サーバー構築に何日も工数をかけられない」という現場も少なくありません。

そんなときに圧倒的な威力を発揮するのが、Linux標準の Bash・curl・cron・systemctl のみで完結する 「自作の軽量死活監視&Slack通知スクリプト」 です。追加ミドルウェアのインストールは一切不要で、わずか10分で「HTTP死活監視」「3回リトライ」「Slackへのリッチな障害通知」「systemctlによる自動再起動」「連続アラート防止(クーリングダウン)」までを備えた堅牢な自動復旧環境が手に入ります。

こんにちは、 Bash玄(ばっしゅげん) です。当サイトのモットーは「手作業は負け」「スクリプトはシンプルに」。本記事では、コピペで即動くWebサーバー死活監視スクリプトの完成コードから、curlの軽量ステータス取得テクニック、誤検知を防ぐタイムアウトとリトライ設計、systemctlによる自動再起動、cronへの安全な登録手順まで、現場目線で余すところなく徹底解説します。

  1. 3分で動く!コピペで使えるWebサーバー死活監視&Slack通知スクリプト完成コード
    1. スクリプトの設定パラメータ早見表
    2. Slack Incoming WebhookへのJSONペイロード送信ワンライナー
  2. スクリプトの重要設計ポイントと実務ノウハウ
    1. curl -s -o /dev/null -w “%{http_code}” による軽量ステータスコード取得の仕組み
    2. ネットワーク遅延による誤検知を防ぐタイムアウト設定(–connect-timeout / –max-time)
    3. 一時的な瞬断を無視する「N回連続失敗でアラート発報」のリトライロジック設計
  3. 死活監視から自動復旧へ!systemctl連携によるプロセス自動再起動
    1. Webサーバー(Nginx / Apache)やバックエンドサービスの自動再起動コマンド
      1. 一般ユーザーに最小権限で再起動を許可するsudoers設定
    2. 再起動成功時・失敗時それぞれでSlack通知メッセージを分岐させる方法
  4. cronへの登録と年末年始の安全運用設定
    1. crontabでの5分間隔定期実行設定
      1. 現場で頻出する「cron環境のPATH問題」の回避策
    2. スクリプト多重起動を防止する排他制御(flock コマンドの実装)
    3. 連続アラート地獄(Slackスパム)を防ぐクーリングダウン(フラグファイル管理)ロジック
    4. ログ肥大化を防ぐlogrotateの設定
  5. まとめ:ZabbixやDatadogを入れる前の「最速・軽量」自前監視のベストプラクティス
    1. 年末年始・休暇前の安全点検チェックリスト
    2. あわせて読みたい関連記事
  6. 関連記事

3分で動く!コピペで使えるWebサーバー死活監視&Slack通知スクリプト完成コード

まずはファーストビューで、本番環境やステージングサーバーにそのまま配備して使える 「Webサーバー死活監視&Slack自動通知・自動再起動スクリプト」 の完成コードを提示します(Pattern #20準拠・アンサーファースト)。

#!/usr/bin/env bash
# ==============================================================================
# Webサーバー死活監視 & Slack通知 & 自動復旧スクリプト
# ==============================================================================
set -euo pipefail

# ------------------------------------------------------------------------------
# 1. 運用設定パラメータ(環境に合わせて変更してください)
# ------------------------------------------------------------------------------
TARGET_URL="https://example.com/healthcheck"     # 監視対象URL
SERVICE_NAME="nginx"                             # 自動再起動するsystemdサービス名
SLACK_WEBHOOK_URL="https://hooks.slack.com/services/T00/B00/XXXXX" # Slack Webhook URL

MAX_RETRIES=3                                    # 失敗とみなす連続試行回数
RETRY_INTERVAL=3                                 # リトライ間隔(秒)
CONNECT_TIMEOUT=5                                # 接続タイムアウト(秒)
MAX_TIME=10                                      # レスポンス完了タイムアウト(秒)

# 状態管理・ログファイル
LOG_FILE="/var/log/server_healthcheck.log"
ALERT_FLAG_FILE="/var/run/healthcheck_${SERVICE_NAME}.alert"

# ------------------------------------------------------------------------------
# 2. ロガー関数と通知ヘルパー
# ------------------------------------------------------------------------------
log_msg() {
  local level="$1"
  shift
  echo "[$(date '+%Y-%m-%d %H:%M:%S')] [${level}] $*" | tee -a "${LOG_FILE}"
}

send_slack_notification() {
  local color="$1"
  local title="$2"
  local message="$3"

  local hostname
  hostname="$(hostname)"

  local payload
  payload=$(cat <<EOF
{
  "attachments": [
    {
      "color": "${color}",
      "title": "${title}",
      "text": "${message}",
      "fields": [
        { "title": "対象サーバー", "value": "${hostname}", "short": true },
        { "title": "監視対象URL", "value": "${TARGET_URL}", "short": true },
        { "title": "検知時刻", "value": "$(date '+%Y-%m-%d %H:%M:%S')", "short": false }
      ]
    }
  ]
}
EOF
)

  # Slack Incoming Webhookへ送信(失敗しても監視スクリプト自体は止めない)
  curl -s -X POST -H 'Content-type: application/json' --data "${payload}" "${SLACK_WEBHOOK_URL}" > /dev/null 2>&1 || true
}

# ------------------------------------------------------------------------------
# 3. HTTPヘルスチェック(リトライ付き)
# ------------------------------------------------------------------------------
check_health() {
  local status_code="000"

  for ((i = 1; i <= MAX_RETRIES; i++)); do
    # レスポンス本文を破棄し、HTTPステータスコードのみを取得
    status_code="$(curl -s -o /dev/null -w "%{http_code}" \
      --connect-timeout "${CONNECT_TIMEOUT}" \
      --max-time "${MAX_TIME}" \
      "${TARGET_URL}" || echo "000")"

    if [[ "${status_code}" =~ ^(200|301|302)$ ]]; then
      echo "${status_code}"
      return 0
    fi

    log_msg "WARN" "試行 ${i}/${MAX_RETRIES}: 異常なHTTPコード (${status_code})。${RETRY_INTERVAL}秒後に再試行します..."
    sleep "${RETRY_INTERVAL}"
  done

  echo "${status_code}"
  return 1
}

# ------------------------------------------------------------------------------
# 4. メイン監視・自動復旧ロジック
# ------------------------------------------------------------------------------
main() {
  local final_status="000"
  
  if final_status="$(check_health)"; then
    # --- 正常時の処理 ---
    # 直前までアラート状態だった場合は「復旧通知(Recovery)」を送信
    if [[ -f "${ALERT_FLAG_FILE}" ]]; then
      log_msg "INFO" "サービスが正常に復旧しました (HTTP ${final_status})。Slackへ復旧通知を送信します。"
      send_slack_notification "good" "【復旧】Webサービス正常化" "Webサーバーの死活監視が正常状態(HTTP ${final_status})に戻りました。"
      rm -f "${ALERT_FLAG_FILE}"
    fi
    exit 0
  fi

  # --- 異常検知時の処理 ---
  log_msg "ERROR" "ヘルスチェックが ${MAX_RETRIES} 回連続で失敗しました (最終HTTPコード: ${final_status})。"

  # 自動再起動の試行
  log_msg "INFO" "systemctl restart ${SERVICE_NAME} を実行して自動復旧を試みます..."
  local restart_result="SUCCESS"
  if sudo systemctl restart "${SERVICE_NAME}"; then
    log_msg "INFO" "${SERVICE_NAME} の再起動コマンドが成功しました。"
    sleep 3

    # 再起動後の再確認
    local post_restart_status
    post_restart_status="$(curl -s -o /dev/null -w "%{http_code}" --connect-timeout 3 --max-time 5 "${TARGET_URL}" || echo "000")"

    if [[ "${post_restart_status}" =~ ^(200|301|302)$ ]]; then
      log_msg "INFO" "再起動後にHTTP ${post_restart_status} を確認。自動復旧に成功しました。"
      
      # 初回アラート時のみSlack通知(クーリングダウン制御)
      if [[ ! -f "${ALERT_FLAG_FILE}" ]]; then
        send_slack_notification "warning" "【自動復旧完了】${SERVICE_NAME} 再起動成功" "ヘルスチェック失敗(HTTP ${final_status})を検知しましたが、${SERVICE_NAME} を自動再起動し正常復旧しました(現ステータス: HTTP ${post_restart_status})。"
        touch "${ALERT_FLAG_FILE}"
      fi
      exit 0
    else
      restart_result="FAILED_TO_SERVE"
    fi
  else
    restart_result="RESTART_CMD_FAILED"
  fi

  # 再起動後も復旧しなかった場合の重大アラート(CRITICAL)
  log_msg "CRITICAL" "${SERVICE_NAME} の自動再起動後もサービスが回復しませんでした。"
  
  # 初回のみ、または重大アラートは確実に通知
  if [[ ! -f "${ALERT_FLAG_FILE}" ]]; then
    send_slack_notification "danger" "【至急対応】Webサービス障害検知・自動復旧失敗" "ヘルスチェック失敗(HTTP ${final_status})後、${SERVICE_NAME} の自動再起動を試みましたが復旧しませんでした(結果: ${restart_result})。手動での緊急調査が必要です。"
    touch "${ALERT_FLAG_FILE}"
  fi

  exit 1
}

main "$@"

スクリプトの設定パラメータ早見表

上記スクリプトは、冒頭の設定変数を書き換えるだけであらゆる環境に即座に適合させることができます。

変数名デフォルト値設定内容と現場での推奨値
TARGET_URLhttps://example.com/healthcheck死活監視対象のURL。できればDB疎通や主要APIを簡易確認できるヘルスチェック専用エンドポイントが理想です。
SERVICE_NAMEnginx障害検知時に自動再起動する systemd ユニット名(nginx, apache2, httpd, docker, pm2 等)。
SLACK_WEBHOOK_URLhttps://hooks.slack.com/...通知先Slackチャンネルの Incoming Webhook URL。
MAX_RETRIES3障害と判定するまでの連続試行回数。一時的なネットワーク揺らぎによる誤検知を防ぐため 3 回を推奨。
RETRY_INTERVAL3リトライまでの待機秒数(sleep 時間)。実務では 3〜5 秒が標準的です。
CONNECT_TIMEOUT5TCP接続確立までの上限タイムアウト(秒)。サーバー無応答のハングを即座に切るため 5 秒を推奨。
MAX_TIME10転送全体の完了上限タイムアウト(秒)。重いクエリによる遅延を検知するため 10 秒程度に設定します。
ALERT_FLAG_FILE/var/run/healthcheck_*.alert多重アラート(Slackスパム)を防止するための状態管理フラグファイル。

Slack Incoming WebhookへのJSONペイロード送信ワンライナー

「まずは手元のターミナルからSlackへ通知が飛ぶかどうかを今すぐテストしたい」という場合は、以下の curl ワンライナーを実行してください。

# Slack通知テスト用ワンライナー(WEBHOOK_URLを実際のURLに置き換えて実行)
WEBHOOK_URL="https://hooks.slack.com/services/T00/B00/XXXXX"
curl -s -X POST -H 'Content-type: application/json' \
  --data '{"text":"[TEST] サーバー死活監視のSlack通知疎通テストに成功しました。"}' \
  "${WEBHOOK_URL}"

Slackチャンネルに「ok」というレスポンスとともにテストメッセージが表示されれば、Incoming Webhookの準備は完了です。Incoming Webhook URLは、Slackの「App(インテグレーション)」管理画面から「Incoming Webhooks」を追加し、通知先チャンネルを選択することで数十秒で発行できます。

スクリプトの重要設計ポイントと実務ノウハウ

死活監視スクリプトを「本番サーバーで安心して放置できるレベル」に仕上げるためには、シェルスクリプト特有の落とし穴を塞ぐ設計が不可欠です。現場のエンジニアが特に重視する3つの設計ポイントを深掘りします。

curl -s -o /dev/null -w “%{http_code}” による軽量ステータスコード取得の仕組み

死活監視でWebサーバーにアクセスする際、最も避けなければならないのが 「重いHTML本文や画像・アセットを毎回すべてダウンロードしてネットワーク帯域やメモリを浪費すること」 です。

スクリプト内で使用している以下のコマンドは、監視における「世界標準の黄金コンビネーション」です。

status_code="$(curl -s -o /dev/null -w "%{http_code}" "https://example.com/")"
echo "HTTPステータス: ${status_code}"
  • -s(–silent) : プログレスメーター(進捗バー)やcurl独自のエラー出力を非表示にし、標準出力をクリーンに保ちます。
  • -o /dev/null(–output /dev/null) : サーバーから返されたHTTPレスポンスボディ(HTML等の本文)をすべて /dev/null に破棄します。これにより、どれだけ巨大なWebページであってもスクリプト側のメモリを一切消費しません。
  • -w "%{http_code}"(–write-out) : リクエスト完了後に、通信結果に関するカスタム変数を標準出力へ書き出します。%{http_code} を指定することで、「200」「404」「500」「502」といった3桁の数字だけをスマートに変数へ格納できます。

なぜ curl -I(HEADリクエスト)を使わないのか?
「ヘッダーだけ取得するなら curl -I(HEADメソッド)の方が軽いのでは?」と考える方も多いでしょう。しかし実務の現場では、WebサーバーやCloudflareなどのCDN、WAF、ロードバランサーの設定によって 「HEADリクエストに対して 405 Method Not Allowed や 403 Forbidden を返す」 ケースが頻繁に発生します。本番環境で誤検知を起こさないためには、通常の GET リクエストを送りつつ -o /dev/null で本文を破棄する手法が最も確実で安全です。

ネットワーク遅延による誤検知を防ぐタイムアウト設定(–connect-timeout / –max-time)

デフォルト状態の curl コマンドには、 「通信タイムアウトの上限が設定されていない(OSのTCPタイムアウトに依存し、数分間ハングし続ける)」 という致命的な落とし穴があります。cronで定期実行している場合、タイムアウトを設定していないとスクリプトが滞留し、プロセスが雪だるま式に増殖してサーバーのメモリを食い潰してしまいます。

そのため、必ず 2種類のタイムアウト を明示的に指定します。

curl -s -o /dev/null -w "%{http_code}" \
  --connect-timeout 5 \
  --max-time 10 \
  "https://example.com/"
  • --connect-timeout 5 : 相手先サーバーとの 「TCPコネクション確立(3-way handshake)」および「SSL/TLSハンドシェイク」までの上限秒数 です。Webサーバー自体が完全に死滅している場合やネットワーク経路が途絶している場合、この5秒で即座にタイムアウト判定を下します。
  • --max-time 10(または -m 10) : コネクション確立からデータ転送が完了するまでの 「リクエスト全体の総実行時間の上限秒数」 です。サーバープロセスは生きているものの、バックエンドDBのデッドロックや高負荷によってレスポンスが極端に遅延している場合、10秒で強制打ち切りとして検知します。

一時的な瞬断を無視する「N回連続失敗でアラート発報」のリトライロジック設計

インターネット上の通信環境では、定期的なデプロイによる数ミリ秒の再起動、瞬間的なパケットロス、回線のジッターなどによって「1回だけ通信が失敗する」ことは日常茶飯事です。

もし1回の失敗で即座にSlack通知を飛ばしてしまうと、夜間や年末年始の休暇中に「誤アラート」が頻発し、運用担当者が通知に慣れて本当の障害を見落とす 「オオカミ少年症候群(アラート疲れ)」 に陥ります。

そのため、スクリプトには以下のような 「3回連続失敗で初めて異常と判定するリトライループ」 を組み込みます。

# リトライループの基本設計
MAX_RETRIES=3
RETRY_INTERVAL=3
status_code="000"

for ((i = 1; i <= MAX_RETRIES; i++)); do
  status_code="$(curl -s -o /dev/null -w "%{http_code}" \
    --connect-timeout 5 --max-time 10 "https://example.com/" || echo "000")"

  # 200 OK またはリダイレクト(301/302)なら成功として即リターン
  if [[ "${status_code}" =~ ^(200|301|302)$ ]]; then
    echo "健全性確認OK (HTTP ${status_code})"
    break
  fi

  echo "[警告] 試行 ${i}/${MAX_RETRIES}: 失敗 (コード: ${status_code})。${RETRY_INTERVAL}秒待機..." >&2
  
  if (( i < MAX_RETRIES )); then
    sleep "${RETRY_INTERVAL}"
  fi
done

このように「3秒間隔で3回リトライ(合計約10〜15秒の猶予)」を設けることで、一時的な瞬断をスマートにいなし、 「本当にサーバーが死んでいる時だけ確実に対応する」 という高い信頼性を確保できます。シェルスクリプトにおける堅牢なエラーハンドリング手法や set -euo pipefail の詳細な効能については、 【Bash】エラーハンドリング完全設計|set -euo pipefailとtrapによる安全なスクリプト終了処理 もあわせてご覧ください。

死活監視から自動復旧へ!systemctl連携によるプロセス自動再起動

単に「Webサイトが落ちました」とSlackに通知するだけでは、結局のところ休暇中の担当者がベッドから飛び起きてノートPCを開き、SSH接続して systemctl restart を叩かなければなりません。

「手作業は負け」を掲げるBash道としては、 「障害を検知したら、その場でスクリプト自身がプロセスを自動再起動して自己修復する」 ところまで自動化してこそ真の無人運用と言えます。

Webサーバー(Nginx / Apache)やバックエンドサービスの自動再起動コマンド

現代のLinux(Ubuntu、Debian、RHEL、Rocky Linux、AlmaLinux等)では、サービスの起動・停止・再起動はすべて systemd(systemctl コマンド)によって一元管理されています。

# プロセスの生存確認(アクティブなら終了コード0、停止中なら非ゼロ)
if ! systemctl is-active --quiet nginx; then
  echo "nginx が停止しています!"
fi

# サービスの再起動
sudo systemctl restart nginx

一般ユーザーに最小権限で再起動を許可するsudoers設定

監視スクリプトを一般ユーザー(例: deploy や monitor)のcronで動かす場合、パスワードなしで sudo systemctl restart nginx を実行できるように権限を付与する必要があります。ただし、全コマンドのsudo権限(NOPASSWD: ALL)を与えてしまうとセキュリティ上の重大な脆弱性になります。

必ず visudo コマンドを用いて、 「対象サービスの再起動のみ」 を許可する安全な設定を追加してください。

# visudo で編集(または /etc/sudoers.d/healthcheck を作成)
sudo visudo -f /etc/sudoers.d/healthcheck

# 以下の1行を追加(monitor ユーザーに nginx の再起動とステータス確認のみパスワード不要で許可)
monitor ALL=(ALL) NOPASSWD: /bin/systemctl restart nginx, /bin/systemctl status nginx

自作のNode.jsやPythonアプリケーションなどをsystemdサービスとして常駐化し、同様に自動復旧させたい場合は、 【Linux】systemd 自作サービスの作成手順まとめ|自動起動・常駐化・ユニットファイル設定を徹底解説 を参照してください。

再起動成功時・失敗時それぞれでSlack通知メッセージを分岐させる方法

自動復旧を導入した場合、Slackに届く通知は 「結果の深刻度」に応じて見た目を完全に差別化 する必要があります。

状況Slackメッセージの色タイトル・緊急度担当者が取るべき行動
再起動で自動復旧成功黄〜オレンジ(warning)【自動復旧完了】nginx 再起動成功対応不要(様子見)。休暇明けにログを確認すればOK。
再起動後も回復せず赤(danger)【至急対応】障害検知・自動復旧失敗至急対応が必要。即座にPCを開いて原因調査に着手。
正常化(復旧確認)緑(good)【復旧】Webサービス正常化安心情報。障害が完全に解消されたことをチームに共有。

Slack Incoming Webhookでは、JSONペイロードの attachments 内にある color フィールドに good(緑)、warning(黄)、danger(赤)、または任意のカラーコード(#36a64f 等)を指定することで、通知バーの色を直感的に変化させることができます。

# Slackのカラー別ペイロード構築例
payload=$(cat <<EOF
{
  "attachments": [
    {
      "color": "danger",
      "title": "【至急対応】サーバー障害・自動復旧失敗",
      "text": "ヘルスチェック失敗後、nginxを再起動しましたがHTTP 502が継続しています。",
      "fields": [
        { "title": "ホスト", "value": "$(hostname)", "short": true },
        { "title": "重要度", "value": "CRITICAL", "short": true }
      ]
    }
  ]
}
EOF
)

このように色分けされていれば、スマートフォンに届いたSlack通知を一瞥するだけで「自動復旧したからそのまま休暇を楽しもう」「これは本物の重大インシデントだから対応しよう」と瞬時に判断できるようになります。

cronへの登録と年末年始の安全運用設定

スクリプトが完成したら、最後に cron へ登録して定期実行を設定します。しかし、ここにも「年末年始の無人運用でサーバーを落とす典型的な落とし穴」が3つ存在します。

crontabでの5分間隔定期実行設定

まずは、死活監視スクリプトを5分ごとに自動実行する標準的な crontab の設定です。

# crontab 編集画面を開く
crontab -e

# 以下の設定行を追加
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
*/5 * * * * /usr/local/bin/server_healthcheck.sh >> /var/log/server_healthcheck.log 2>&1

現場で頻出する「cron環境のPATH問題」の回避策

cronでスクリプトを動かした際、「手動実行だと動くのに、cron経由だと curl: command not found や systemctl: not found で失敗する」という事故が頻発します。

これは、 cronが実行されるシェル環境のデフォルト PATH が極めて限定されている(/usr/bin:/bin 程度しかない)ため です。上記のようにcrontabの先頭で PATH=... を明示的に宣言するか、スクリプト内で /usr/bin/curl のようにコマンドを絶対パスで記述することを徹底しましょう。crontabの構文や動かない時のデバッグ手順は、 【crontab書き方早見表】時間指定・曜日・実行間隔の構文と動かない時のデバッグ完全ガイド や cron設定の基本とよく使う書き方 でも詳しく解説しています。

スクリプト多重起動を防止する排他制御(flock コマンドの実装)

年末年始などの無人運用で最も恐ろしい事故の一つが、 「スクリプトの多重起動(二重起動)によるカスケードダウン」 です。

たとえば、サーバーが高負荷に陥ってレスポンスが極端に遅延した場合、1回目のヘルスチェックスクリプトがタイムアウト待ちやリトライ処理で5分以上滞留してしまうことがあります。そこへ次の5分が訪れて2回目のスクリプトが起動し、さらに5分後に3回目が起動し……とプロセスが多重滞留すると、スクリプト自身の負荷でサーバーのCPUとメモリが枯渇し、 「監視スクリプトがトドメを刺してサーバーが完全に沈黙する」 という本末転倒の事態が発生します。

この惨事を1行で完全に防止するのが、Linux標準の排他制御コマンド flock です。

# flock によるノンブロッキング排他制御付き crontab 設定
*/5 * * * * /usr/bin/flock -n /var/run/healthcheck.lock /usr/local/bin/server_healthcheck.sh >> /var/log/server_healthcheck.log 2>&1
  • -n(–nonblock) : すでに別のインスタンスがロックファイル(/var/run/healthcheck.lock)を掴んで実行中である場合、待機せずに 即座に終了コード1を返して後続プロセスを破棄 します。

これだけで、前の回の監視が何らかの理由で長引いていても、後続のジョブが絶対に重複起動しなくなり、サーバーの安全性が劇的に向上します。

連続アラート地獄(Slackスパム)を防ぐクーリングダウン(フラグファイル管理)ロジック

もしサーバーが本格的なハードウェア故障やデータ破損によって長時間ダウンし続けた場合、5分間隔でcronが走るとどうなるでしょうか?

1時間に12回、24時間で288回もの「障害検知アラート」がSlackチャンネルに猛烈な勢いで連打 されます。深夜や元日の朝にスマホのSlack通知が鳴り止まなくなり、チームメンバー全員がパニックに陥るか、通知をミュートして放置されるのがオチです。

完成コードで実装している 「フラグファイル方式のクーリングダウン」 は、この問題を極めてシンプルに解決します。

ALERT_FLAG_FILE="/var/run/healthcheck_${SERVICE_NAME}.alert"

# 障害検知時: フラグファイルが存在しない場合(初回)のみSlack通知
if [[ ! -f "${ALERT_FLAG_FILE}" ]]; then
  send_slack_notification "danger" "障害検知" "サーバーがダウンしています。"
  touch "${ALERT_FLAG_FILE}"  # フラグを作成
fi

# 正常復旧時: フラグが存在していた場合のみ「復旧通知」を送りフラグを削除
if [[ -f "${ALERT_FLAG_FILE}" ]]; then
  send_slack_notification "good" "復旧完了" "サーバーが正常稼働に戻りました。"
  rm -f "${ALERT_FLAG_FILE}"  # フラグを削除
fi

このステート管理ロジックにより、 「障害発生時の初回に1回だけ通知」→「落ちている間は通知を抑制」→「直ったら1回だけ復旧通知」 という、有償SaaS並みにスマートで洗練されたアラート運用が実現できます。

ログ肥大化を防ぐlogrotateの設定

5分ごとに実行結果をログファイル(/var/log/server_healthcheck.log)へ書き出し続けると、数ヶ月〜1年でログファイルが数十メガ〜数百メガバイトに肥大化し、ディスク容量逼迫(No space left on device)の原因になります。

logrotate に設定ファイルを1枚追加して、ログの自動圧縮と世代交代(ローテーション)を必ず仕込んでおきましょう。

# /etc/logrotate.d/server_healthcheck を作成
sudo tee /etc/logrotate.d/server_healthcheck << 'EOF'
/var/log/server_healthcheck.log {
    weekly
    rotate 4
    compress
    delaycompress
    missingok
    notifempty
    create 0640 root root
}
EOF

logrotate の詳細な設定ディレクティブやテスト実行(logrotate -d)の手順については、 logrotate設定ファイルの書き方|圧縮・テスト実行・ログ溢れ防止 を参照してください。

まとめ:ZabbixやDatadogを入れる前の「最速・軽量」自前監視のベストプラクティス

サーバーの死活監視において、「どんな仕組みを採用すべきか」はシステムの規模や予算によって異なります。Bashによる自前監視と有償SaaS・専用監視サーバーの特徴を比較してみましょう。

比較項目Bash自前スクリプト監視監視SaaS(Datadog / Mackerel等)Zabbix等の監視サーバー
導入コスト・時間◎ 0円・10分で配備可能△ 有償(ホスト課金)・約30分× 監視用サーバー構築が必要(数日)
リソース消費◎ 極小(curl実行時のみ)○ 常駐エージェント(数十MB〜)○ 常駐エージェント(数十MB〜)
自動再起動(自己修復)◎ systemctl連携で即座に復旧△ Webhook経由の外部連携が必要○ アクション機能でリモート実行可
リソース推移のグラフ化× 単体では不可◎ 非常に美しいダッシュボード◎ 高度なグラフ・可視化
最適な利用シーン開発機・検証機・中小規模本番・年末年始の急場しのぎ大規模本番・マイクロサービス・予算が潤沢な組織オンプレミス大規模環境・閉域網環境

「手作業は負け」「スクリプトはシンプルに」。最初は今回ご紹介したBashスクリプトで最速かつ堅牢に足元を固め、システムの成長やサービスの規模拡大に合わせてSaaSへとステップアップしていくのが、現場における最も費用対効果の高いエンジニアリングアプローチです。

年末年始・休暇前の安全点検チェックリスト

  1. スクリプトの手動テスト : 実際にスクリプトを実行し、Slackに通知が届くことを確認したか?
  2. 自動再起動の動作確認 : ステージング環境等で意図的にサービスを停止させ、自動復旧とSlack通知が行われるかテストしたか?
  3. crontabとPATHの確認 : cron実行環境で絶対パス指定やPATH定義が正しく記述されているか?
  4. 排他制御(flock)の配備 : スクリプト多重起動によるサーバー過負荷対策を講じたか?
  5. ログローテーションの設定 : 監視ログでディスク容量がパンクしないよう logrotate を設定したか?

万全の監視体制を整えて、安心して快適な年末年始や長期休暇をお過ごしください!

あわせて読みたい関連記事

Bash玄

はじめまして!Bash玄です。

エンジニアとしてシステム運用に携わる中で、手作業の多さに限界を感じ、Bashスクリプトを活用して業務を効率化したのがきっかけで、この道に入りました。「手作業は負け」「スクリプトはシンプルに」をモットーに、誰でも実践できるBashスクリプトの書き方を発信しています。

このサイトでは、Bashの基礎から実践的なスクリプト作成まで、初心者でもわかりやすく解説しています。少しでも「Bashって便利だな」と思ってもらえたら嬉しいです!

# 好きなこと
- シンプルなコードを書くこと
- コマンドラインを快適にカスタマイズすること
- 自動化で時間を生み出すこと

# このサイトを読んでほしい人
- Bashに興味があるけど、何から始めればいいかわからない人
- 定型業務を自動化したい人
- 効率よくターミナルを使いこなしたい人

Bashの世界に一歩踏み出して、一緒に「Bash道」を極めていきましょう!

Bash玄をフォローする

コメント