0Pricing
DevOps Bootcamp · レッスン

システムヘルスチェックとアラートスクリプトの構築

負荷、メモリ、ディスクのメトリクスを収集し、スケジュール実行するスクリプトからしきい値ベースのアラートを発生させます。

「システムヘルスチェックとアラートスクリプトの構築」はCoddyKit上の無料DevOps Bootcampレッスンです。 これはレッスン4/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはDevOps Bootcamp学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 DevOps Bootcampコースには全4レッスンが含まれています。

システムヘルスチェックが重要な理由

本番サーバーは、気づかないうちに劣化することがあります。CPUスパイク、メモリリーク、ディスク容量の枯渇は障害を引き起こしますが、適切なタイミングで誰かが気づけば防げる場合もあります。システムヘルスチェックスクリプトは、メトリクスを収集し、しきい値と比較し、ユーザーが問題を感じる前にアラートを発生させるという監視ループを自動化します。

  • cronでスケジュール実行すれば、人の操作なしで数分おきに実行できます
  • ログ集約に適した、一貫性のあるタイムスタンプ付きの出力を生成します
  • しきい値ベースのロジックにより、意味のあるアラートだけを発生させます。小さな一時的な問題のたびにオンコールチームへ通知することはありません

このレッスンでは、ロードアベレージ、メモリプレッシャー、ディスク使用率を扱いながら、本番環境向けのヘルスチェック スクリプトをゼロから段階的に構築します。

ロードアベレージを取得する

Linuxでは、1分、5分、15分のロードアベレージを/proc/loadavgとuptimeコマンドから取得できます。スクリプトでは、/proc/loadavgが最も扱いやすい情報源です。ロケールの影響を受けず、ディストリビューション間で解析方法が変わることもありません。

次のスニペットでは、1分間のロードアベレージを読み取り、しきい値との比較に使う変数へ格納します。cutで最初のフィールドを抽出し、awkで小数部分を取り除いて、bcによる浮動小数点演算を使った整数比較を行います。

#!/usr/bin/env bash
# Read 1-minute load average from /proc/loadavg
LOAD_RAW=$(cut -d' ' -f1 /proc/loadavg)
echo "Raw load average: $LOAD_RAW"

# Number of CPU cores — used to normalise load
CPU_CORES=$(nproc)
echo "CPU cores: $CPU_CORES"

# Compute load percentage (load / cores * 100) using bc
LOAD_PCT=$(echo "scale=2; $LOAD_RAW / $CPU_CORES * 100" | bc)
echo "Load %: $LOAD_PCT"

浮動小数点値でしきい値を比較する

Bashは浮動小数点数をネイティブには比較できません。[ 1.5 -gt 1.2 ]を実行するとエラーになります。慣用的な解決策は次の2つです。

  • bc — 比較式の結果として1(真)または0(偽)を出力します
  • awk — パイプライン内で浮動小数点数の条件を評価できます

bcを使うと、ロジックが読みやすく、テストもしやすくなります。$(echo "$A > $B" | bc)というパターンは、条件が成立すると1を返します。この値を[ ... -eq 1 ]でテストします。

#!/usr/bin/env bash
LOAD_RAW=$(cut -d' ' -f1 /proc/loadavg)
CPU_CORES=$(nproc)
THRESHOLD=80  # alert when load % exceeds 80%

LOAD_PCT=$(echo "scale=2; $LOAD_RAW / $CPU_CORES * 100" | bc)

# bc returns 1 if the expression is true
if [ "$(echo "$LOAD_PCT > $THRESHOLD" | bc)" -eq 1 ]; then
    echo "ALERT: Load is ${LOAD_PCT}% (threshold ${THRESHOLD}%)"
else
    echo "OK: Load is ${LOAD_PCT}%"
fi

メモリメトリクスを収集する

