シェルスクリプト

【CPU使用率監視シェル】SNMPでサーバのCPU使用率を取得し、閾値判定する方法

SNMPでCPU使用率監視
悩み人

snmpを用いて、サーバのCPU使用率を取得し、閾値判定するシェルスクリプトの作り方を知りたい~!

閾値やサーバの宛先IPアドレスは運用者にて編集しやすいようにしておきたいな。

今回はこのような疑問をお答えします!

本記事の内容
  • snmpでサーバのCPU使用率を取得し、閾値判定するシェルスクリプトを紹介
  • 「CPU使用率が取得できるMIB」「CPU使用率MIBを取得する際のsnmpwalkコマンドのコツ」「イレギュラーパターンの考慮」といった、重要テクニックを解説
プロフィール画像

【この記事を書いた人】
らいし

30代システムエンジニア
 大手インフラ系企業SV/NW/社内SE経験
資格:応用情報,基本情報,CCNP,CCNA等
6年間Linux系サーバ保守を経験

サーバ監視で最も大切な要素の1つである”CPU使用率の監視”。

システム障害の暫定対策やサーバ監視ソフトウェアサポート切れ等個別にCPU使用率を監視したいシーンありませんか?

悩み人

あるある~!特に障害調査の一環で短期間の間だけCPU使用率を取得したい場合が多いんだよね。

この記事では、メンテナンス性の優れたSNMPによるCPU使用率監視シェルスクリプトを紹介します。
重要テクニックも解説していますので、色んなシーンに応用できる知識も身に付きますよ。

らいし

このシェルスクリプトは実際のSEの現場で使用したものですので、ほぼそのまま使えますよ

CPU使用率監視シェルスクリプトの概要

それでは今回紹介・解説するシェルスクリプトを説明していきます。
それほど難しくないので、気難しく考えなくてOKですよ。

動作概要

今回紹介するスクリプトの処理フローは次のようになります。

snmp-CPU使用率取得フロー
  1. 「被監視サーバIPアドレス」が記述されたファイルを1行ずつ読み込み変数格納
  2. 被監視サーバに対してMIB「HOST-RESOURCES-MIB::hrProcessorLoad」をsnmpwalkで取得し、論理プロセッサ毎の各MIB値の平均及び小数点以下を切り捨てた値を変数格納
  3. SNMPが取得できない(疎通が取れない)場合は、”疎通不可”の旨をloggerコマンドでシステムログに書き込み、処理終了。そうでない場合は④へ
  4. ②で取得した「現在のCPU使用率」と初期定義した「アラート閾値」を比較し、アラート閾値を上回る場合、”閾値超過”の旨を、下回る場合は”閾値以下”の旨をloggerコマンドでシステムログに書き込む

MIBとは、SNMPなどで遠隔から機器を監視・管理する際に用いられる、監視対象の機器が自らの設定や状態についてまとめたデータ集合。また、その形式や参照方法について定めた規格。

引用元:MIB 【Management Information Base】 管理情報ベース

被監視サーバ(snmpクライアント)はWindowsでもlinuxでも対応できます

動作確認環境

CentOS Linux release 7.9.2009 (Core)

この記事内では、被監視サーバ(snmpクライアント)のOSはWindowsでテストした結果を記述しております。

プログラムの難易度

【総合評価】
 (優しい)


<詳細>
【処理量】
(非常に少ない)
【分岐量】
(非常に少ない)
【高度なコマンド】
(普通)

らいし

snmpwalkコマンドの際、ちょっと一工夫するところが難しいかも

プログラムコード

#!/bin/bash

#------------パラメータ--------------#
tooldir=/work/cpu_chk/   #ツールの配置フォルダ
nd_li=${tooldir}nodelist         #被監視サーバリストファイルフルパス
community=public           #被監視サーバ側のコミュニティ名
CPU_Thr=90     #アラート閾値(%)
CPU_OID=1.3.6.1.2.1.25.3.3.1.2 #HOST-RESOURCES-MIB::hrProcessorLoad 直近1分間のCPU使用率(%) [chk1]
#----------------------------------#


