Shellスクリプトのみを使って今監視ソフトを作るとしたら
はじめに
本記事はQiita夏祭り2020の 「 〇〇(言語)のみを使って、今△△(アプリ)を作るとしたら」のテーマ記事となります.
環境
- ubuntu18.04 LTS
-
/bin/sh
(dash)
できたもの
/bin/sh
(dash)https://github.com/taro-hida/shell-mon
出力は以下のようになっています。
1行目:実行されたコマンドの内容
2行目:コマンドの出力(あれば)
3行目:コマンドの結果
実行するコマンドを変更することで、様々な監視を行うことができます。
実行するコマンドはファイルで指定します。一行ずつ読み込まれて実行されます。
今回であれば、以下のような内容です。
exit 0
exit 1
ping google.com -c 1 > /dev/null 2>&1
curl https://google.com/ > /dev/null 2>&1
mem=`ssh example.com -C 'free' 2> /dev/null | grep Mem | awk '{print $3*100/$2}'`;echo 'Mem Use: '$mem' %';if [ `echo $mem | awk '{printf("%d", $1)}'` -le 60 ]; then exit 0; else exit 1; fi
監視ソフトのアーキテクチャ
Nagiosをモデルに実装しました。Nagiosのアーキテクチャについては、ペパポテックブログさんより画像を引用しました。以下のような形となります。
今回実装する機能を切り出して図にしてみました。以下のような感じです。
必要な部品は以下となります。
- 監視スケジューリングを行う
- プラグイン(実際の監視を行い、結果を返す)
- 監視プラグインを実行する
- 監視の結果を判定、表示する
- 監視の結果に応じて、アクションを実行する
監視スケジューリングを行う
何かを定期的に実行する、と言われてまず思い浮かぶのは crond ですが、
crond を使ってしまうと 「Shellスクリプトのみを使って」と言えなくなってしまいそうです。
Shellスクリプトの制御構文を使って実装しましょう。
+ #!/bin/sh
+ while true
+ do
+ sleep 60
+ done
無限ループを用いて、1分に一度何かを実行してくれるスクリプトが書けました。
これで監視実行のスケジュールとします。
プラグイン(実際の監視を行い、結果を返す)
プラグインにはシェルコマンドを利用します。
コマンドを実際に実行し、実行後の "exit status" を $?
で取得、0であれば成功と判断します。
成功の場合は0を、失敗の場合は0以外を返すようにプラグインを設計すれば、独自の監視プラグインの作成が可能です。
例えばpingの監視であれば ping qiita.com -c 5
のようなコマンドラインです。
疎通成功のケース
$ ping qiita.com -c 1
...
--- qiita.com ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
$ echo $?
0
疎通失敗のケース
$ ping qiita.com -c 1
...
--- qiita.com ping statistics ---
1 packets transmitted, 0 received, 100% packet loss, time 0ms
$ echo $?
1
これも、Nagiosの実装を参考にしています。
Nagios による監視のステータスというのは 4 種類あって、 OK, WARNING, CRITICAL, UNKNOWN ですが、これは Nagios プラグインの exit status にてそれぞれ 0, 1, 2, 3 に対応しています。つまり、
ステータスのラベル EXIT STATUS の値 OK 0 WARNING 1 CRITICAL 2 UNKNOWN 3 ということです。
同様に、httpsの疎通監視であれば curl
コマンドを用いて curl -s https://qiita.com/
を実行します。
監視プラグインを実行する
監視プラグインはシェルコマンドを使用し、fileから1行ずつ読み込んで実行することにしましょう。
check_command.txt
というファイルを用意し、そこに実行したいコマンドを羅列しておきます。
$ cat check_command.txt
echo 'しぇる'
ping qiitadon.com -c 1
curl https://qiita.com/
ファイルからのプラグイン読み込みと実行を先ほどのスクリプトに合わせて実装します。
#!/bin/sh
while true
do
+ while read cmd; do
+ echo "$cmd"
+ $cmd > /dev/null 2>&1
+ done < check_command.txt
sleep 60
done
監視の結果を判定する
前述の通り、監視の結果は "exit status"にて判定します。
Shellスクリプトの if ~ else
構文を用いて実装しましょう。
#!/bin/sh
while true
do
while read cmd; do
echo "$cmd"
$cmd > /dev/null 2>&1
+ if [ $? -eq 0 ]
+ then
+ echo 'OK'
+ else
+ echo 'NG'
+ fi
done < check_command.txt
sleep 60
done
かなり形になってきましたね。
監視の結果に応じて、アクションを実行する
「監視結果がNGだった際、メールを送信する」等のアクションを実装しましょう。
これも、スクリプトにアクションをベタ書きします。
#!/bin/sh
while true
do
while read cmd; do
echo "$cmd"
$cmd > /dev/null 2>&
if [ $? -eq 0 ]
then
echo 'OK'
else
echo 'NG'
+ echo 'NG' | mail root@localhost
fi
done < check_command.txt
sleep 60
done
完成
ということで、完成です!
#!/bin/sh
while true
do
while read cmd; do
echo "$cmd"
$cmd > /dev/null 2>&1
if [ $? -eq 0 ]
then
echo 'OK'
else
echo 'NG'
echo 'NG' | mail root@localhost
fi
done < check_command.txt
sleep 60
done
監視ソフトとしての基本的な機能は押さえて、かつ理解しやすいシンプルな形で実装できたように思います。
ただ、この状態ではいくつか不便な点が出てきます。
出力が確認できない
標準・エラー出力を/dev/null
へのリダイレクトで殺してしまったので、結果の表示の柔軟性が失われてしまいました。
Nagios先生よろしく、標準出力を画面に出せるようにしましょう。ping qiita.com
など、自動で終了しないコマンドを発行した場合処理が進まなくなる。
ping qiita.com
などを指定すると、現在の実装では永遠にpingを打ち続け、そこから処理が進まなくなってしまいます。
監視のタイムアウトを設定します。例えば、プラグインの実行に10秒以上かかった場合は処理を中断するようにしましょう。
標準出力を画面に出せるようにする
コマンド実行を、子シェルにて実行するように変更します。
#!/bin/sh
while true
do
while read cmd; do
echo "$cmd"
- $cmd > /dev/null 2>&1
+ sh -c "$cmd"
if [ $? -eq 0 ]
then
echo 'OK'
else
echo 'NG'
echo 'NG' | mail root@localhost
fi
done < check_command.txt
sleep 60
done
監視のタイムアウト設定
timeoutコマンドを利用して、監視のタイムアウトを実装しましょう。
#!/bin/sh
while true
do
while read cmd; do
echo "$cmd"
- sh -c "$cmd"
+ timeout -k 15 10 sh -c "$cmd"
if [ $? -eq 0 ]
then
echo 'OK'
else
echo 'NG'
echo 'NG' | mail root@localhost
fi
done < check_command.txt
sleep 60
done
timeoutコマンドは、指定した時間(今回は10秒)が経過しても引数にとったコマンドが動いていた場合、TERMシグナル(シグナルの数値は15)をコマンドに送信してくれるコマンドです。
なので、指定した時間より前にコマンドが終了した場合は何も行わず、その "exit status" は変化しません。
$ timeout 1 test 1 -gt 5
$ echo $?
1
$ timeout 1 test 5 -gt 1
$ echo $?
0
TERMシグナルにてコマンドが終了した場合は、 "exit status" が124となります。
$ timeout 1 ping google.com
...
$ echo $?
124
リファクタリング
最後に、できる限りスッキリした形にスクリプトの構造を変更しましょう。
#!/bin/sh
MESSAGE_OK='--> OK'
MESSAGE_NG='--> NG'
ACTION_NG='echo NG | mail root@localhost'
CHECK_COMMAND_FILE_PATH='./check_command.txt'
CHECK_INTERVAL=60
# $1: check plugin command
exec_check()
{
timeout -k 15 10 sh -c "$1"
if [ $? -eq 0 ]
then
printf "\033[32m%s\033[m\n" "${MESSAGE_OK}"
else
printf "\033[31m%s\033[m\n" "${MESSAGE_NG}"
sh -c "${ACTION_NG}"
fi
}
while true
do
while read cmd; do
echo "$cmd"
exec_check "$cmd"
echo '-------'
done < ${CHECK_COMMAND_FILE_PATH}
sleep ${CHECK_INTERVAL}
done
出力に色もつけてみました。
おわりに
Qiita夏「祭り」ということで何かの形でお祭りに参加したく、今回記事を執筆させていただきました。
監視用のShellスクリプトというのは何も珍しいものではなく、様々な用途で、数多くの現場で書かれてきたものかと思います。
思い切り車輪の再発明ですが、あくまで一つのネタ記事として楽しんでいただけましたら幸いです。
記事を書いてみて
- 既存の監視ツールのありがたみを感じました。
- diff 形式で追記していく形式の記事はもう二度と書きません。修正の際に全部訂正しないといけないから大変でした。
お世話になったサイト
Author And Source
この問題について(Shellスクリプトのみを使って今監視ソフトを作るとしたら), 我々は、より多くの情報をここで見つけました https://qiita.com/taro-hida/items/d529628fc01e19b984ea著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .