死活監視スクリプトにリトライ機能を追加【Bashシェルスクリプト】

この記事では、これまで作成した死活監視スクリプトにリトライ機能を追加した実装方法を解説します
リトライ機能により、一時的なネットワーク障害やを考慮した動作を実現しました。また、既存機能(通知やログ保存)もすべて活かしています。


実装内容のポイント

  1. リトライ機能の導入
    対象サイトが異常と判断された場合、一定回数リトライして再確認する仕組みを追加しました。
  • リトライ回数: 2回(デフォルト設定)
  • リトライ間隔: 5秒(デフォルト設定)
  1. ステータスコード 000 の特別処理
    通信失敗を表す 000 ステータスコードもリトライ対象にし、ログや通知に反映しました。
  2. 通知時間帯の制御
    夜間の不要な通知を避けるため、メール通知が指定時間内にのみ送信されるよう制御しました。

使用技術

以下の技術を活用しています:

  • Bash: メインの死活監視スクリプト
  • curl: サイトのステータスコードを取得
  • Chatwork API / Slack Webhook: 異常検知時の通知
  • cron: スクリプトの定期実行

実装コード

死活監視スクリプト

以下は、リトライ機能を追加した最新の死活監視スクリプトです。

#!/bin/bash

# パラメーターで指定された会社名を取得
company="$1"

# 配列の宣言
declare -A sites

# 設定ファイルのパスを作成
config_file="$(dirname "$0")/config_${company}.sh"

# 設定ファイルが存在するかチェック
if [[ ! -f "$config_file" ]]; then
    echo "エラー: 設定ファイル '${config_file}' が見つかりません。"
    exit 1
fi

# 設定ファイルの読み込み
source "$config_file"

# 実行開始時間の取得
start_time=$(date "+%Y-%m-%d %H:%M:%S")
log_date=$(date "+%Y%m%d")
current_time=$(date "+%H:%M")

# ベース保存ディレクトリ (logs/company毎のディレクトリを作成)
base_log_dir="$(dirname "$0")/logs/$company"
mkdir -p "$base_log_dir"  # ベースディレクトリ作成

# リトライ設定
RETRY_COUNT=2  # リトライ回数
RETRY_INTERVAL=5  # リトライ間隔(秒)

