pythonとcronで10分で作るWEBサイトの死活管理・監視(slack通知)

pythonとcronで作るWEBサイトの死活管理・監視(slack通知)

ざっくり目的

当社で運用保守を代行しているクライアント様のWEBサイトがちゃんと表示されているか確認したい。(心配性なので、、)

日本向けのウェブサイト監視ツール「モニタリングプラス」や「SavaMoni.」、外国製だと「Pingdom」などがあるものの、無料プランを超えてしまう量のサイト数だったりして、いちいち複数メールアドレスを登録するのも面倒だし、ましてや有料で監視するほどじゃないから、この際自作しちゃえということで作ってみました。

簡単な要件

  • 監視対象のURLは、同ディレクトリ内にあるcsvファイルから読み込む。
  • logファイルを年月日毎に生成する(例:20180107.log)がしかし、通知はしない。
  • アラートの条件は、ステータスコード200以外(リダイレクト時は、リダイレクト先のURLを評価)が発された時。
  • と、上記以外の想定外の例外エラー時にもアラート。
  • アラートはslackのincoming-webhookを用いて、任意のチャネル内に通知。本文はログをごっそり。
  • 間隔はざっくりと毎日4時間おきに。

ざっくりと必要なもの(環境)

  1. python3.6.3(python3系なら動くと思われ)
  2. requests(事前にpip install requestsしておく)
  3. 監視したいURLの一覧が記載されたcsvファイル(実行ファイルと同ディレクトリに置く)
  4. slackで事前に通知用のチャンネルを作っておく(「着信WEBフック」アプリも追加しておく)
  5. スクリプトの実行サーバー(今回はcentos7、macでも良き)

ざっくりなディレクトリ構成

simple_url_checker/
├ log/
│ └ 20180108.log
│ └ 20180109.log(日付別に今後増えていく)
├ main.py
└ target_urls.csv

ざっくりな流れ

  1. 上記のディレクトリ構成に従って、フォルダやファイルを作成。
  2. main.pyには、後で紹介するスクリプト全文をコピペ。(変数部分は各々変更。)
  3. python3が実行環境内にあるか確認(python -vpython3 -vをターミナルで打つ)
  4. ターミナル及びコマンドプロンプトで、上記のディレクトリに移動(cd simple_url_checkerとか)
  5. main.pyをpythonで実行(python main.pypython3 main.py
  6. 自動で定期的に監視したいなら、cronで定期的にスクリプトを実行させる。(後ほど記載)

スクリプト全文

target_urls.csv

一行ごとにURLをコピペしていく。

https://itachizame.net/

https://itachizame.net/test

https://itachizame.net/test2

main.py

import csv
from datetime import datetime
from logging import getLogger, Formatter, FileHandler, DEBUG, ERROR
import requests

## 変数
SLACK_HOOK_URL = 'https://hooks.slack.com/XXXXXXXXXXXX' # ここにはslackで取得したincming-webhookのURLを入力します。
URLS_FILE_NAME = 'target_urls.csv' # ここには監視対象とするURL一覧が記載されたcsvファイル名を入力します。
LOG_FILE_DIR = 'log/' # log/ディレクトリにlogを保存する。

## logging config
TODAY_DATE = datetime.now().strftime("%Y%m%d")
LOG_FILE_NAME = str(TODAY_DATE) + '.log'
logger = getLogger(__name__) # ロガー:__name__には実行モジュール名を
logger.setLevel(10) # ログレベルの設定
fh = FileHandler(LOG_FILE_DIR + LOG_FILE_NAME) # ログのファイル出力先を設定
logger.addHandler(fh)
# ログの出力形式の設定
formatter = Formatter('[%(levelname)s] %(asctime)s %(name)s %(filename)s:%(lineno)d %(message)s')
fh.setFormatter(formatter)


def get_urls(file):
    '''
    csvファイルからURLの一覧を取得し、リストで返す
    '''
    with open(file, encoding='utf-8') as f:
        rows = csv.reader(f)
        url_list = []
        if rows:
            for row in rows:
                url_list.append(row[0])
            return list(set(url_list))
        else:
            logger.error('URLが記載されていないか、ファイル名が間違っています。')

def get_status_code(url):
    '''
    URLのステータスコードを取得する
    '''
    try:
        req = requests.get(url, timeout=20)
        return req.status_code
    except requests.exceptions.RequestException as error:
        logger.exception('ステータスコード取得中にエラーが起きました: %r', error)

def checking_urls(url_list):
    '''
    URLリストのURLを一つずつチェック。
    '''
    try:
        logger.info('URLチェックを開始します。')

        for url in url_list:
            status = get_status_code(url)
            text = str(status) + ' ' + str(url)
            if status == 200:
                logger.info(text)
            else:
                logger.warning(text)

        logger.info('URLチェックを終了しました。')
    except:
        logger.error('何らかのエラーが発生しました。')

def notify_to_slack(text):
    '''
    slack通知を行うメソッド
    '''
    payload = {
        'text': text,
        'username': 'SimpleUrlChecker'
    }
    try:
        response = requests.post(SLACK_HOOK_URL, json=payload)
    except requests.exceptions.RequestException as error:
        logger.exception('slack送信中にエラーが起きました: %r', error)


def extract_body(filename):
    '''
    file名から本文をごっそり抜き出しテキストで返す
    '''
    with open(filename, encoding='utf-8') as r:
        body = r.read()
    return body


def main():
    '''
    URLチェック実行メソッド
    '''
    # csvファイルからURL一覧を配列で取得
    target_urls = get_urls(URLS_FILE_NAME)
    # URL一覧のステータスコードをそれぞれ調べる
    checking_urls(target_urls)
    # もしステータスコードに[warning]以上が含まれていたらslackに通知
    log_body = extract_body(LOG_FILE_DIR + LOG_FILE_NAME)
    if ('WARNING' or 'ERROR') in log_body:
        notify_to_slack(log_body)

if __name__ == '__main__':
    main()

centos7上でcronを使って定期的にスクリプトを実行させる

自分の場合は、さくらVPSのcentos7上で上記スクリプトをcronで定期的に実行させています。

centosにsshでログイン後、以下のコマンドを実行します。

crontab -e

その後、iで挿入モードにし、以下のように記述します。

0 0-23/4 * * * root cd /home/<ユーザー名>/simple_url_cheker && python3.6 main.py

色々な説明を省いていますが、まず「0-23/4」とすることで4時間おきにcronを実行するようにしています。
スクリプトの実行ユーザーはrootに指定し、まずはd /home/<ユーザー名>/simple_url_chekerとすることで、スクリプト群があるディレクトリに移動します。そして&&で繋ぎ、python3.6 main.pyでスクリプトを実行します。

また、この際centos上にもしpython3系が入っていなければ、事前にpython3系を導入しておきます。
centos7なら、以下を順に入力していきます。

sudo yum install -y https://centos7.iuscommunity.org/ius-release.rpm
sudo yum install -y python36u python36u-libs python36u-devel python36u-pip
# 確認
which python3.6
# 忘れずrequestsモジュールをインストール
python3.6 install requests

centos6なら、https://centos6.iuscommunity.orgに変えればOKなはずです。
ネット上にあるものを知らずにコピペしちゃうと、インストール時に競合エラーが起きます。

完成イメージ

生成されるログファイル

生成されるログファイル
モザイク部分にURLが表示されています。

slackに通知されるアラート

slackへのアラート通知