/proc/meminfoは、Linuxにおけるメモリ統計情報の信頼できる情報源です。主なフィールドは次のとおりです。

  • MemTotal — 物理RAMの総容量(kB)
  • MemAvailable — スワップを発生させずに新しい割り当てへ使用できる推定容量(kB)。MemFreeより適しています

パターンマッチングを使ったawkが、これらの値を抽出する最も簡潔な方法です。MemAvailableをMemTotalで割り、100から引くと使用メモリ率が得られます。この値をアラートのしきい値判定に使用します。

#!/usr/bin/env bash
# Extract memory figures from /proc/meminfo (values in kB)
MEM_TOTAL=$(awk '/^MemTotal:/ {print $2}' /proc/meminfo)
MEM_AVAIL=$(awk '/^MemAvailable:/ {print $2}' /proc/meminfo)

# Used memory percentage
MEM_USED_PCT=$(echo "scale=2; (1 - $MEM_AVAIL / $MEM_TOTAL) * 100" | bc)

echo "Total RAM : ${MEM_TOTAL} kB"
echo "Available : ${MEM_AVAIL} kB"
echo "Used      : ${MEM_USED_PCT}%"

ディスク使用量メトリクスを収集する

dfコマンドは、ファイルシステムの使用量を報告します。スクリプトでは、次の2つのフラグが重要です。

  • -h — 人間が読みやすいサイズで表示します(表示専用で、算術演算には使用しないでください)
  • --output=pcent,target — 機械で解析しやすい列を出力します(GNU coreutils)

マウントされているすべてのファイルシステムを反復処理すると、/だけでなく、容量が限界に達したパーティションをすべて検出できます。整数比較の前に、%記号をtr -d '%'で取り除きます。

#!/usr/bin/env bash
DISK_THRESHOLD=85