# メール通知の時間帯をチェックする関数
is_within_email_notify_time() {
    # 現在時刻を取得し、分単位に変換
    current_minutes=$((10#$(date "+%H") * 60 + 10#$(date "+%M")))
    # 通知開始・終了時刻を分単位に変換
    start_minutes=$((10#${email_notify_start%%:*} * 60 + 10#${email_notify_start##*:}))
    end_minutes=$((10#${email_notify_end%%:*} * 60 + 10#${email_notify_end##*:}))

    if [[ "$start_minutes" -le "$end_minutes" ]]; then
        # 通常の時間帯 (例: 08:00 - 22:00)
        if [[ "$current_minutes" -ge "$start_minutes" && "$current_minutes" -le "$end_minutes" ]]; then
            return 0
        fi
    else
        # 翌日にまたがる時間帯 (例: 22:00 - 08:00)
        if [[ "$current_minutes" -ge "$start_minutes" || "$current_minutes" -le "$end_minutes" ]]; then
            return 0
        fi
    fi

    return 1
}


# メール通知関数
send_email() {
    local site=$1
    local emails=$2
    local status_code=$3
    local status_message=$4
    local subject="【サイト監視】$site は$status_message (HTTPステータスコード: $status_code)"
    local body="【サイト監視結果】\n\n日時: $start_time\n\n対象サイト: $site\n状態: $status_message\nHTTPステータスコード: $status_code\n\nご確認ください。"

    echo -e "$body" | mail -s "$subject" -b "$bcc_email" "$emails"
}

# Chatwork 通知関数
send_chatwork_notification() {
    local message=$1
    local room_id=$2
    curl -X POST -H "X-ChatWorkToken: $chatwork_api_token" \
        -d "body=$message" \
        "https://api.chatwork.com/v2/rooms/$room_id/messages"
}

# Slack 通知関数
send_slack_notification() {
    local message=$1
    curl -X POST -H 'Content-type: application/json' --data "{\"text\": \"$message\"}" "$slack_webhook_url"
}

# サマリー通知用の変数
summary_subject="【サイト監視サマリー】全サイトの状態レポート"
summary_body="【サイト監視結果サマリー】\n\n日時: $start_time\n\n監視結果:\n\n"
alert_needed=false
alert_summary_body="【サイト監視異常通知】\n\n日時: $start_time\n\n異常検知されたサイト:\n\n"

# ステータスチェック関数
check_status() {
    local url=$1
    curl -o /dev/null -s -w "%{http_code}" "$url"
}

# サイトの死活監視
declare -A processed

for key in "${!sites[@]}"; do
    # site_keyをキーから抽出
    site_key="${key%%.*}"

    # 同じサイトを一度だけ処理
    if [[ -z "${processed[$site_key]}" ]]; then
        url="${sites[$site_key.url]}"
        emails="${sites[$site_key.email]}"
        email_notify="${sites[$site_key.notify]}"
        processed[$site_key]=1  # 処理済みマークを設定

        # 個別サイト用ログディレクトリを作成
        site_log_dir="$base_log_dir/${site_key//[^a-zA-Z0-9]/_}"
        mkdir -p "$site_log_dir"

        # 保存ファイルのパス (日付単位で分割保存)
        save_file="$site_log_dir/monitor_results_${log_date}.csv"

        # 初回実行時にヘッダーを追加
        if [[ ! -f "$save_file" ]]; then
            echo "日時,サイト,状態,ステータスコード" > "$save_file"
        fi

        # curlを使ってサイトのHTTPステータスコードを取得
        status_code=$(curl -o /dev/null -s -w "%{http_code}" "$url")

        # ステータスコードによる通知内容の判断
        if [[ "$status_code" == "200" || "$status_code" == "301" || "$status_code" == "302" ]]; then
            status_message="正常に稼働中"
        else
            for ((i=1; i<=RETRY_COUNT; i++)); do
                sleep "$RETRY_INTERVAL"
                status_code=$(check_status "$url")
                if [[ "$status_code" == "200" || "$status_code" == "301" || "$status_code" == "302" ]]; then
                    status_message="正常に稼働中(リトライ成功)"
                    break
                fi
            done

            if [[ "$status_code" == "000" ]]; then
                status_message="接続失敗"
            elif [[ "$status_code" == "503" ]]; then
                status_message="サービスが一時的に利用できません"
            elif [[ -z "$status_message" ]]; then
                status_message="ダウンしています"
            fi
        fi

        # ログ保存
        echo "$start_time,$url,$status_message,$status_code" >> "$save_file"

        # 必要に応じて通知(メール通知は指定時間内のみ送信)
        if [[ "$email_notify" == "true" && -n "$emails" ]]; then
            if is_within_email_notify_time; then
                send_email "$url" "$emails" "$status_code" "$status_message"
            else
                echo "$(date): メール送信をスキップしました ($url - 時間外)"
            fi
        fi

        # 異常検知時の追加
        if [[ "$status_message" != "正常に稼働中" ]]; then
            alert_needed=true
            alert_summary_body+="$url\n  状態: $status_message\n  ステータスコード: $status_code\n\n"
        fi

        # サマリーへの追加
        summary_body+="$url\n  状態: $status_message\n  ステータスコード: $status_code\n\n"
    fi
done


# サマリー通知の送信 (メール)
if is_within_email_notify_time; then
    echo -e "$summary_body" | mail -s "$summary_subject" -b "$bcc_email" "$bcc_email"
    echo "$(date): サマリーメールを送信しました。" >> "$base_log_dir/debug.log"
else
    echo "$(date): サマリーメール送信をスキップしました (時間外)" >> "$base_log_dir/debug.log"
fi


# サマリー通知の送信 (Chatwork)
encoded_summary_body=$(echo -e "$summary_body" | sed ':a;N;$!ba;s/\n/%0A/g')
send_chatwork_notification "$encoded_summary_body" "$chatwork_room_id"

# サマリー通知の送信 (Slack)
slack_message="【サイト監視サマリー】\n\n日時: $start_time\n\n監視結果:\n\n$summary_body"
send_slack_notification "$slack_message"

# 異常が検知された場合の通知
if [ "$alert_needed" = true ]; then
    echo -e "$alert_summary_body" | mail -s "【サイト監視異常通知】異常が検知されました" "$alert_email"
    encoded_alert_summary_body=$(echo -e "$alert_summary_body" | sed ':a;N;$!ba;s/\n/%0A/g')
    send_chatwork_notification "$encoded_alert_summary_body" "$alert_chatwork_room_id"
    slack_alert_message="【サイト監視異常通知】\n\n日時: $start_time\n\n異常検知されたサイト:\n\n$alert_summary_body"
    send_slack_notification "$slack_alert_message"
fi

コンフィグファイルの設定

以下は、対象サイトの設定を記載したコンフィグファイルの内容です。

# BCC用メールアドレス
bcc_email="dummy.bcc@example.com"

# Chatwork API設定
chatwork_api_token="xxxxxxxxxxxxxxxxxxxxxxxx"
chatwork_room_id="123456789"

# 異常時専用の通知先
alert_chatwork_room_id="987654321"    # 異常検知時に通知するChatworkルーム
alert_email="alert@example.com"  # 異常検知時に通知するメールアドレス

# Slack Webhook URL
slack_webhook_url="https://hooks.slack.com/services/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"

# メール通知の時間帯
email_notify_start="08:00"
email_notify_end="22:00"

# 監視対象サイトの設定 (URL, メールアドレス, メール通知フラグ)
declare -A sites

site_key="example_site_1"
sites["$site_key.url"]="https://example1.com/"
sites["$site_key.email"]="contact@example1.com"
sites["$site_key.notify"]=true

site_key="example_site_2"
sites["$site_key.url"]="https://example2.com/"
sites["$site_key.email"]="info@example2.com"
sites["$site_key.notify"]=false

まとめ

リトライ機能の追加により、短時間のネットワーク障害に対する柔軟性が向上しました。この改良により、無駄な通知を削減し、監視業務の効率化が期待できます。今後はさらなるデータ解析や通知手段の拡張を検討します。

コメント

タイトルとURLをコピーしました