2026年6月29日

2026年6月29日

Linuxのメモリ使用量アラートを設定する方法(MemAvailableで判定し、スクリプトとPrometheusで通知する)

Linux のメモリ不足を通知するなら、「使用率」ではなく /proc/meminfo の MemAvailable(新しいプロセスがスワップせずに使える見込みの量)で判定するのが基本です。使用率だけで判定すると、ファイルキャッシュに使われているだけの正常な状態でも通知が鳴り続けます。この記事では、何を基準にアラートを出すかを決めたうえで、シェルスクリプトと cron での通知、Prometheus でのアラートルール、通知が来たときの調べ方までを、WordPress を動かしている VPS を例に説明します。

「使用率が高い」は異常とは限らない

free -h を実行すると、used(使用中)、free(未使用)、buff/cache(キャッシュ)、available(利用可能)といった列が並びます。

free -h

Linux は空いているメモリをディスクのキャッシュに積極的に使い、必要になればキャッシュを手放してアプリケーションに回します。そのため free の値が小さくても、available に余裕があれば問題ありません。逆に available が小さくなり、スワップの使用量が増え始めたら、実際にメモリが足りなくなりつつあります。

アラートの判定に使う値は、free コマンドの available 列のもとになっている /proc/meminfo の MemAvailable です。比較的新しいカーネル(3.14 以降)で提供されており、現在の主要なディストリビューションなら利用できます。

何を基準に通知するか決める

通知の条件は、少なすぎると見逃し、多すぎると無視されるようになります。次の3つを組み合わせるのがおすすめです。

  1. MemAvailable の割合: 総メモリに対する MemAvailable の割合が一定(例: 10%)を下回ったら通知します。
  2. 継続時間: 一瞬だけ下がった場合は無視し、数分続いたら通知します。バックアップやキャッシュ再生成など、一時的にメモリを使う処理で鳴らないようにするためです。
  3. OOM Killer の発動: メモリが尽きてカーネルがプロセスを強制終了した記録です。これは起きた時点で問題なので、継続時間を待たずに知りたい出来事です。

しきい値に決まった正解はありません。普段の MemAvailable の推移を数日見て、平常時より明らかに低い水準に置くのが実用的です。メモリが 1〜2GB の小さな VPS では、割合だけでなく「残り 200MB を下回ったら」のように残りの量(MB)で決めるほうが感覚に合うこともあります。

カーネル 4.20 以降では、メモリ不足でプロセスが待たされている時間の割合を示す PSI(Pressure Stall Information)も /proc/pressure/memory で読めます。MemAvailable が低いうえに PSI の値が上がっていれば、実際に処理が遅くなっていると判断できます。

シェルスクリプトとcronで通知する

監視の仕組みをまだ持っていないサーバーでも、短いスクリプトで通知を始められます。ここでは Slack の Incoming Webhook に送る例を示します。Webhook の URL は秘密情報なので、スクリプトに直接書かず、root だけが読めるファイルに分けます。

# Webhook URLを root だけが読めるファイルに置く(値はダミー)
sudo sh -c 'echo "SLACK_WEBHOOK_URL=https://hooks.slack.com/services/your/webhook/url" > /etc/memory-alert.env'
sudo chmod 600 /etc/memory-alert.env
sudo apt install jq
#!/bin/bash
# /usr/local/bin/memory-alert.sh
set -u

THRESHOLD_PCT=10                  # MemAvailable がこの割合(%)を下回ったら通知
COOLDOWN_SEC=1800                 # 一度通知したら30分は同じ通知を送らない
STATE_FILE=/var/tmp/memory-alert.last
ENV_FILE=/etc/memory-alert.env    # SLACK_WEBHOOK_URL=... を書いたファイル

[ -r "$ENV_FILE" ] && . "$ENV_FILE"

read -r total avail < <(awk '/^MemTotal:/{t=$2} /^MemAvailable:/{a=$2} END{print t, a}' /proc/meminfo)
avail_pct=$(( avail * 100 / total ))
swap_used_mb=$(awk '/^SwapTotal:/{t=$2} /^SwapFree:/{f=$2} END{printf "%d", (t-f)/1024}' /proc/meminfo)

[ "$avail_pct" -ge "$THRESHOLD_PCT" ] && exit 0

now=$(date +%s)
last=$(cat "$STATE_FILE" 2>/dev/null || echo 0)
[ $(( now - last )) -lt "$COOLDOWN_SEC" ] && exit 0

top_procs=$(ps -eo rss,comm --sort=-rss | awk 'NR>1 && NR<=6 {printf "%s(%dMB) ", $2, $1/1024}')
msg="[$(hostname)] メモリ残り ${avail_pct}% / スワップ使用 ${swap_used_mb}MB / 上位: ${top_procs}"

logger -t memory-alert "$msg"
if [ -n "${SLACK_WEBHOOK_URL:-}" ]; then
  jq -n --arg text "$msg" '{text: $text}' \
    | curl -fsS -m 10 -X POST -H 'Content-Type: application/json' --data @- "$SLACK_WEBHOOK_URL" >/dev/null
fi
echo "$now" > "$STATE_FILE"
sudo chmod 700 /usr/local/bin/memory-alert.sh

