「サーバーが重くなった原因、すぐに気づけますか?」
定期的なCPUやメモリの使用率チェックを怠ると、いざという時にボトルネックを特定できず、障害対応が後手になります。
本記事では、RHEL系Linux環境で動作する軽量な監視スクリプトを使い、CPU・メモリの使用率を自動で監視し、異常をログで通知する仕組みを紹介します。
「ZabbixやPrometheusは大げさすぎる…」と感じている方にこそ、最小構成で始められる本スクリプトは最適です。
スクリプトの目的と特徴
システム運用において、リソース使用率の監視は避けて通れない要素の一つです。
特にCPUやメモリといった主要なリソースは、過剰な使用が続けば性能劣化や障害を引き起こします。商用の監視ツールを使えば多機能な可視化が可能ですが、一方で導入コストや学習コストが高く、「そこまで大げさな運用は不要」という場面も少なくありません。
今回紹介するスクリプトは、RHEL系Linux環境での汎用的な運用を前提に、必要最小限の構成でCPU・メモリの使用率を自動で監視し、しきい値を超えた際にログ通知を行う仕組みです。軽量でcronから定期実行可能なため、既存のLinuxサーバーに即時導入できます。
CPU・メモリの使用率を自動監視
このスクリプトは、topやfreeといった標準的なLinuxコマンドを活用し、現在のCPUおよびメモリ使用率をリアルタイムで取得します。
取得した使用率は、あらかじめ設定された警告・致命的なしきい値と比較され、超過した場合はログに記録されます。また、しきい値の超過が複数回繰り返された場合、特定のログコードを通じて通知を発生させます。
以下は、使用率の取得処理の一例です。
getCpuUtilization() {
top -bn1 | grep "Cpu(s)" | \ awk -F'id,' '{ split($1, vs, ",");
v=vs[length(vs)]; sub("%", "", v); printf("%.0f", 100 - v) }'
}
getMemUtilization() {
free | awk '/Mem:/ { printf("%.0f", ( ($2 - $7) / $2 ) * 100 ) }'
}
これらの関数は、毎回のスクリプト実行時にCPUやメモリの現在使用率を即座に取得し、スクリプト内部で整数値に変換して扱います。そのため、外部の統合監視ソリューションを使わなくても、しきい値管理に必要な最低限の情報は自前でカバーできます。
RHEL系Linuxで汎用的に動作可能
このスクリプトはRHEL7以降の環境を前提としており、CentOS StreamやAlmaLinux、Rocky Linuxなど、RHEL互換ディストリビューションでも問題なく動作します。
依存するのはtop、free、awk、grepなどの標準コマンドのみであり、追加のパッケージインストールは不要です。また、設定ファイルと記録ファイルの保存先も、スクリプト内でベースディレクトリを指定して分離されているため、サーバーごとの運用ポリシーに応じた柔軟なパス設定が可能です。
以下は、設定ファイルと記録ファイルの例です。
ファイル名 | 役割 | 内容形式 |
---|---|---|
cpu_threshold.conf | CPUしきい値設定 | 3 75% 90% |
mem_threshold.conf | メモリしきい値設定 | 3 70% 85% |
cpu_alert.rep | CPUの異常検知履歴 | 88% 2025-07-30 10:00:00 |
mem_alert.rep | メモリの異常検知履歴 | 82% 2025-07-30 11:30:00 |
これらのファイルをサーバーごとに個別に保持することで、複数サーバーの並列監視や記録保持も柔軟に対応できます。また、ファイル形式は非常にシンプルな構造であるため、エンジニアでなくても保守・変更がしやすい点も特徴です。
スクリプトの構成と動作の仕組み
このスクリプトは、汎用的なCPU・メモリ使用率の監視を目的としながらも、非常にシンプルかつ堅牢な構造を持っています。
処理全体は主に4つのパートで構成されており、リソース種別の判定、使用率取得、異常判定と記録、ログ出力という一連の流れが一度の実行ごとに完結します。
また、共通関数やログ出力の仕組みを外部スクリプト化しているため、保守性が高く、他の監視スクリプトへの転用もしやすくなっています。
ここでは、しきい値を管理する設定ファイルの構造と、ログ記録と通知ロジックの流れについて詳しく解説します。
設定ファイルでしきい値を管理
本スクリプトは、監視対象がCPUかメモリかに応じて、対象のしきい値設定ファイルを読み込みます。設定ファイルは各ホスト単位で作成され、cpu_threshold.conf または mem_threshold.conf という名前で管理されます。
いずれのファイルも、1行目に次の3つの値を記述します。
項目名 | 意味 | 例 |
---|---|---|
回数 | 警告の連続回数しきい値 | 3 |
警告しきい値 | 使用率がこの値以上で警告 | 75% |
致命しきい値 | 使用率がこの値以上で致命 | 90% |
実際のファイルの中身は非常にシンプルで、以下のようになります。
3 75% 90%
スクリプトではこのファイルを read コマンドと awk で読み取り、変数 threshold_count, warn_limit, critical_limit に分解して使用します。
この構造によって、1ファイルで複数しきい値を一括で管理でき、ログ通知の条件分岐もシンプルになります。また、ファイル冒頭にコメントを記述することもできるため、設定の意図や履歴を明示しておくことも可能です。
ログ記録と異常通知のロジック
しきい値を超えた場合には、その時点の使用率と日時を記録ファイルに追記します。
この記録ファイルは /tmp などの一時領域に作成され、リソースごとに cpu_alert.rep や mem_alert.rep という名前で区別されます。
記録形式は以下の通りです。
84% 2025-07-30 10:33:15
スクリプトはこの記録ファイルの行数を毎回チェックし、threshold_count に設定された回数を超えていた場合に、あらかじめ定義されたログコードを用いて通知を発生させます。
ログ出力の仕組みは logger.shrc を通じて logSystem 関数を呼び出し、syslog連携や外部ログ監視にも対応できます。
if [ "$count_exceed" -ge "$threshold_count" ]; then
if [ "$type" = "cpu" ]; then
[ "$usage_int" -ge "$critical_limit" ] && logSystem "21004" || logSystem "11004"
else
[ "$usage_int" -ge "$critical_limit" ] && logSystem "21003" || logSystem "11003"
fi
fi
このように、ログコードはリソース種別と通知レベルによって個別に定義されており、たとえばCPUの致命的異常には 21004、メモリの警告には 11003 といったコードが割り当てられています。
これにより、ログ管理側では出力元と異常レベルを即座に識別でき、外部監視ツールへの連携にも柔軟に対応できます。
また、使用率が警告しきい値を下回った場合には、記録ファイルが自動的に初期化される仕組みとなっており、一時的なスパイクによる誤検知を防ぐ設計がなされています。これにより、記録ファイルの肥大化や無駄な通知の増加を抑えることができます。
インストールと実行方法
このリソース監視スクリプトは、RHEL系Linuxで動作することを前提としていますが、導入に際して特殊なモジュールやパッケージのインストールは不要です。
必要なのは、一般的なLinux環境に標準で搭載されているコマンド群(top、free、awk、grepなど)だけであり、外部依存性は極めて低く抑えられています。
ここではスクリプトの設置方法と、実行にあたっての注意点、さらに出力されるログや記録ファイルの挙動について解説します。
リソース監視スクリプト
本記事で紹介している resource_alert.sh は、CPUおよびメモリ使用率を対象に、しきい値の超過や異常継続を検知するためのシェルスクリプトです。ここでは実際のスクリプト全体を掲載し、構成やロジックの参考としてご利用いただけるようにしています。運用環境に応じてカスタマイズ可能な設計となっており、ベーススクリプトとして活用することができます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 |
#!/bin/sh #_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/ # スクリプト名 :resource_alert.sh # 概要 :CPUまたはメモリ使用率を監視し、しきい値超過を通知 # 説明 : # 本スクリプトは、システムリソースのうち CPU またはメモリを対象として、 # 使用率を取得し、事前に設定された WARN/CRITICAL しきい値と比較します。 # 閾値を超過した回数を記録し、指定回数以上となった場合にログ通知を行います。 # 対象リソースは `-m cpu` または `-m mem` により明示的に指定します。 # 関連設定は /etc 配下の threshold.conf、記録は tmp 配下の repファイルに出力されます。 # 使用率の取得には `top`(CPU)および `free`(MEM)コマンドを使用します。 # ログは logSystem 関数を介して user.warn / user.err として一元出力されます。 # # 使用方法 :sh resource_alert.sh -m cpu # :sh resource_alert.sh -m mem # # 依存関係 :logger.shrc / utils.shrc / cpu_threshold.conf / mem_threshold.conf # # 注意事項 : # - 引数は必ず -m オプションで指定すること(getopts必須) # - 使用率は内部で自動取得され、外部引数では上書きできない # - 閾値ファイル形式は「回数 警告% 致命%」の順で1行目に定義される # - ログIDはリソース別に固定:CPU=11004/21004、MEM=11003/21003 # - スクリプトはロック機構を持ち、同時実行を排除する # #_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/ # <変更履歴> # Ver. 変更管理No. 日付 更新者 変更内容 # 1.0 SYS-00001 2025/07/30 Bepro 新規作成(CPU/MEM統合) #_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/ # ------------------------------------------------------------------ # 初期処理 # ------------------------------------------------------------------ . "$(dirname "$0")/../com/utils.shrc" . "$(dirname "$0")/../com/logger.shrc" setLANG utf-8 # ------------------------------------------------------------------ # 変数定義 # ------------------------------------------------------------------ scope="var" readonly JOB_OK=0 readonly JOB_ER=2 host_id=$(hostname -s) exec_time=$(date "+%Y-%m-%d %H:%M:%S") type="" threshold_file="" record_file="" usage_val="" usage_int="" # ------------------------------------------------------------------ # 関数定義 # ------------------------------------------------------------------ scope="func" # 終了処理(ロック解除) terminate() { releaseLock } # 使用方法表示 usage() { cat <<EOUSAGE ----------------------------------------------------------------- Usage: $0 -m <type> Options: -m type : Specify resource type to monitor (cpu or mem) Example: $0 -m cpu $0 -m mem ----------------------------------------------------------------- EOUSAGE exit ${JOB_ER} } # 引数解析(getopts) parseArgs() { while getopts "m:" opt; do case "$opt" in m) type="$OPTARG" ;; *) usage; exit ${JOB_ER} ;; esac done # -m 未指定または不正値ならエラー if [ -z "$type" ] || ! echo "$type" | grep -qE '^(cpu|mem)$'; then logOut "ERROR" "Invalid or missing -m argument." usage fi } # CPU使用率取得(整数%) getCpuUtilization() { top -bn1 | grep "Cpu(s)" | \ awk -F'id,' '{ split($1, vs, ","); v=vs[length(vs)]; sub("%", "", v); printf("%.0f", 100 - v) }' } # メモリ使用率取得(整数%) getMemUtilization() { free | awk '/Mem:/ { printf("%.0f", ( ($2 - $7) / $2 ) * 100 ) }' } # 閾値・記録ファイルのロード loadThreshold() { threshold_file="$ETC_PATH/$host_id/${type}_threshold.conf" record_file="$NNN_TMP_PATH/${type}_alert.rep" [ ! -f "$threshold_file" ] && logOut "ERROR" "Missing threshold file: $threshold_file" && exitLog ${JOB_ER} [ ! -f "$record_file" ] && touch "$record_file" read threshold_count warn_limit critical_limit < <( grep -v '^\s*#' "$threshold_file" | head -n 1 | awk '{gsub(/%/, "", $2); gsub(/%/, "", $3); print $1, $2, $3}' ) } # ------------------------------------------------------------------ # pre-process(事前処理) # ------------------------------------------------------------------ scope="pre" parseArgs "$@" startLog logOut "INFO" "Args: -m $type" if acquireLock; then logOut "INFO" "Lock acquired" else abort "Lock acquisition failed." fi trap "terminate" 0 1 2 3 15 loadThreshold # ------------------------------------------------------------------ # main-process(メイン処理) # ------------------------------------------------------------------ scope="main" if [ "$type" = "cpu" ]; then usage_val=$(getCpuUtilization) else usage_val=$(getMemUtilization) fi usage_int=$(echo "$usage_val" | awk '{printf("%d", $1)}') logOut "INFO" "Threshold(WARN): ${warn_limit} %" logOut "INFO" "Current Usage : ${usage_int} %" logOut "DEBUG" "Execution Time : ${exec_time}" # 使用率が警告以上なら記録 if [ "$usage_int" -ge "$warn_limit" ]; then echo "${usage_int}% $exec_time" >> "$record_file" logOut "WARN" "Usage exceeded: ${usage_int}%" else if [ -s "$record_file" ]; then > "$record_file" logOut "INFO" "Reset alert history: $record_file" fi logOut "DEBUG" "Usage within normal range." fi # 超過カウント取得 count_exceed=$(wc -l < "$record_file" | tr -d ' ') logOut "INFO" "Exceed count: $count_exceed" # 致命的判定とログ出力(対象別) if [ "$count_exceed" -ge "$threshold_count" ]; then if [ "$type" = "cpu" ]; then [ "$usage_int" -ge "$critical_limit" ] && logSystem "21001" || logSystem "11001" else [ "$usage_int" -ge "$critical_limit" ] && logSystem "21002" || logSystem "11002" fi fi # ------------------------------------------------------------------ # post-process(終了処理) # ------------------------------------------------------------------ scope="post" exitLog ${JOB_OK} |
前提となる実行環境
Beエンジニアでシェルスクリプトを実行する環境は下記の通りとします。
実行環境
BASE_DIR(任意のディレクトリ)
- scripts
- bin(実行スクリプト格納領域)
- <<各種実行スクリプト>>.sh (実行ファイル)
- com(共通スクリプト格納領域)
- logger.shrc(共通ログ出力ファイル)
- utils.shrc(共通関数定義ファイル)
- etc(設定ファイル等の格納領域)
- infraMessage.conf(メッセージ定義ファイル)
- log(スクリプト実行ログの格納領域)
- スクリプト名.log
- tmp(テンポラリ領域)
- rep(レポート出力領域)
- bin(実行スクリプト格納領域)
配置ディレクトリと前提条件
本スクリプトは以下のディレクトリ構造で管理することを前提としています。
この構成を守ることで、共通関数やログ出力機構との連携が正しく動作し、保守管理も容易になります。
ディレクトリ | 用途 |
---|---|
scripts/bin/ | resource_alert.sh 本体配置場所 |
scripts/com/ | logger.shrc、utils.shrc 等の共通関数 |
scripts/etc/ | cpu_threshold.conf や mem_threshold.conf の格納 |
scripts/tmp/ | 記録ファイル(cpu_alert.rep、mem_alert.rep)保存先 |
このほか、ログ出力先として scripts/log/ ディレクトリを別途設けておくことも推奨されます。各スクリプトには共通して logger.shrc を読み込む処理が含まれており、ログ出力にはこのスクリプトの logOut 関数を用います。
また、スクリプト実行には実行権限が必要です。viなどで編集した後は、必ず実行権限を付与してください。
chmod +x resource_alert.sh
さらに、cronなどで定期実行する場合は、相対パスではなく絶対パスで呼び出すようにしてください。これはcron環境下では環境変数が制限されており、想定通りの読み込みができないケースがあるためです。
実行コマンドと出力結果の例
実行コマンドは非常にシンプルです。CPUまたはメモリのどちらを監視するかを引数で指定します。
./resource_alert.sh -m cpu
./resource_alert.sh -m mem
cronに登録する場合は、例えば以下のように設定します。
*/5 * * * * /home/user/scripts/bin/resource_alert.sh -m cpu > /dev/null 2>&1
実行結果は標準出力には表示されず、ログファイルまたはシステムログへ記録されます。
異常検知のログ出力は、事前に設定されたログID体系に従って出力されます。たとえば、CPUの使用率が90%を超えた場合には、以下のような出力が発生します。
user.err [21004] [CPU使用率異常] CPUの稼働率が許容範囲を大きく超過しました。
逆に、使用率がしきい値以下で安定している場合は、次のようなログが記録されるのみで、通知は発生しません。
INFO Usage within limits.
また、記録ファイルには次のように使用率と実行日時が1行ずつ記録されます。
84% 2025-07-30 10:33:15
このファイルの行数が設定ファイルに記載された閾値回数を超えた場合にのみ、ログ通知が行われる設計になっています。よって、一時的なスパイクによる誤検知を防ぎつつ、連続する異常を的確に捉えることができます。
運用における注意点と改善例
リソース監視スクリプトは一度導入すれば自動で動作し続けるものですが、安定運用のためにはいくつか注意点があります。
誤検知を減らし、記録ファイルを適切に扱うこと、そして将来的な他の監視項目への拡張可能性を踏まえた設計が重要になります。ここでは、実際の運用フェーズで遭遇しやすい課題と、それに対する改善方針を紹介します。
誤検知の抑制と記録ファイル管理
リソース使用率の一時的なスパイクはよくある現象です。
このスクリプトでは、あらかじめ設定ファイルに記載された「しきい値回数」を使って、連続した異常のみを通知対象とする仕組みで誤検知を抑制しています。
記録ファイルは /tmp などの一時領域に格納され、スクリプトごとに以下のように分けられています。
記録ファイル | 対象リソース |
---|---|
cpu_alert.rep | CPU使用率 |
mem_alert.rep | メモリ使用率 |
記録形式は非常にシンプルで、使用率と実行時刻を1行に記録します。
88% 2025-07-30 09:15:42
一定回数分の異常が記録されると、通知ログが出力される設計ですが、逆に正常値に戻った場合にはこの記録ファイルは自動で初期化されます。これにより、異常が継続しているかどうかを視覚的に確認することも可能です。
ただし、記録ファイルが予期せぬ理由で削除された場合や、複数プロセスから同時に書き込まれた場合には破損する恐れがあります。
このため、スクリプト内部ではロックファイルによる排他制御が導入されており、同時実行を防止しています。また、cronの設定で間隔が短すぎると、処理の重複が起きやすくなるため、5分〜10分間隔の実行が推奨されます。
*/5 * * * * /path/to/resource_alert.sh -m cpu
ログの蓄積量についても注意が必要です。syslogと連携させている場合は、logrotateの設定を確認し、出力量が多すぎる状況が発生していないかを定期的に見直す必要があります。
他監視項目への拡張可能性
resource_alert.sh は、CPUやメモリなど、対象が単一で、しきい値が「上方向のみ」の数値評価で済むものに限定して設計されています。
この構造は非常にシンプルで明快ですが、他の監視対象すべてに流用できるわけではありません。
たとえばディスク使用率の監視では、複数のファイルシステム(/、/var、/homeなど)ごとに個別に評価が必要になります。
このような「対象が複数存在し、全てに対してループ処理が必要な監視設計」は、resource_alert.sh のロジックとは完全に異なります。
また、プロセス数監視のように、評価が「特定の範囲内にあるか」を見るタイプの監視も、現在のスクリプトでは構造的に非対応です。
こうした対象を監視するには、評価ロジック・設定ファイル構造・ログ出力方法などを根本的に分けて設計する必要があります。
そのため、resource_alert.sh は「単一リソース × 上方向評価」に特化した小型モジュールとして割り切り、その他の監視は別スクリプトに分離する方が、メンテナンス性・可読性ともに優れた構成となります。
まとめと次のステップ
resource_alert.sh は、シンプルな構造でありながら、CPUやメモリといった基本的なリソースの使用率を安定的に監視できるスクリプトです。
その最大の特徴は、ログ出力やしきい値設定、異常継続判定といった基本的な機能を標準装備しており、実環境にそのまま導入できる汎用性にあります。
ただし、スクリプトは単体で完結するものではなく、サーバー運用全体の中に組み込んで初めて真価を発揮します。このセクションでは、運用面での展開方法と、さらに広い監視対象への拡張の可能性について検討します。
cron連携による常時監視
resource_alert.sh を現場で活用するためには、一定間隔での定期実行が前提になります。
その際に有効なのが、cron との連携です。たとえば以下のような cron 設定を /etc/cron.d/resource_alert に記述すれば、1分間隔での常時監視が実現できます。
* * * * * root /opt/scripts/bin/resource_alert.sh -m cpu
* * * * * root /opt/scripts/bin/resource_alert.sh -m mem
このように CPU・メモリごとに個別に監視処理を行うことで、処理の独立性を維持しながら並列に監視を継続できます。また、実行ログが蓄積されるよう設計されているため、cron による定期実行結果もそのまま検証可能です。
さらに、1分間隔での監視は過剰と感じる場合には、5分ごとや10分ごとといった間隔に調整することも容易です。しきい値と同様、現場のサーバースペックや用途に応じて柔軟に設計できる点がこのスクリプトの利点です。
プロセス・ディスク監視との連動提案
CPUやメモリ以外にも、監視すべきリソースは多く存在します。代表的なものに、プロセス数やディスク使用率がありますが、これらを単純に resource_alert.sh に追加するという運用は現実的ではありません。理由はロジックの非互換性です。
まず、プロセス数の監視では、「一定の数を下回っても上回っても異常」となるケースが多く存在します。このような両方向のしきい値評価は、現在の「上方向のみの連続異常カウントロジック」とは根本的に異なります。さらに、監視対象プロセスが動的に変化する場合には、設定ファイル自体を対象プロセスごとに分割する必要があり、構造が複雑化します。
また、ディスク使用率監視では、対象が複数(/、/var、/home など)存在することが前提となります。この場合には、設定ファイルの形式からループ処理、判定ロジック、ログ出力まで全てが「マルチターゲット」前提で設計されなければなりません。
以下に、比較表を掲載します。
監視対象 | 判定方式 | 複数対象 | resource_alert.sh 対応 |
---|---|---|---|
CPU | 上方向のみ | 単一 | 対応済み |
メモリ | 上方向のみ | 単一 | 対応済み |
プロセス数 | 上下限あり | 単一〜複数 | 非対応 |
ディスク | 上方向のみ | 複数 | 非対応 |
このように、対象ごとの監視ロジックが大きく異なるため、汎用化よりも「監視対象ごとに専用スクリプトを分離する」方針が合理的です。
共通部分(ログ出力、しきい値管理、通知処理など)は共通関数クラスに切り出し、それぞれの監視モジュールから呼び出す構造にすることで、一貫した設計のままスケーラブルな監視基盤を実現することができます。