while read line            #【while1】nodelistを読み込み最終行まで処理する
do
 #----CPU使用率をhrProcessorloadから取得する(小数点以下切り捨て)---#

 CPUUsed=`snmpwalk -Ovq -v 2c -c $community $line $CPU_OID |awk '{m+=$1} END{print m/NR;}' | awk '{printf("%d\n",$1)}'` #[chk2]
   ##上記の説明 1:snmpwalkでCPUの各論理プロセッサの数値のみ取得 | 2:平均をとる | 3:小数点を捨てる
 
  if [ -z "$CPUUsed" ]; then  #snmp取得が失敗して値がNULLの場合  [chk3]
    logger -p syslog.error "$lineのCPU使用率取得に失敗しました"

  else

 echo "$lineのCPU使用率は$CPUUsed%です"

 #------------閾値判定---------------------------#

    if [ $CPUUsed -ge $CPU_Thr ]; then   #現在のCPU使用率>=アラート閾値
         logger -p syslog.error "$lineのCPU使用率が$CPU_Thr%を超えました。"

      else
         logger -p syslog.info "$lineのCPU使用率は閾値以下でした"

    fi
 echo  #ただの改行

 fi
done < $nd_li  #読み込む非監視サーバリストファイルを指定
  • 転用する場合はパラメータ内の各変数をお好みの値に変更したうえでお使いください
  • テクニック解説するコードは「chk No」で表記しています

プログラム利用前の準備事項

らいし

今回紹介したCPU使用率監視シェルスクリプトを実行するためにはちょっとだけ事前準備が必要です

①次のコマンドを実行し、プログラム実行に必要となるディレクトリを作成してください。

mkdir /work/cpu_chk/

②次の例を参考に被監視サーバリストファイルを作成し、/work/cpu_chk/に配置してください。

[root@centos7 cpu_chk]# cat nodelist 
192.168.0.11
192.168.0.20
192.168.0.99

③被監視サーバにsnmpクライアントの設定をしてください。

snmpサービス設定は「管理者で実行」で管理画面を開かないとセキュリティタブが表示されませんので注意が必要です

プログラム実行結果

[root@centos7 cpu_chk]# ./cpu_chk.sh 
192.168.0.11のCPU使用率は6%です

192.168.0.20のCPU使用率は94%です

Timeout: No Response from 192.168.0.99
awk: コマンドライン:1: 致命的: ゼロによる除算が試みられました
[root@centos7 cpu_chk]# cat /var/log/messages
~中略~
Mar 10 10:10:47 centos7 root: 192.168.0.11のCPU使用率は閾値以下でした
Mar 10 10:10:47 centos7 root: 192.168.0.20のCPU使用率が90%を超えました
Mar 10 10:10:53 centos7 root: 192.168.0.99のCPU使用率取得に失敗しました

192.168.0.99は疎通の取れないIPアドレスです

らいし

各CPU使用率の値をファイルに残せば、トレンド管理にも利用できますね!

重要テクニック解説

さて、ここからは紹介したプログラムコードの中で、特に着目したい重要テクニックを解説していきます。
紹介するテクニックは、今回のプログラムだけではなく、色んなシーンで応用できるものになりますので必見です。

chk1:「HOST-RESOURCES-MIB::hrProcessorLoad」について

CPU_OID=1.3.6.1.2.1.25.3.3.1.2 #HOST-RESOURCES-MIB::hrProcessorLoad 直近1分間のCPU使用率(%) 

「HOST-RESOURCES-MIB::hrProcessorLoad(OID:1.3.6.1.2.1.25.3.3.1.2)」は直近1分間のCPU使用率(%)を論理プロセッサ単位に出力する標準MIBの1つです。
標準MIBはOIDの最初が「1.3.6.1.2.1」となっています。

標準MIBとはRFCによって定義された業界標準の規格です。各ベンダーで共通で利用することができ、どのメーカーの機器であっても、標準MIBを介することで画一的な情報の交換が可能になります。なお、現在の標準MIBとして位置づけられているのは「MIB-2」です。

