その他
    ホーム 技術発信 DoRuby 【cron × Gmail】バッチ実行エラーを手軽に監視しよう
    【cron × Gmail】バッチ実行エラーを手軽に監視しよう
     

    【cron × Gmail】バッチ実行エラーを手軽に監視しよう

    この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

    今回のお話

    こんにちは。いくたです。
    宝塚歌劇公式サイトの更新を5分ごとに巡回して通知するボットを作りました。

    このようにほぼ常時動いてるようなプログラムを組む時には、 何かしらエラーが発生した時に気づけるような仕組み が必要です。
    今回はcronのメール送信機能を使って手軽にエラーの監視をしていきます。

    cron以外にもスケジューラは色々とありますが、サーバーをわざわざ立てたりログローテーションを設定したりと面倒なことが多そうです。
    そこでcronで使える お手軽 な運用・監視方法を考えました。

    (ボットの紹介は今回の趣旨とは違いますが、よかったらフォローしてください。)
    https://twitter.com/PolarChildren

    このボットの大まかな仕組み

    1. サイトの更新履歴ページの一番上(最新)をNokogiriでスクレイピングして取得(スクレイピングについて詳しく知りたい方はこちらの記事もご覧ください)
    2. 前回の結果(テキストファイルに保存してある)と比べる
    3. 違いがあれば各所(twitter/slack/line)に通知する
    4. 新しい結果をテキストファイルで保存

    この一連のプログラムを5分に1回動かしているのが cron です。

    cronとは?

    cronとはUnix系のOSで使われるシステムで、様々なコマンドを定時実行してくれます。
    詳しい設定方法などはこちらの記事を参考にしました。

    今回作ったボットのcron設定

    $ crontab -l
    # 実行時の出力内容をメールで送ってくれる(ボット専用のメールアドレスを取得)
    MAILTO="test.mail.for.bot@mail.com"
    
    # cronの書式
    # 分 時 日 月 曜日 実行したいコマンド
    # サイト更新監視バッチ(毎時間の0分から55分まで5分毎に実行)
    0-55/5 * * * * /bin/bash -lc 'ruby ~/scripts/takarazuka/update_catcher.rb'
    

    /bin/bash -lc の -l はログインシェルと同じ環境で実行したい時使うオプションで、 -c は引数にとった文字列( ruby ~/scripts 〜 以下)をコマンドとして実行するオプションです。

    MAILTO を使うことで実行時の出力をメールに転送することができます。

    # こんな感じのメールが来る
    =====
    From: サーバー
    To: test.mail.for.bot@mail.com
    Subject: Cron <サーバー> /bin/bash -lc 'ruby ~/scripts/takarazuka/update_catcher.rb'
    -----
    [debug] -- START --
    [debug] loaded setting file.
    [debug] execute: takarazuka
    [debug] result is OK.
    [debug] {:title=>"花組公演『ポーの一族』ムービーページ 更新!", :link=>"https://kageki.hankyu.co.jp/revue/2018/ponoichizoku/movie.html"}
    [debug] This site is updated.
    [debug] tweeted
    [debug][message]
    宝塚歌劇公式ホームページが更新されました。
    【花組公演『ポーの一族』ムービーページ 更新!】
    https://kageki.hankyu.co.jp/revue/2018/ponoichizoku/movie.html
    [debug] -- END --
    =====
    

    メールを使ってエラー通知

    ボット専用のメールは普段見ないのでエラーがあっても気づけません。
    そこでエラーのログが出力されたメールを、普段使っているメールアドレスに転送するようにしました。

    例外エラーをキャッチする

    まず、プログラム内でエラーをキャッチして [error] と出力させます。
    後々メールで出力結果を送った時に固定文字’error’でエラーメールを振り分けます。

    puts '[debug] -- START --'
    begin
      # サイトの更新を監視して通知する UpdateCatcher のインスタンスを生成・実行
      UpdateCatcher.new.execute
    rescue StandardError => error
      # 例外エラーをキャッチして出力する
      puts "[error] #{error}"
    end
    puts '[debug] -- END --'
    

    メールのフィルタ・転送を設定する

    Gmailにはメールを条件で分けて処理するフィルタ機能があります。
    Gmailのフィルタルールの作成方法

    このフィルタ機能を使って、本文中に’error’が含まれるメールだけを自分のメールアドレスに転送します。
    これで実行時に何かエラーが発生しても、すぐに気づいて対処できます。

    # こんな感じのメールが来る
    =====
    From: サーバー
    To: test.mail.for.bot@mail.com
    Subject: Cron <サーバー> /bin/bash -lc 'ruby ~/scripts/takarazuka/update_catcher.rb'
    -----
    [debug] -- START --
    [debug] loaded setting file.
    [debug] execute: takarazuka
    [error] Failed to open TCP connection to kageki.hankyu.co.jp:443 (getaddrinfo: Name or service not known)
    [debug] -- END --
    =====
    

    メリット

    メリット① 容量を気にしなくていい

    RubyのLoggerを使えば割と簡単にログを出すこともできます。
    しかし、サーバーにログを残すということは、多少なりとも容量を気にして管理することが必要です。

    そこでログを出さずに全てメールで送ることでサーバーの容量を気にせず使えます。
    (代わりにメールサーバーのディスク容量を圧迫していくので、自分のメールアドレスに全ログ送るのはやめましょう)

    メリット② 必要な情報が検索しやすい

    過去の実行状況を確認したいときも、送信日時(実行日時)やキーワードでメールを検索すればまとめて確認することができます。
    個人的にはテキストファイルのログよりブラウザで確認できるほうが分かりやすくて気に入っています。

    メリット③ Gmailのフィルター機能で応用が利く

    Gmailのフィルター機能は細かくカスタマイズが可能です。
    先ほどは転送機能を紹介しましたが、既読にしたりフラグを立てたりすることもできます。

    Gmailのフィルタルールの作成方法

    私の場合はエラーメール以外は既読にして、エラーメールはフラグをつけて後から探しやすくしています。

    スクリーンショット 2017-11-29 13.34.41.png
    エラーメールのフィルタ設定

    デメリット

    デメリット① 実行自体出来なかった場合気づけない

    この仕組みはRubyが実行できる前提なので、なんらかの理由で実行ができなかった場合(ファイルが壊れてるとか)エラーを正しくキャッチできない場合があります。

    # こんな感じのメールが来る
    =====
    From: サーバー
    To: test.mail.for.bot@mail.com
    Subject: Cron <サーバー> /bin/bash -lc 'ruby ~/scripts/takarazuka/update_catcher.rb.hoge'
    -----
    ruby: No such file or directory -- /home/ikuta/scripts/takarazuka/update_catcher.rb.hoge (LoadError)
    =====
    

    これだと ‘error’の文字列に引っかからないので自分のメールアドレスには転送されません。
    本来なら、cronで標準エラーを受け取ってメールする設定をする必要があります。
    その場合、Rubyで例外エラーをキャッチすると異常終了ではなく正常終了するので、スクリプトの方も工夫が必要です。

    参考:
    cronの設定について
    Unix :: cron / 標準エラー(STDERR)のみアラートメールする

    デメリット② 頻繁にメールを送るとスパム認定されるかもしれない

    今の所問題なくメールは送られていますが、昼も夜もなく5分に1回メールを送っているのでGmailに目をつけられてスパム認定されてしまう可能性もあります。

    デメリット③ 汎用性がない

    ログとしてファイルに出力すれば、そのデータからいろんなことを分析することができます。
    今回のボットでいえば、「サイトの更新がどれだけ頻繁に起きているか」とか「何曜日が一番更新されやすい」とかです。
    しかし、今回はメールで全て送ってしまって手元に何も残らないので、出力を眺めることしかできません。

    まとめ

    今回紹介した方法は業務レベルでは使えませんが、趣味でやっている分にはこれで充分かと思います。

    それでは、今日はこの辺で。
    読んでいただき、ありがとうございました。