【新・実践シェルスクリプト講座 第2回】Webサイトが死んだら自動で蘇生せよ!「サービス死活監視」と「自動復旧」スクリプト

【新・実践シェルスクリプト講座 第2回】夜中に「サイトが見れません!」と電話で起こされないための自動蘇生術

こんにちは!「リナックス先生」です。
前回は、コマンド一発でWebサーバー環境を構築する「自動構築スクリプト」を作りましたね。
コウ君、その後のサーバー運用は順調?

コウ君

先生…それが、聞いてくださいよ。
昨日の深夜、アクセスが急増したみたいで、メモリ不足でApacheが落ちちゃってたんです。
朝起きたらクライアントから「サイトが見れない!」って鬼のように着信が入ってて…(涙)。
24時間画面を見張り続けるなんて無理ですよ!

リナックス先生

あらら、インフラエンジニアの洗礼を受けたわね。
サーバーは「落ちるもの」よ。大切なのは、落ちないように祈ることじゃなくて、「落ちたらすぐに拾い上げる(再起動する)」こと。
今回は、ダウンを検知して自動で蘇生し、Discordに「直しておいたよ」と報告するスクリプトを作るわよ!

新シリーズ「新・実践シェルスクリプト講座」の第2回は、**「サービス死活監視と自動復旧」**です。
単にプロセスがあるか確認するだけでなく、実際にHTTPアクセスを行って「サイトが正常に見えているか」までチェックする、実践的な監視スクリプトを構築します。

本講座のカリキュラム(全4回)

本シリーズでは、「攻めの自動化」でサーバー管理を楽にします。

  1. 黒い画面でポチポチするな!「Webサーバー構築(LAMP環境)」をボタン一発で全自動化する【完了】
  2. 【今回】Webサイトが死んだら自動で蘇生せよ!「サービス死活監視」と「自動再起動」
  3. ローカル保存じゃ終わらない!「DBバックアップ」と「AWS S3/クラウドへの自動転送」
  4. 黒い画面をアプリ化!?「対話型メニュー」で作る自分だけの管理ツール

1. そもそも、なぜWebサーバーは「死ぬ」のか?

設定ミスをしていないのに、稼働中のサーバーが突然止まる原因の多くは「OOM Killer (Out Of Memory Killer)」です。
アクセス集中などでメモリが足りなくなると、Linuxカーネルはシステム全体を守るために、メモリを食っているプロセス(多くの場合 Apache や MySQL)を強制的にキル(停止)します。

この場合、サーバー自体(OS)は動いていますが、Webサイトだけが表示されない状態になります。
これを復旧するには、人間が systemctl restart httpd を打つ必要がありますが…それをスクリプトにやらせましょう。

2. 通知の準備:Discord Webhook

「再起動しました」という報告を受け取るために、今回はDiscordを使います。
(SlackやTeamsでも手順は似ています)

  1. Discordで通知を送りたいチャンネルの「設定(歯車)」を開く。
  2. 「連携」→「ウェブフック」→「新しいウェブフック」を作成。
  3. 「ウェブフックURLをコピー」ボタンを押して、URLをメモしておく。

これだけで、特定のURLにデータを投げるだけでメッセージが届くようになります。

3. 自動復旧スクリプト「auto_heal.sh」

では、スクリプトを作成します。
仕様は以下の通りです。

  1. プロセス監視: httpdmariadb が起動しているかチェック。停止していたら起動する。
  2. HTTP監視: 自分のサイトにアクセスして、ステータスコード 200 OK が返ってくるかチェック。応答がなければApacheを再起動する。
  3. 通知: 復旧処置を行ったらDiscordに通知する。

vim auto_heal.sh を作成します。

#!/bin/bash

# ==========================================
# 設定エリア
# ==========================================
# 監視対象のサービス名
TARGET_SERVICES=("httpd" "mariadb")
# 監視対象のURL(自分のサイト)
CHECK_URL="http://localhost/"
# Discord Webhook URL(ここに先ほどコピーしたURLを貼る)
DISCORD_URL="https://discord.com/api/webhooks/xxxxxxxx/xxxxxxxxx"
# ログファイル
LOG_FILE="/var/log/auto_heal.log"

# --- 安全設定 ---
set -u

# ==========================================
# 関数定義
# ==========================================
log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}

# Discord通知関数
send_discord() {
    local message="$1"
    # JSON形式でメッセージを作成
    # メンション(@here)を付けて緊急度をアピール
    local payload="{\"content\": \"🚑 **[Server Alert] 自動復旧発動** @here\n${message}\"}"
    
    # curlでPOST送信
    curl -s -H "Content-Type: application/json" -X POST -d "$payload" "$DISCORD_URL"
}

# ==========================================
# 1. プロセス死活監視 (systemctl check)
# ==========================================
log "--- 監視チェック開始 ---"

for service in "${TARGET_SERVICES[@]}"; do
    # is-active は起動中なら0、停止中なら非0を返す
    if ! systemctl is-active --quiet "$service"; then
        log "【危険】$service が停止しています! 再起動を試みます..."
        
        # サービスの再起動
        systemctl start "$service"
        
        # 結果確認
        if systemctl is-active --quiet "$service"; then
            log "【成功】$service の再起動に成功しました。"
            send_discord "✅ 停止していたサービス **$service** を再起動しました。"
        else
            log "【失敗】$service の再起動に失敗しました。手動対応が必要です。"
            send_discord "⛔ **$service** の再起動に失敗しました! 至急確認してください!"
        fi
    fi
