HerokuとPythonとSlackによるお手軽可視化システム
pandasやmatplotlib、他科学計算系のpackageを用いデータを定期的に可視化しチェックしたいケースがある。
この際特に大げさなシステムを作る必要がないし、金もねぇ、時間もねぇときがある。でもRedashじゃできねぇ...
そんな状況の中作ったHeroku、Python、Slackを用いたシステムについての備忘録。
目次
- Heroku Schedulerを設定
- pyplotでグラフを保存しSlackに通知
- 動的にmoduleをロードし、スクリプト追加を簡略化
1. Heroku Schedulerを設定
Heroku上で定期的にスクリプトを実行できるHeroku Schedulerというものがある。
適当にHerokuでappを作成した後、appにスクリプトファイルを配置する。
あとはSchedulerの設定画面でpython3 スクリプトファイル
を実行するよう設定すれば
pythonのスクリプトが定期的に実行される。便利。
この場合特にProcfileとか用意する必要はない。
Heroku CLIをインストールした後、以下のコマンドでappを作成
$ heroku create
以下URLに従いgitの設定をする
https://devcenter.heroku.com/articles/git
# heroku createした場合 $ git remove -v or # すでにherokuのappが存在する場合 $ heroku git:remote -a app名
なにかしらgitが整ったらHerokuのgitにpushし
$ git push heroku master
以下コマンドでHeroku Schedulerをappに追加
$ heroku addons:create scheduler:standard
tips
# schedulerの設定を開きたいときは $ heroku addons:open scheduler # ログを見たいとき $ heroku logs
Schedulerの設定では以下のようにpythonスクリプトを実行するように設定する。
2. pyplotでグラフを保存しSlackに通知
必要なもの:
Slack Web Apiを使うためにSlackのBotが必要になる。 python-slackclientはファイルアップロード機能を使うために利用する。
※当初SlackのIncoming Webhoookでbase64の画像をアップロードしようと思っていたが無理でした。
以下のようなスクリプトでSlackに簡単に画像が投稿できる
from matplotlib import pyplot matplotlib.use('Agg') import io import numpy import os import slack def main(argv): filename = "result.png" plot(filename) post(filename) os.remove(filename) def plot(filename): x = numpy.linspace(0, 10) y = numpy.sin(x) pyplot.plot(x, y) pyplot.savefig(filename, format="png") def post(filename): client = slack.WebClient(token=os.environ['SLACK_API_TOKEN']) response = client.files_upload( channels='#random', file=filename, title="test", filename=filename) if __name__ == '__main__': main(sys.argv)
画像の生成はtempfile.NamedTemporaryFile
を使う方が好ましく当初それで作成していた。
なぜかうまくSlackの投稿でサムネイルが表示されなくなったため、result.pngを保存するようにした。
SLACK_API_TOKEN
は環境変数を参考するようにしており(参考)、
開発時にはpython-dotenvを使うと良いと思われる。
Herokuには以下のコマンドで環境変数を設定する
$ heroku config:set SLACK_API_TOKEN=トークン
あとはHerokuになんのpackageを使っているか伝えるためrequirements.txtを配置する。
$ pip freeze > requirements.txt
これで下記画像のようなものがSlackに投稿される。
3. 動的にmoduleをロードし、スクリプト追加を簡略化
今後も追跡したいデータとそれにまつわる可視化のグラフは増えそう。 簡単にグラフを追加できるようにする。
以下のようなディレクトリ構成
- app.py - jobs |- some_job | |- __init__.py | |- analyzer.py | |- another_job |- __init__.py |- analyzer.py
app.py
は2.のものを改変し以下のような形にする
from datetime import datetime from dotenv import load_dotenv load_dotenv() import importlib import os imporo sys import slack def main(argv): jobs_dir = os.listdir('jobs') for job_dir in jobs_dir: m = importlib.import_module("jobs.%s.analyzer" % job_dir) filename = "result.png" try: if os.path.isfile(filename): os.remove(filename) m.plot(filename) post(job_dir, filename) finally: if os.path.isfile(filename): os.remove(filename) def post(job_dir, filename): client = slack.WebClient(token=os.environ['SLACK_API_TOKEN']) response = client.files_upload( channels='#random', file=filename, title="%s_%s.png" % (job_dir, datetime.now().strftime("%Y_%m_%d")), filename=filename) if __name__ == '__main__': main(sys.argv)
ディレクトリ名、メソッド名はもうちょい良い感じにしていただければ。
importlib
を用いてjobsディレクトリ配下のmoduleをimportする。そのためには空でもいいので__init__.py
が必要らしいので作成しておく(間違ってる可能性あるのでツッコミください)。
各moduleにはanalyzer.py
というファイルがあり、plot
メソッドでresult.png
という画像を保存する。
以下例:
analyzer.py
import matplotlib from matplotlib import pyplot matplotlib.use('Agg') import numpy import io def plot(filename): image = io.BytesIO() x = numpy.linspace(0, 10) y = numpy.sin(x) pyplot.plot(x, y) pyplot.savefig(filename, format="png")
このような各moduleのanalyzer.py
をapp.py
から動的に読み込み、plot
メソッドを随時呼び出して画像を作成、そしてSlackに投稿という流れになる。
新しく定期的にplotしたいものが増えた場合、jobsディレクトリにディレクトリを新規作成しanalyzer.py
を用意しplot
メソッドを定義すればよい。
まとめ
HerokuとPythonとSlackによる可視化システムについて説明しました。時間がほぼないなかさっとつくったにしては結構便利です。 前々からRedashだけだと足りないが、壮大なシステムを作るほどでもないケースが多かったのでこれからも重宝しそう。 個人的な理想としてはRedashと定期的にJupyter Notebookが動作するようなシステムがあればそれで十分なんだけどなーと思うのですがあるのでしょうか。 あったら教えてください偉い人!
余談ですが、これまでpipenvをつかっていたが、venvが便利なことに気づく。
Pythonは四半期に1回ちょっと使う程度なので間違いがあればご指摘お願いします。