はじめに
こんにちは、データ分析部の森本です。
この記事ではGunosyデータ分析部がどのような視点に基づいてバッチアプリケーション(以下、バッチ)を開発・運用しているかしているのかを紹介します。
クライアントアプリ開発やAPI開発と比較してバッチ開発のノウハウなどをまとめたWeb記事の数は少なく感じます。
また、言語に関わらずWebフレームワークの数に対して、バッチフレームワークの数も少数です。
このような点を踏まえると一般的には難易度の高くない(ノウハウを必要としない、フレームワークに頼る必要のない)、もしくはニーズがあまりないなどの印象があるのかもしれません。
一方で我々は日々バッチ開発を行い、数多くの地雷を踏んできました。
これらの経験を踏まえてどのような点に気をつけているのかについて共有します。
理想的には多くの方の経験を共有して、建設的な議論に発展するとうれしいです。
全体像
Gunosyではニュースアプリを開発しています。ニュースアプリ開発には大きく5つの柱があります。
- iOS, Androidのクライアントアプリ開発
- クライアントからのリクエストに対応するAPI開発
- 提携しているメディア様からニュース記事を収集するクローラー開発
- ニュース記事をプッシュ配信するプッシュ機能開発
- スコアリングやニュース記事分類などの記事配信アルゴリズム開発
(アドテクノロジーとインフラは別の大きな枠組みであるため、ここでは除外しました)
データ分析部ではデータ分析や機械学習を活用したプロトタイプの実装だけでなく、プロダクションコードの実装、デプロイ、運用まで実施しています。
また、バッチにも複数の種類があると思います。
我々が開発しているバッチは金融機関等の日次、月次処理とは趣が異なります。
ユーザー満足度向上を支えるために機械学習などを活用し、スコアなどを計算するバッチと捉えてください。
開発について
フレームワーク
記事配信アルゴリズムは主にpythonで実装しています。
特に数値計算ライブラリnumpy, pandasや機械学習ライブラリscikit-learn、各種deep learningライブラリの力を借りたいというのがpythonを使用している主な理由です。
一方、バッチフレームワークとしてDjangoを利用しています。
DjangoといえばWebフレームワークに分類されると思いますが、なぜバッチ開発に利用しているのでしょうか。
Djangoをバッチフレームワークと使用するメリットに次のようなものが上げられます。
- 同期処理/非同期処理を柔軟に実装できる
- Commandクラスを使用した同期処理、Celeryを使用した非同期処理を用途に応じてDjango上で実装できる
- Celeryを使用することでmaster/slave形式で分散処理を実現できる
- MySQL、Redisを始めとした各種データソースへのアクセス処理が充実
- バッチ開発の基本はInput/Process/Output
- Input/Outputのデータアクセス層のコードを比較的綺麗かつ隠蔽して書くことができる
- ロギング処理の充実
- デバッグが容易
python manage.py shell
で対話的に実装できる
- ドキュメントが充実&十分枯れた技術
逆にここがいまいち
- ORMコードが書きづらい
- この点は慣れやエンジニアの技術レベルに依存するとは思いますが、Railsと比較するとなかなかシンプルに書けないと感じています(主観)
さて、バッチフレームワークにDjangoを使用していますが、他の候補としてLuigiやAirflow等が挙げられます。 皆様の中にこれらフレームワークをプロダクション環境でガッツリ使用している方がいらっしゃいましたら、ぜひ情報共有したいです。
どこに記事配信アルゴリズムを書くか
いわゆるサービス層のロジックをどこに書くかとも表現できます。
Input/Outputを担う各種データソースへのアクセスコードは各種データソースごとにappディレクトリを作成しています。
記事配信アルゴリズムコードは散らからないように1appディレクトリ内に閉じ込めるように努めています。
一応この方法でコードが散在することは防げますが、より確かな方法を模索中でもあります。
デプロイ環境
デプロイ環境はプロダクション環境、ステージング環境、サンドボックス環境の3種類あります。
新しいアルゴリズム(例えばニュース記事の分類器やクラスタリングロジックなど)をリリースするときはステージング環境で動作検証します。
一方でスコアリングアルゴリズムではアクセスログも使用します。ステージング環境では十分なアクセスログがありません。そこでサンドボックス環境を用意しました。サンドボックス環境ではプロダクション環境と同様のアクセスログを取得でき、スコアリングアルゴリズムの検証を実施できます。このようにステージング環境とサンドボックス環境の2種類の検証環境があり、用途に応じて使い分けてます。
ABテスト
新しいアルゴリズムをリリースする時、ステージング環境、サンドボックス環境で動作確認できても、いきなりプロダクション環境に100%デプロイすることはありません。ABテストを実施してKPIが向上するか(もしくは想定内の減少をするか等)を必ず確認します。
品質
品質チェックはステージング環境、サンドボックス環境で十分に行います。また最後の砦としてABテストがあります。
一方恥ずかしながら、記事配信アルゴリズム開発については明文化されたテスト計画はありません。
そのため、テストコードの実装は複雑なロジックに対して重点的に書くなどのエンジニア依存の部分もあります。
また、プルリクエストレビューは必ず実施します。比較的即座にレビューしてくれるのでメンバーには感謝です。
実装&デバッグ
組織ではなく個人の感想になってしまいますが、pdbなどのデバッガを使用するよりも python manage.py shell
で対話的に実装して同時にデバッグするほうが生産性が高いと思っています。
また、対話的にすすめることでORMコードのSQLも容易に確認でき、重い処理の把握や改善を即座にできるのがうれしいです。
プロダクション環境でしか発現しない問題の原因追求にも python manage.py shell
で実施する場合もあります。
ただしこの方法はおすすめできるものではありません。本来はログより確認すべきで、それをログから確認できない時点でログ実装が不十分といえます。
開発生産性を高めるために皆様がどのように実装しているか知恵を借りたいです。
運用について
デプロイ
Gunosyでは全プロダクトを通して、デプロイは主にAWS OpsWorks
を使用しています。ボタンポチでデプロイでき、非常にありがたいです。SREチームに頭が上がりません。
ロギング&監視
データ分析部が運用するバッチは多数あります。そのためリリース直後を除いてINFOログを定期的に監視するということはしません。問題が発生したときのみタイムリーに気づける仕組みを取っています。
- アプリケーションにERRORログを実装&必要に応じてリトライ処理
- ログ監理はpapertrailを使用
- エラーログはpapertrailを通じてwebhookでSlack通知
おわりに
我々が日々実施していることを比較的かっこよく書いてみました。一方ではpython2系のスクリプト集合体のバッチ群も現役バリバリで生き残っていたりします。
言うは易く行うは難しであり、泥臭い運用に頼っている点もあります。
ベターなノウハウが共有されて、よりよいバッチ開発ライフを実現できることを祈っています。
(バッチ開発運用Nightとかやりたくなってきた)