引用元:こっそり聞きたいネットワークのキホン(第27回) ネットワーク機器が持つMIBについて解説

実際のsnmpwalkの実行結果は次のとおりです。(snmpwalkコマンドの書式解説は後述)

[root@centos7 cpu_chk]# snmpwalk -c public -v 2c 192.168.0.20 1.3.6.1.2.1.25.3.3.1.2
HOST-RESOURCES-MIB::hrProcessorLoad.4 = INTEGER: 33
HOST-RESOURCES-MIB::hrProcessorLoad.5 = INTEGER: 26
HOST-RESOURCES-MIB::hrProcessorLoad.6 = INTEGER: 34
HOST-RESOURCES-MIB::hrProcessorLoad.7 = INTEGER: 28
悩み人

あれ!?4行表示されているけど、これはどう見ればいいの?

4行表示されている理由は、論理プロセッサ単位でCPU使用率が表示されているからです。

今回MIBを取得したSNMPクライアント側の論理プロセッサ数を確認してみましょう。

WindowsOSなので、「タスクマネージャ」→「パフォーマンス」タブの論理プロセッサ数から確認できます。

SNMPクライアントCPU情報確認
らいし

今回は論理プロセッサが4つのクライアントだったので、4つ分のCPU使用率が表示されました

さて、サーバ全体としてのCPU使用率を求めるには、全論理プロセッサの平均値を求める必要があります。
また閾値判定をしやすいように小数点以下を切り捨てることもやってみたいと思います。

chk2:snmpwalkコマンド | awkコマンド について

 CPUUsed=`snmpwalk -Ovq -v 2c -c $community $line $CPU_OID |awk '{m+=$1} END{print m/NR;}' | awk '{printf("%d\n",$1)}'`

この1行で3つのコマンドを組み合わせ、次のことを同時に行っています。

  • 論理プロセッサ単位のCPU使用率を取得
  • 平均計算
  • 小数点以下切り捨て

1つずつ解説していきます。

snmpwalkコマンドとは

”snmpwalk”は指定した宛先IPアドレス・MIBに対して、snmp通信を行い、MIB値を取得するものです。

他にもsnmpgetコマンドやsnmpgetnextコマンド等類似するものがありますが、今回は複数のMIBを一括取得できるsnmpwalkを利用します。

またsnmpwalkコマンドにはv1,v2c,v3の3種類からバージョン指定が必要ですが、今回は最も扱いやすいv2cを利用します。

別記事にてsnmpコマンドを活用してプロセス監視を行うシェルスクリプトの紹介もしていますので、ぜひこちらもご覧ください。

【シェルスクリプト】snmpでサーバのプロセスを監視し、結果をシステムログに書き込む方法
【プロセス死活監視シェル】snmpでサーバのプロセス稼働状況を取得し、結果をシステムログに書き込む方法サーバプロセス監視をしたい方必見!この記事ではシェルスクリプトでsnmpを用いてプロセスを監視し、結果をシステムログに書き込む方法を紹介しています。プロセス監視って実はとても簡単に実現できます。この記事を読めば、snmpやプロセス監視に役立つテクニックを学習することができます。...

snmpwalkコマンドの書式

snmpwalk -v 2c -c <コミュニティ名> <通信相手> < OID >

< OID >を省略すると標準MIBを全て取得する処理になります。

snmpwalkコマンドのオプション

バージョン指定オプション”-v”、コミュニティ名指定オプション”-c”以外にもオプションはありますので参考に記載しておきます。

オプション意味
-VNet-SNMPのバージョンを表示します
-r試行回数を指定します。
-tリクエストのタイムアウト時間を秒で指定します。
-d通信されたパケットを16進数で出力してデバッグに使用します。

引用元:snmpwalkの使い方は?snmpwalkコマンドの使用方法を徹底解説

平均値を求める「awk ‘{m+=$1} END{print m/NR;}’」 について

awk(オーク)コマンドは、空白などで区切られたテキストを処理したり、演算したりすることができる便利コマンドです。

今回、平均値を求めるため、awkコマンドを活用します。

