DevDevデブ!!

プログラミングのこととか書きます。多分。。。

ログ集計バッチとしてのaws athena

タイトルそのまんまなんですが、最近aws athenaをログ集計バッチ代わりにつかっています。

athenaって何?

小宇宙(コスモ)を燃やすやつでも、サイコソルジャーでもない。

雑に説明すると、S3に配置したログファイルについてスキーマを定義して、SQLを実行できるawsのマネージド・サービス、という感じ

裏側ではprestoが動いてるらしい。

詳しくはググって

バッチ的に使ってみてどうか

本来データアナリスト的なポジションの人が、インタラクティブにクエリを実行するようなユースケースを想定していることもあって、バッチ的に使うのは本来のユースケースとは外れると思う。

というのも、athenaのクエリは発行と結果の出力が非同期的であるため、バッチからクエリ発行して結果を受け取って〜という処理は結構めんどくさい

(awsコンソール上からクエリを実行した場合は、クエリが完了次第、画面下部に結果が表示される)

boto3だとクエリ発行は以下のような感じになる

        athena = boto3.client('athena')
        response = athena.start_query_execution(
            QueryString="クエリ文字列",
            QueryExecutionContext={'Database': 'データベース名'},
            ResultConfiguration={'OutputLocation': 's3://結果出力先'}
        )

start_query_executionはすぐに完了するのだけど、戻り値はSQLの結果ではなく、「クエリの発行が完了した」ことを示すjson(boto3の場合はpythonの辞書型)

SQLの結果は上記コードのOutputLocationに指定したs3のパス配下に"クエリ発行ID".csvというファイル名で配置される。

"クエリ発行ID"というのはstart_query_executionの戻り値に含まれるQueryExecutionIdのこと

クエリの実行が完了したかどうかは、s3にファイルが出力されたかポーリングするか、get_query_executionでクエリの実行ステータスを確認する必要がある。

なお、出力ファイル名が"クエリ発行ID".csvになるというのは、ドキュメントに記載が見当たらなかったので、仕様というわけではなく、現状の法則にすぎないことに留意する必要がある。

何故prestoじゃなくてathenaを利用したのか

コストが安い

EMRでprestoを使うのと比較して、athenaのほうが安くなるため(これはユースケースによる)

athenaの料金はクエリ実行において、S3からスキャンしたデータ量に応じて課金される。5ドル/1TB

筆者の環境ではfluentd->kinesis stream->kinesis firehose->s3という流れでログをs3に配置し、1時間ごとにathenaを起動してログ集計を実行しているのだが、大体1クエリ700MB程度なので、EMRを使うより確実に安く抑えることができる。

無論、今後のログ流量の増加によっては料金が逆転することもあるだろう。

hadoop周辺に詳しい人がいない

EMR自体はawsコンソールからぽちぽちやるだけで立ち上がるのだが、障害発生時に対応できる人がいない。(hadoop周辺についてマジで無知であるため、エラーメッセージの意味が読み取れない)

筆者が新卒のころに、ちょうど「ビッグデータ」が持て囃されたのだが、どうせ自分で触る機会はないだろうとhadoopノータッチできたツケが回ってきた感じ。。。。

毎時バッチにEMR立ち上げるのはビミョい

微妙です。立ち上げっぱなしも無駄だし、毎時起動するにもオーバーヘッドがでかい気がするし。

TIPS

スキーマ定義の方法

スキーマAWS Glue Data Catalogに定義する。(言葉の使い方があってるのかいまいち分かってない)

awsコンソールから手動でポチポチ入力して定義することもできるのだが、AWS Glueのクローラを利用するのがてっとり早い

Glueクローラにs3の指定のパス配下をクロールさせると、そこに存在するログファイルをスキャンして、自動的にスキーマを抽出してくれる。

一度クローラで自動抽出させてから、細かい部分を手動で修正するとよいだろう

(なお、先頭行がラベルになってないcsvなど、ログからスキーマを抽出できないフォーマットの場合、恐らく自動抽出できないと思われる。jsonとparquetはできた)

パーティションの設定

スキーマパーティションを設定していると、クエリ実行時に指定したパーティションだけスキャンするので、データスキャン量を抑えることができ、クエリの実行時間も短縮することができる。

実はGlueのクローラはs3のパス構造から自動的にパーティションを設定してくれる機能がある。

たとえば、s3://sample.bucket/2018/02/14/20/sample_log.gz という感じにログファイルが配置されている場合、「2018」、「02」、「14」、「20」の部分を自動的にパーティションとして認識してくれる。

新規にs3://sample.bucket/2018/02/14/21/sample_log.gz ができた後、再びクロールを実行すると、新規にできたパスについてもパーティションを追加してくれる。

しかし、Glueクローラは指定したパス配下について、毎回全スキャンをかけてしまうため、ログファイルの増加に比例してクロール所要時間が増加するという問題がある。

残念だが、現状では自分でパーティション追加のDDLを発行するのが良いと思われる。

トラブルシューティング

athenaのクエリ実行は結構コケる

SQLの文法エラーはないにも関わらず、エラーになって失敗することがある。

エラーメッセージには「HIVE_CURSOR_ERROR」という文字列が含まれているので、背後で動いているhive、prestoのエラーなのだろう。

単純にリトライすると成功する。

上記を回避したい場合

自前でEMRを運用するしかないと思われる。

憶測だけど、athenaはでかいEMRのクラスタを複数のユーザが叩く形になっているので、どうしてもリソースが逼迫する状況があって、エラーが発生するのだと考えてる。

幸いなことにGlue Data Catalogはhiveメタストアとして指定することができるので、Glue Data Catalogに定義したスキーマは、prestoやhiveからも参照することができる。

docs.aws.amazon.com

athenaで動作するクエリはprestoでも動くので、athenaからprestoに切り替えるのはそれほど難しくはないと思われる。