# Skip header line with tail -n +2
# --output=pcent,target gives "85% /var" style lines
df --output=pcent,target | tail -n +2 | while read -r USED_PCT MOUNT; do
    # Remove the % sign for arithmetic
    USED_INT=${USED_PCT//%/}

    if [ "$USED_INT" -ge "$DISK_THRESHOLD" ]; then
        echo "ALERT: Disk $MOUNT is ${USED_PCT} full"
    else
        echo "OK   : Disk $MOUNT is ${USED_PCT} full"
    fi
done

タイムスタンプ付きの構造化アラート出力

タイムスタンプのないアラートメッセージは、ログファイルやメールレポートではほとんど役に立ちません。一貫したプレフィックスがあれば、grepやログ転送エージェントで簡単にログを解析できます。

スクリプトの先頭で小さなアラート関数を定義します。この関数は、ISO-8601形式のタイムスタンプ、重大度、チェック名を先頭に付加します。すべてのアラートをtee経由でstdoutとログファイルの両方へ書き込みます。

  • date -u +"%Y-%m-%dT%H:%M:%SZ" — ロケールに依存しないUTCタイムスタンプ
  • ALERTはstderrへ、OKはstdoutへ書き込むことで、パイプライン内の重要な情報とノイズを分離できます
#!/usr/bin/env bash
LOG_FILE="/var/log/healthcheck.log"

alert() {
    local LEVEL="$1"   # OK | WARN | ALERT
    local CHECK="$2"
    local MSG="$3"
    local TS
    TS=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
    local LINE="[$TS] [$LEVEL] [$CHECK] $MSG"

    if [ "$LEVEL" = "ALERT" ]; then
        echo "$LINE" | tee -a "$LOG_FILE" >&2
    else
        echo "$LINE" | tee -a "$LOG_FILE"
    fi
}

# Usage examples
alert "OK"    "DISK"  "/ is 42% full"
alert "ALERT" "DISK"  "/var is 91% full"

mailとsendmailでメールアラートを送信する

サーバー上で最も簡単に利用できるアラート手段は、ローカルMTA(postfix、sendmail、またはmsmtp)経由のメールです。mailコマンド(mailutilsまたはbsd-mailxに含まれます)を使うと、1行でメッセージを作成して送信できます。

  • -s — 件名
  • 本文をstdin経由でパイプします
  • ローカルMTAがないサーバーでは、mailをトランザクションメールAPIへのcurl呼び出しに置き換えます

1つのノイズの多い状態で受信トレイが大量のメールで埋まらないよう、重複排除用ロックで送信を制御してください。

#!/usr/bin/env bash
ALERT_EMAIL="ops@example.com"
LOCK_DIR="/tmp/healthcheck_locks"
mkdir -p "$LOCK_DIR"

send_alert() {
    local CHECK="$1"
    local MSG="$2"
    local LOCK="$LOCK_DIR/${CHECK}.lock"

    # Only send if no lock exists (prevents repeated emails within the hour)
    if [ ! -f "$LOCK" ]; then
        echo "$MSG" | mail -s "[ALERT] $CHECK on $(hostname)" "$ALERT_EMAIL"
        touch "$LOCK"
        # Lock expires after 1 hour via cron or find+delete
        echo "Alert sent for $CHECK"
    else
        echo "Alert suppressed for $CHECK (lock active)"
    fi
}

send_alert "HIGH_LOAD" "Load average exceeded 80% on $(hostname) at $(date)"

完全なヘルスチェック スクリプトの作成

ここまでの3つのチェック、つまり負荷、メモリ、ディスクを、先頭でしきい値を設定できる1つのまとまりのあるスクリプトに統合します。これは、本番環境のシステム管理自動化で使われるパターンです。

  • ロジックを編集せずに簡単に調整できるよう、先頭で定数を宣言する
  • 可読性と単体テストのしやすさのため、各チェックを関数に分離する
  • main 関数で各処理の呼び出しを統括する
  • いずれかのアラートが発生した場合は終了コード 1、それ以外は 0 にする — これにより、Nagios/Icinga などの監視フレームワークと組み合わせて利用できます
#!/usr/bin/env bash
set -euo pipefail

# ── Thresholds ───────────────────────────────────────────
LOAD_THRESHOLD=80   # percent of CPU capacity
MEM_THRESHOLD=90    # percent used
DISK_THRESHOLD=85   # percent used
ALERT_EMAIL="ops@example.com"
LOG_FILE="/var/log/healthcheck.log"
ALERT_FIRED=0

# ── Helpers ──────────────────────────────────────────────
ts()    { date -u +"%Y-%m-%dT%H:%M:%SZ"; }
log()   { echo "[$(ts)] $*" | tee -a "$LOG_FILE"; }
alert() { log "ALERT: $*"; echo "$*" | mail -s "[ALERT] $(hostname)" "$ALERT_EMAIL" 2>/dev/null; ALERT_FIRED=1; }

# ── Checks ───────────────────────────────────────────────
check_load() {
    local raw cores pct
    raw=$(cut -d' ' -f1 /proc/loadavg)
    cores=$(nproc)
    pct=$(echo "scale=2; $raw / $cores * 100" | bc)
    if [ "$(echo "$pct > $LOAD_THRESHOLD" | bc)" -eq 1 ]; then
        alert "Load ${pct}% exceeds ${LOAD_THRESHOLD}%"
    else
        log "OK load=${pct}%"
    fi
}

check_memory() {
    local total avail pct
    total=$(awk '/^MemTotal:/    {print $2}' /proc/meminfo)
    avail=$(awk '/^MemAvailable:/{print $2}' /proc/meminfo)
    pct=$(echo "scale=2; (1 - $avail / $total) * 100" | bc)
    if [ "$(echo "$pct > $MEM_THRESHOLD" | bc)" -eq 1 ]; then
        alert "Memory ${pct}% used (threshold ${MEM_THRESHOLD}%)"
    else
        log "OK memory=${pct}%"
    fi
}

check_disk() {
    df --output=pcent,target | tail -n +2 | while read -r used mnt; do
        local pct_int=${used//%/}
        if [ "$pct_int" -ge "$DISK_THRESHOLD" ]; then
            alert "Disk $mnt at ${used}"
        else
            log "OK disk $mnt=${used}"
        fi
    done
}

main() {
    log "=== Health check START ==="
    check_load
    check_memory
    check_disk
    log "=== Health check END (alerts=$ALERT_FIRED) ==="
    exit "$ALERT_FIRED"
}

main

Cron によるスケジューリング

ヘルスチェック スクリプトは、自動的に実行されて初めて価値を発揮します。cron は標準的な Unix スケジューラです。システム全体の crontab、または /etc/cron.d/ 内の専用ファイルを編集して、スクリプトをスケジュールします。

  • 5分ごとに実行: */5 * * * *
  • cron では必ず 絶対パスを使用します — cron 環境の $PATH は最小限に設定されています
  • 毎回 cron からメールが送信されるのを防ぐため、出力をリダイレクトします: >> /var/log/healthcheck.log 2>&1
  • cron 自身が送信するメールを無効にするには、crontab の先頭で MAILTO="" を指定します
# /etc/cron.d/healthcheck
# Run the health check every 5 minutes as root
MAILTO=""
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin

*/5 * * * * root /usr/local/sbin/healthcheck.sh >> /var/log/healthcheck.log 2>&1

クールダウンロックによるアラートの集中送信防止

しきい値の超過が継続していると、単純なスクリプトでは5分ごとにアラートが発生し、エンジニアが対応する前に何十通ものメールが送られてしまいます。クールダウンロックを使うと、設定可能な期間内に繰り返し発生するアラートを抑制できます。

このパターンでは、最初のアラートでロックファイルを書き込みます。その後は、ロックファイルがクールダウン期間より新しい間、アラートをスキップします。find の -mmin を使えば、日付の計算をせずにファイルの経過時間をアトミックに確認できます。

#!/usr/bin/env bash
LOCK_DIR="/tmp/hc_locks"
COOLDOWN_MIN=60  # suppress repeat alerts for 60 minutes
mkdir -p "$LOCK_DIR"

should_alert() {
    local check="$1"
    local lock="$LOCK_DIR/${check}.lock"

    if [ ! -f "$lock" ]; then
        # No lock — allow alert and create lock
        touch "$lock"
        return 0  # true: send alert
    fi

    # Lock exists — check if it is older than the cooldown
    # find returns the filename only if it's OLDER than COOLDOWN_MIN
    local expired
    expired=$(find "$lock" -mmin +"$COOLDOWN_MIN" 2>/dev/null)

    if [ -n "$expired" ]; then
        touch "$lock"  # refresh lock timestamp
        return 0       # cooldown expired — allow alert
    fi

    return 1  # still within cooldown — suppress
}

# Usage
if should_alert "HIGH_LOAD"; then
    echo "Sending load alert..."
    # mail -s "..." ops@example.com <<< "Load too high"
else
    echo "Load alert suppressed (cooldown active)"
fi

ヘルスチェック スクリプトのテストと検証

デプロイする前に、次の3つの方法でスクリプトを検証します。

  • 構文チェック: bash -n healthcheck.sh は、実行せずに解析エラーを検出します
  • トレースモード: bash -x healthcheck.sh は、実行されるすべてのコマンドを表示します — デバッグに非常に役立ちます
  • しきい値の上書き: しきい値を一時的にほぼゼロまで下げ、正常なホストでもスクリプトがアラートを発生させるようにします。これにより、アラート経路が最初から最後まで正しく動作することを確認できます

メール経路をテストする際は、MOCK_MAIL フラグを使って mail の出力をログファイルにリダイレクトします。

#!/usr/bin/env bash
# Smoke-test the alert path without sending real email
MOCK_MAIL=true
ALERT_EMAIL="ops@example.com"

send_mail() {
    local subject="$1"
    local body="$2"
    if [ "$MOCK_MAIL" = true ]; then
        echo "[MOCK MAIL] To: $ALERT_EMAIL | Subject: $subject"
        echo "[MOCK MAIL] Body: $body"
    else
        echo "$body" | mail -s "$subject" "$ALERT_EMAIL"
    fi
}

# Override threshold to guarantee an alert fires
LOAD_THRESHOLD=0   # Any load will exceed 0%
LOAD_RAW=$(cut -d' ' -f1 /proc/loadavg)
CPU_CORES=$(nproc)
PCT=$(echo "scale=2; $LOAD_RAW / $CPU_CORES * 100" | bc)

if [ "$(echo "$PCT > $LOAD_THRESHOLD" | bc)" -eq 1 ]; then
    send_mail "[ALERT] Load on $(hostname)" "Load is ${PCT}%"
fi

知識チェック: クールダウン戦略

次の状況を考えてみてください。ヘルスチェックの cron ジョブが5分ごとに実行されています。/var のディスク使用率が85%を超え、その状態が3時間続いています。オンコール担当のエンジニアには5分ごとではなく、1時間に1回アラートを送信したいとします。最も適切な実装戦略はどれでしょうか。

レッスンのまとめ: システムヘルスチェック スクリプト

このレッスンでは、本番環境で利用できる完全なシステムヘルスチェックとアラート通知のパイプラインを構築しました。今後も活用できる重要な原則を以下にまとめます。

  • 信頼できる情報源: /proc/loadavg と /proc/meminfo からメトリクスを読み取ります。これらは安定しており、ロケールに依存せず、すべての Linux ホストで利用できます
  • 浮動小数点演算: 浮動小数点数のしきい値比較には bc を使用します。Bash の整数比較 (-gt) は整数に対してのみ機能します
  • ディスクの反復処理: df --output=pcent,target を使って、/ だけでなく、マウントされているすべてのファイルシステムを確認します
  • 構造化ログ: すべての行の先頭に UTC のタイムスタンプと重大度レベルを付けます。これにより、ログを grep で扱いやすくなり、集中ログシステムにも適切に送信できます
  • クールダウンロック: find -mmin で確認するロックファイルにより、cron のスケジュールを変更せずにアラートの集中送信を防げます
  • 組み合わせやすさ: いずれかのアラートが発生した場合は終了コード 1 で終了し、Nagios、Icinga、その他の監視フレームワークと連携できるようにします
  • テスト: 構文チェックには bash -n、トレースデバッグには bash -x を使い、MOCK_MAIL フラグで正常なホスト上のアラート経路を検証します

完成したスクリプトを /etc/cron.d/ 経由でスケジュールすれば、インフラストラクチャが継続的に自己監視を行い、しきい値を明確に超えた場合にのみアラートを発生させられます。

よくある質問

「システムヘルスチェックとアラートスクリプトの構築」レッスンは無料ですか?

はい。「システムヘルスチェックとアラートスクリプトの構築」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、DevOps Bootcampコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 DevOps Bootcampコースには全4レッスンが含まれています。

「システムヘルスチェックとアラートスクリプトの構築」で何を学びますか?

負荷、メモリ、ディスクのメトリクスを収集し、スケジュール実行するスクリプトからしきい値ベースのアラートを発生させます。 ブラウザで直接実行するハンズオンコードでDevOps Bootcampを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

DevOps Bootcampを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのDevOps Bootcampは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン4/4です。

「システムヘルスチェックとアラートスクリプトの構築」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このDevOps Bootcampレッスンでコードを書いて実行できますか?

はい。すべてのDevOps Bootcampレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. ユーザーとグループのプロビジョニング自動化
  2. systemdサービスの制御とユニットファイルの作成
  3. ディスク、ファイルシステム、マウントの自動化
  4. システムヘルスチェックとアラートスクリプトの構築
← DevOps Bootcampに戻る