# /etc/cron.d/memory-alert(1分ごとに実行)
* * * * * root /usr/local/bin/memory-alert.sh

スクリプトの考え方は次のとおりです。

  • クールダウン: 状態ファイルに最後の通知時刻を書き、30分以内なら送りません。メモリ不足が続く間、1分ごとに通知が来ると、肝心なときに通知を見なくなります。
  • 上位プロセスを添える: 通知文にメモリを多く使っているプロセスを5つ入れています。通知を見た時点で「php-fpm が多い」「mysqld が大きい」と当たりをつけられます。
  • JSON の組み立ては jq に任せる: プロセス名などに記号が含まれても JSON が壊れないようにするためです。
  • ログにも残す: logger で syslog(journal)にも記録するので、Slack 側で見落としても後から確認できます。

このスクリプトは「1回の測定で下回ったら通知」なので、一時的な落ち込みでも鳴ることがあります。気になる場合は、状態ファイルに連続で下回った回数を記録し、3回続いたら通知する形に拡張してください。

通知が届くかを試す

設定したら、THRESHOLD_PCT を一時的に 99 にして手動で実行し、通知が届くかを確認します。本番サーバーでメモリを意図的に使い切る負荷試験は、サイトが止まる原因になるので避けてください。負荷をかけて試したい場合は、ステージング環境で行います。

sudo /usr/local/bin/memory-alert.sh
journalctl -t memory-alert --since "10 min ago"

確認が終わったら、しきい値を元に戻し、状態ファイル(/var/tmp/memory-alert.last)を削除しておくと、次の本当の通知がクールダウンで止められずに済みます。

OOM Killerの発動を見つける

メモリが尽きると、カーネルは OOM Killer でプロセスを選んで強制終了します。WordPress のサーバーでは、メモリを多く使う mysqld が選ばれやすく、「突然データベース接続エラーが出た」という症状で気づくことがよくあります。記録はカーネルのログに残ります。

journalctl -k --since "24 hours ago" | grep -iE "out of memory|oom-kill"

この確認を定期的に行い、見つかったら通知するようにしておけば、MemAvailable の通知を見逃した場合の保険になります。

Prometheusを使っている場合のアラートルール

すでに Prometheus と node_exporter で監視しているなら、同じ判定をアラートルールで書けます。継続時間の判定を for で指定できるので、スクリプトより素直に書けます。

groups:
  - name: memory
    rules:
      - alert: LowMemoryAvailable
        expr: node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes < 0.10
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "{{ $labels.instance }} の利用可能メモリが10%を下回っています"

      - alert: OomKillDetected
        expr: increase(node_vmstat_oom_kill[10m]) > 0
        labels:
          severity: critical
        annotations:
          summary: "{{ $labels.instance }} でOOM Killerが動作しました"

node_vmstat_oom_kill は node_exporter の vmstat 収集器が出す指標で、カーネルや node_exporter の設定によっては取得されない場合があります。Prometheus の画面で値が出るかを確認してからルールに入れてください。通知の送り先は Alertmanager の設定で決めます。

通知が来たときの調べ方

通知が来たら、まず何がメモリを使っているかを確認します。

free -h
ps -eo pid,rss,comm --sort=-rss | head -15

WordPress のサーバーで多い原因と確認の順番は次のとおりです。

  1. PHP-FPM の子プロセスが多すぎる: 1プロセスあたりのメモリ × 最大子プロセス数(pm.max_children)が、実メモリから MySQL などの分を引いた量を超えていないかを確認します。アクセスが集中すると子プロセスが上限まで増え、一気にメモリを使い切ります。
  2. MySQL のバッファ設定が大きすぎる: innodb_buffer_pool_size などが、サーバーのメモリに対して大きすぎないかを確認します。
  3. 一時的な重い処理: バックアップ、画像の一括再生成、重いプラグインの定期処理などが通知の時刻と重なっていないかを、cron やプラグインの実行時刻と突き合わせます。
  4. メモリリーク: 時間とともに特定のプロセスのメモリが増え続け、再起動で戻る場合はリークが疑われます。

スワップを追加すると OOM による強制終了は起きにくくなりますが、ディスクを使うぶん処理は遅くなります。スワップは時間を稼ぐ緩衝材と考え、根本的には上の設定の見直しやメモリの増設で対処します。設定を変える前には、設定ファイルのバックアップを取ってください。

まとめ

  • メモリの通知は「使用率」ではなく MemAvailable の割合(または残量)で判定します。
  • 一時的な落ち込みで鳴らないよう継続時間やクールダウンを入れ、OOM Killer の発動は別途すぐ知らせる対象にします。
  • 監視基盤がなければシェルスクリプトと cron で始められます。Webhook の URL は root だけが読めるファイルに分けて管理します。
  • Prometheus を使っているなら、node_exporter の指標と for を使ったアラートルールで同じ判定ができます(2026年10月現在の node_exporter の指標名で説明しています)。
  • 通知が来たら上位プロセスを確認し、PHP-FPM の子プロセス数、MySQL のバッファ、重い定期処理の順に原因を探します。

お気軽にご相談ください

お見積りへ お問い合わせへ
▲