らいし

awkコマンドは書式に癖がありますが、便利なコマンドです

awkの主なオプション

オプション意味
-f ファイル名awkスクリプトが書かれたファイルを指定する
-F 区切り文字区切り文字を指定する(デフォルトは空白文字)
-v 変数名=値変数を定義する

引用元:【 awk 】コマンド(基本編)――テキストの加工とパターン処理を行う

awkで使用できる主な組み込み変数

変数名意味
ARGC引数の個数
ARGV引数(配列)
ENVIRON環境変数を収めた連想配列。例えば環境変数LANGならばENVIRON[“LANG”]と参照できる
FILENAME現在処理しているファイルの名前
FNR現在処理しているファイルのレコード番号(処理しているファイルが1つの場合はNRと同じ値になる)
FSフィールドの区切り文字(-Fオプションで変更可能、デフォルトはスペース)
NR現在処理しているレコード番号(行番号)
OFS出力時のフィールドの区切り(デフォルトは空白)
ORS出力時のレコードの区切り(デフォルトは改行)
RSレコードの区切り(デフォルトは改行)

引用元:【 awk 】コマンド(基本編)――テキストの加工とパターン処理を行う

awkコマンドは演算(合計や平均等)を行うことができます。
今回のコマンドを分解すると次のとおりとなります。

元々のコマンド:awk ‘ ①{m+=$1} END ②{print m/NR;}

  1. {m+=$1}  → 表示された値全てを一旦m変数に加算しながら格納 (合計)
  2. {print m/NR;} →変数mをNR(表示された行数を表示する変数)で除算 (平均)

これで平均値が求められます。

少数点以下を切り捨てる「awk ‘{printf(“%d\n”,$1)}’」について

さらに小数点以下を切り捨てるため同じくawkコマンドを応用します。
printf内について解説します。

  • %d:10進数整数
  • \n:改行
  • $1:今までのコマンドで表示された値(つまり少数点が含まれている、各論理プロセッサの平均値)

ちなみに四捨五入したい場合は、次のように”+0.5”すればOK

awk ‘{printf(“%d\n”,$1 + .0.5)}’

らいし

awkコマンドは便利ですけど、記述が独特で非常に難しいですね・・・

参考:シェルスクリプトで小数点以下を四捨五入する方法
参考:awkコマンド(テキストの加工やパターン処理をする)

chk3:「 if [ -z 値 ] 」について

if [ -z "$CPUUsed" ]; then  #snmp取得が失敗して値がNULLの場合

MIBを取得する被監視サーバと疎通が取れない場合を考慮する必要があります。

MIB値が取得できない場合、CPUUsed変数には何も値が入りません。
値が入っていないのであれば、その後の閾値判定処理をする必要がありません。

プログラマーとして余計な処理をさせないため、if条件式の-zを利用します。

<-zの意味>
文字列長が 0 ならば真

これで疎通が取れない場合の分岐を設けます。

特殊変数$?を分岐条件にしてはダメ?

悩み人

直前のコマンドの成功/失敗結果が格納される特殊変数$?を分岐条件に使ったらダメなの?

はい、今回は使えません。
理由は少数点を切り捨てるawkコマンドをパイプ(|)で組み合わせているため、snmpwalkが失敗しても実行結果としては0を返します。

特殊変数$?については別記事「宛先IPアドレスファイルを読み込みながらpingを実行し記録する方法」でも紹介していますので、ぜひこちらもご覧ください

シェルスクリプト
【ping死活監視シェル】宛先IPアドレスファイルを読み込みながらpingを実行し記録する方法シェルスクリプトでpingツールを作りたい方は必見!この記事では外部ファイルで定義された宛先IPアドレスを読み取りながらpingを実行し記録するシェルスクリプトを紹介します。とても簡単で重要テクニックの解説もあり!これは私が実際のSE現場で作成し活躍したものになります。...

$?が使えないことをテストプログラムで確認

次のようなテストプログラムを組み、各パターン毎で特殊変数$?の結果がどうなるか実験をしてみました。

#!/bin/bash