done

# ==========================================
# 2. HTTP応答監視 (curl check)
# ==========================================
# プロセスが生きていても、ゾンビ状態でアクセスを受け付けない場合があるため
# 実際にアクセスして確認する

# curlオプション解説
# -s: 静かに(プログレスバーを出さない)
# -o /dev/null: 取得したHTMLは捨てる
# -w "%{http_code}": ステータスコード(200, 404, 500等)だけを表示
http_status=$(curl -s -o /dev/null -w "%{http_code}" "$CHECK_URL")

# ステータスコードが 200 (OK) じゃなかったら異常とみなす
# 000 は接続不可
if [ "$http_status" != "200" ]; then
    log "【危険】Webサイト応答なし (Status: $http_status)。Apacheを再起動します..."
    
    systemctl restart httpd
    
    sleep 5 # 起動待ち
    
    # 再チェック
    retry_status=$(curl -s -o /dev/null -w "%{http_code}" "$CHECK_URL")
    if [ "$retry_status" == "200" ]; then
        log "【成功】Webサイトが復旧しました。"
        send_discord "✅ Webサイトの応答がありませんでした(Status: $http_status)。Apacheを再起動し、復旧しました。"
    else
        log "【失敗】Webサイトが復旧しません (Status: $retry_status)。"
        send_discord "⛔ Apacheを再起動しましたが、Webサイトが見れません(Status: $retry_status)。ログを確認してください。"
    fi
else
    log "Webサイトは正常です (Status: 200)"
fi

log "--- 監視チェック終了 ---"

💡 プロのテクニック:プロセス監視 vs サービス監視

systemctl で「active」になっていても、アクセス過多でフリーズしていてサイトが開かないことはよくあります。
逆に、プロセスは死んでいるのに、pidファイルだけ残っていて「active」と誤認されることも。
だからこそ、「実際にアクセスしてみる(外形監視)」「プロセスを見る(内部監視)」の両方を組み合わせるのが最強の監視方法なのです。

4. 定期実行の設定

作成したスクリプトに実行権限を与え、Cronに登録します。
5分おきに実行するのが一般的です。

# 実行権限付与
chmod +x auto_heal.sh

# 動作テスト(わざとApacheを止めてから実行してみよう)
systemctl stop httpd
./auto_heal.sh
# -> Discordに通知が来れば成功!

Cronの設定 (crontab -e)

# 5分ごとに監視を実行
# PATHを通しておくとトラブルが少ない
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin

*/5 * * * * /root/auto_heal.sh >> /var/log/cron_heal.log 2>&1

5. このスクリプトの弱点と対策

このスクリプトは便利ですが、万能ではありません。
例えば、「設定ファイル(httpd.conf)の記述ミス」でApacheが起動しない場合、このスクリプトは5分おきに永遠に再起動を試み、失敗し、Discordに通知を送り続けます(これを「通知スパム」と呼びます)。

実務では、「3回連続で失敗したら、スクリプト自体を一時停止する」といったロジックを追加することもありますが、まずは「深夜に叩き起こされる回数を減らす」ための第一歩として、このスクリプトを使ってみてください。

まとめ:エンジニアは寝るのが仕事

今回は「サービス死活監視と自動復旧」を自動化しました。

監視項目 使用コマンド 目的
プロセス監視 systemctl is-active サービスが落ちていないか確認
HTTP監視 curl -w "%{http_code}" Webサイトが正常に見えているか確認
通知 curl -X POST (Discord) 復旧結果をスマホに通知

これで、突発的なメモリエラーでApacheが落ちても、次の5分後には自動で蘇生されます。
あなたは朝起きて、Discordの通知を見て「おっ、夜中に一回コケたな。後でログ見ておくか」とコーヒーを飲めばいいのです。

次回予告:バックアップは「外」に出せ!

サーバー監視は完璧になりました。
しかし、もしサーバーのディスク自体が壊れたら? VPSごとそのデータセンターが火事になったら?
サーバーの中にバックアップがあっても、一緒に消えてしまいます。

次回は、「データベース(MariaDB)のダンプ」を取り、それを「AWS S3(クラウドストレージ)」などの外部へ自動転送する、プロ仕様のBCP(事業継続計画)対策スクリプトを作成します。

リナックス先生

今回のスクリプトは、Webサーバー以外にも応用が効くわ。
例えば、Minecraftサーバーの監視とか、Botプログラムの監視とか。
「動いてなきゃいけないもの」があったら、とりあえず監視させる癖をつけるといいわよ!

▼監視スクリプトのテストは、本番環境ではなくサブのVPSで!

【2026年最新】Linuxサーバー構築におすすめのVPS比較3選!現役エンジニアが速度とコスパで厳選
Linuxの勉強、まだ「自分のPC」でやって消耗していませんか?「Linuxを覚えたいけど、環境構築でエラーが出て先に進めない…」「VirtualBoxを入れたらパソコンが重くなった…」これは、Linux学習を始める9割の人がぶつかる壁です...

コメント