#------------初期定義--------------#
CPU_OID=1.3.6.1.2.1.25.3.3.1.2 #HOST-RESOURCES-MIB::hrProcessorLoad 直近1分間のCPU使用率(%) [chk-1]
#----------------------------------#

 #パターン1
 CPUUsed=`snmpwalk -Ovq -v 2c -c public 192.168.0.99 $CPU_OID |awk '{m+=$1} END{print m/NR;}' | awk '{printf("%d\n",$1)}'` 
   ##上記の説明 1:snmpwalkでCPUの各論理プロセッサの数値のみ取得 | 2:平均をとる | 3:小数点を捨てる
 echo "パターン1の特殊変数の結果は$?"

 #パターン2
 CPUUsed=`snmpwalk -Ovq -v 2c -c public 192.168.0.99 $CPU_OID |awk '{m+=$1} END{print m/NR;}'`
   ##上記の説明 1:snmpwalkでCPUの各論理プロセッサの数値のみ取得 | 2:平均をとる 
 echo "パターン2の特殊変数の結果は$?"

 #パターン3
 CPUUsed=`snmpwalk -Ovq -v 2c -c public 192.168.0.99 $CPU_OID`
   ##上記の説明 1:snmpwalkでCPUの各論理プロセッサの数値のみ取得 
 echo "パターン3の特殊変数の結果は$?"
悩み人

疎通が取れないsnmpクライアントに対して

  • パターン1:snmpwalk+平均値awk+小数点awk
  • パターン2:snmpwalk+平均値awk
  • パターン3:snmpwalkのみ

の3パターン毎の$?がどうなるか確認するプログラムですね

実行結果を確認してみます。

[root@centos7 cpu_chk]# ./test_cpu_chk.sh 
Timeout: No Response from 192.168.0.99
awk: コマンドライン:1: 致命的: ゼロによる除算が試みられました
パターン1の特殊変数の結果は0
Timeout: No Response from 192.168.0.99
awk: コマンドライン:1: 致命的: ゼロによる除算が試みられました
パターン2の特殊変数の結果は2
Timeout: No Response from 192.168.0.99
パターン3の特殊変数の結果は1

結果はこのようになりました。

らいし

どうも少数点を切り捨てるawkコマンドは元となる値がなくても成功扱いになるみたいですね。ちょっと不思議~

悩み人

ここまで学んできたが、やっぱりもう少し基礎からシェルについて勉強が必要だな~

そんな方のためにLinuxのことが学べるオススメの参考書を紹介しておきます。
初心者に優しく補足説明もしっかり入っており、入門者にはピッタリです。
これらは購入者も多く、人気の参考書です。

¥2,970 (2022/02/25 23:39時点 | Amazon調べ)

実際のSE現場で活躍した時の状況

idea

別記事「snmpでサーバのプロセス稼働状況を取得し、結果をシステムログに書き込む方法」でも紹介した内容と同様です。再掲になることをご了承ください。

私の会社では複数のサーバ(以降、被監視サーバ)を所管・運用しており、これらサーバのCPU使用率監視方法は、とあるベンダーのサーバ監視用パッケージソフトウェア(以降、サーバ監視ソフト)のエージェントを被監視サーバへインストールし、死活監視していました。

ある日、特定の被監視サーバのリプレースが必要になり、OSを

Windows Server 2008→Windows Server 2016

へ変更することになりました。

すると、サーバ監視ソフトのバージョンアップが必要となることがわかり、パッケージライセンスにより、非常に大きなコストとバージョンアップ作業工数がかかることがわかりました。

  • このサーバのSLA上、それほど精度の高いCPU使用率監視をしなくても良い要件
  • サーバ監視ソフトを実装しているサーバのリプレースも近々計画されていた


以上の理由から、コスト削減のため、今回のスクリプトを自ら作成しました。

良く言えば、コスト削減。悪く言えば、手を抜いた、というわけです。

今回は以上です。今回のシェルスクリプトは結構幅広く実務で使えるシェルではないでしょうか?私と同じ境遇でお困りの方の参考になれば幸いです。