LINEの社内向けデータ分析ツール「OASIS」
吉田啓二氏:LINE Data Labs、データエンジニアの吉田啓二と申します。よろしくお願いします。
今年、LINE Data Labsでは、OASISという社内向けのデータ分析用のアプリケーションを開発・リリースしました。
それによって、各社員が自分たちの手で自由に担当サービスのデータを分析できる環境を、LINE社内に作り上げることができました。私の発表では、このOASISについて詳細をお話しします。
お話は全3部構成です。
まず最初に、「なぜ我々が既存のツールを利用するのではなく、OASISを新規に開発することにしたのか?」という理由をご説明します。次に、このOASISの機能や特徴をご紹介します。最後に、「実際にLINEでどういうふうにOASISが活用されているのか?」をお話しします。
それではまず最初に、「なぜ我々がOASISを新規に開発することにしたのか?」という理由・背景をご説明します。
こちらがLINE Data Labsで管理しているデータ基盤の概要です。
先ほどのセッションでもありましたが、我々が「Data Lake」と呼んでいるHadoopクラスタがありまして、こちらへLINEの各サービスのデータが分析用途で集められています。このHadoopクラスタのなかには、いくつかHiveデータベースがありまして、各Hiveデータベースがそれぞれ1つのサービスのデータを格納しています。
これらのデータが、Data Labsで開発しているETLバッチ処理で整形・加工されたり、データサイエンティストによって分析されたり、あるいは、Data Labsが管理しているレポーティングツールを用いて、各サービスのKPIを表示するレポートを作成し、それが各サービスへ提供されたりといったかたちで、データの活用がなされています。
そして昨年末、さらに新しい取り組みが1つ始まりました。それは、先ほどのデータ基盤のデータを、広く社内へ公開するというものです。
それまでデータ基盤のデータは、それを管理しているData Labsという部署の社員しか直接アクセスできませんでした。そのため、各サービス側が自由にデータを分析できないという課題がありました。こちらの課題を解決して、サービス側で自由かつスピーディにデータ分析を行えるようにすることを目的として、この取り組みが始まりました。
求められた3つの要件
この取り組みを行うにあたって、要件が大きく3つありました。
1つ目はセキュリティ面です。このデータ基盤には、LINEのサービスのさまざまなデータが集められているのですが、各社員はすべてのデータへアクセスしてよいわけではなく、必ず自分が担当するサービスのデータにのみアクセスできるようにするといった、データアクセス制御を行う必要がありました。
こちらについて、まずはHadoopクラスタに対してケルベロス認証をかけることで、「誰がこのHadoopクラスタへアクセスしているのか」という情報を特定し、そのうえでApache Rangerという、Hadoopクラスタ用のデータアクセス制御を行うためのフレームワークを導入して、「誰が、どのデータへアクセスできるのか」を制御しました。
こうした方針で対応することで、セキュリティ面の要件はクリアしました。
2つ目の要件は安定性です。データ基盤を広く社内に公開するとなると、さまざまな社員が、分析アプリケーションをHadoopクラスタ上で同時実行するようになります。その際に、各アプリケーションの実行がお互いに影響し合わないよう、アプリケーションを分離させる制御が必要になります。
例えば、あるアプリケーションで重い処理が実施されていたとしても、それが他の実行中のアプリケーションのパフォーマンスに影響を与えないように制御を行う必要があります。
加えて、特定のアプリケーションでクラスタのリソースをほぼすべて使い切ってしまって、他のユーザーが分析アプリケーションを実行できないことがないように、各アプリケーションでどの程度のクラスタのリソースを使用できるのかという、リソース制御も併せて行う必要がありました。
これは、Apache Sparkを分析アプリケーションの実行エンジンとして採用することで、対応することにしました。
Sparkアプリケーションが、我々がすでに持っているYARNクラスタ上で、YARNアプリケーションとして実行できますので、先ほどのアプリケーションの分離やリソース制御などをすべて既存のYARNクラスタが持っている機能に任せることができます。
つまり、我々がすでに持っている技術スタックをそのまま利用することで、Hadoopクラスタの安定性を担保できるメリットがありましたので、Apache Sparkを採用することで、この安定性面の要件をクリアしました。
3つ目は機能面での要件です。LINEの社員には、さまざまなスキルセットを持った人がいるので、各スキルセットに応じて求められる機能も異なってきていました。
例えば、クエリやアプリケーションコードをまったく書けないプロダクトマネージャーのような人にとっては、自分では分析レポートを作成しないけれども、他の人が書いた分析結果を見たい。つまり、分析結果を共有するという機能が求められていました。
一方、クエリだけは自分で書けるサービス側のディレクターやプランナーからは、自分の書いたクエリの実行結果を簡単に可視化したいという要望がありました。
そして、アプリケーションコードを実装できるエンジニアにとっては、実際にアプリケーションを実装して、データの整形・加工ができるETLバッチ処理を、簡単に実装・登録・運用できる仕組みが求められていました。
最後に、データ分析を行えるデータサイエンティストにとっては、簡単にアドホックにデータを分析できる環境が求められていました。
それらの要件をまとめると、このようになります。
まずはクエリを実行できること。実行したクエリの結果については、簡単に可視化・グラフ化できること。クエリだけでなくアプリケーションコードも実行できて、作成したアプリケーションに対してスケジュールを設定することで、そのスケジュールに沿って自動的にアプリケーションを実行できること。さらに、分析結果を他のユーザーへ共有したり、共有した分析結果を誰が見られるのかといったアクセス制御ができるなど、こういった点が機能面での主な要件でした。
既存のBIダッシュボードツールを検討
私たちはこれらの要件を満たすBIダッシュボードツールを探していました。ですが、求める要件をすべて満たすツールを見つけるのはなかなか難しい状況でした。
あるツールは、クエリの実行結果を簡単に、つまりアプリケーションコードの実装なしに可視化する方法がなかったり、またあるツールは、クエリは実行できるけれどもアプリケーションコードは実行できない、もしくは、アプリケーションコードに対してスケジュール実行ができないといった状況でした。我々が求める機能すべてを持つツールを見つけることができませんでした。
一番上のApache Zeppelinだけは、唯一、我々が求めるものに一番近かったんですが、半年間ほど運用していくなかでいくつか課題が見つかり、今回の取り組みでは使うことが難しいという判断をしました。
Apache Zeppelinの課題は大きく2つありまして、1つ目がセキュリティ面です。
こちらのスクリーンショットは、作成したレポートに対してスケジュール実行を登録する機能です。このページ上で各エンドユーザーが、スケジュール実行の実行者アカウントとして別のユーザーを自由に設定できてしまう状態でした。
そのため、いくら我々が基盤側でApache Rangerを使って、「誰が、どのデータにアクセスできるのか」というアクセス制御を厳密に行っていたとしても、エンドユーザー側はApache Zeppelin上で別のユーザーになりすまして、Sparkアプリケーションを実行できてしまいます。
つまり、このApache Rangerでのデータアクセス制御が実質無効化されてしまうというセキュリティ上の問題がありました。
2つ目の課題が、安定性の面になります。Apache Zeppelinは1台のサーバー上でしか稼働させることができず、また、当時我々が使用していたときの最新版では、Apache Sparkのyarn-clusterモードをサポートしておらず、必ず1台のサーバー上で、Apache Zeppelinのプロセスと各Sparkアプリケーションのdriver programのプロセスが一緒に動いてしまうという状況になっていました。
そのため、エンドユーザーからサブミットされるSparkアプリケーションの数が増えたり、あるいは、特定のdriver programプロセスで負荷の高い処理が実施されて、サーバーリソースがほぼそれに費やされてしまう。そういった状況下では、Apache Zeppelinのプロセス自体が不安定になってしまい、結果として、エンドユーザーがApache Zeppelinをスムーズに使えないといった状況にたびたび陥りました。
このような安定性の課題と、先ほどのセキュリティ面での課題の2点から、Apache Zeppelinも、今回の我々の取り組みや規模感では使用するのが難しいという判断をしました。
OASISの機能と特徴
既存のどのBIダッシュボードツールも、我々が求める要件すべてを満たすものが存在しなかったため、今回は「OASIS」を新規に開発することにしました。
それでは次に、このOASISの機能や特徴についてご紹介します。こちらが機能概要になります。
まずは、各エンドユーザーがSparkアプリケーションをサブミットできること。Spark SQLクエリの実行結果については、画面上の操作だけで簡単に可視化・グラフ化できる機能や、複数の分析アプリケーションコードの実行結果を1つのノートブックという単位でまとめて、そのノートブックを他のユーザーに共有する機能があります。
また、作成したノートブックに対してスケジュールを設定して、そのスケジュールに沿って自動的にノートブックを実行させるスケジューリング機能や、安定性の部分で、複数台のサーバーでOASISを稼働させられるといったところが、主な機能や特徴となります。
システム構成
こちらが、OASISのシステム構成の概要になります。
OASISは、主に3つのコンポーネントで成り立っています。各エンドユーザーのリクエストを受け付けるフロントエンドAPI、Hadoop YARNクラスタに対して実際にSparkアプリケーションをサブミットするSparkインタープリター、そして、各ノートブックのスケジュール実行を管理するジョブスケジューラー。OASISは、主にこれら3つのコンポーネントで構成されています。
こちらのスクリーンショットが、実際にエンドユーザーがノートブックを作成する際に使用するページになります。
エンドユーザーは、このページ上でSparkアプリケーションコードを書き、それをサブミットして、その実行結果を画面上の操作だけでグラフ化するという手順で、ノートブックの作成作業が進められます。
Sparkアプリケーションなのですが、こちらはノートブックセッション1つにつき1つのSparkアプリケーションが使用されるようになっています。
先ほどのノートブック作成ページで、エンドユーザーが一番最初にSparkアプリケーションコードをサブミットしたときに、YARNクラスタ上でSparkアプリケーションが1つ起動し、このノートブックの作成中は継続して、このSparkアプリケーションが使用されます。そして、エンドユーザーがノートブックを保存したときに、このSparkアプリケーションが停止する。Sparkアプリケーションのライフサイクルはこのようになっています。
Sparkアプリケーションは、実際にはYARNクラスタ上でYARNアプリケーションとして実行されますが、その際のアプリケーションの実行アカウントは、OASISを操作しているエンドユーザーのユーザーアカウントが、そのままYARNアプリケーションの実行アカウントとして設定されるようになっています。
ですので、SparkアプリケーションからHDFSに対してデータアクセスがあった際は、そのOASISを操作しているユーザーのアカウントとしてデータアクセスがなされて、それに対してApache Rangerが、ユーザーがこのデータへアクセスしていいのかどうかを判断・制御する仕組みになっています。
サポートしている言語
OASISでサポートしている言語は4つあります。Scalaと、Spark SQLと、PySparkと、SparkRになります。そして、同じノートブック上であれば、これら4つの言語が1つのSparkアプリケーションを共有するという仕組みになっています。
こちらが、その際のサンプルのノートブックになります。
1つのノートブック上に3つのSparkアプリケーションコードが記述されていまして、一番左側がScalaで書かれたコードになります。まずはこちらでデータフレームを生成して、それをSparkアプリケーションのキャッシュに載せるということが行われています。
真ん中のコードがPySpark……Pythonで書かれたコードで、こちらでは先ほどキャッシュされたデータを取り出して、それをmatplotlibで可視化するということが行われています。一番右のコードがSpark SQLで書かれたコードで、こちらも同様にキャッシュからデータを取り出して、それをテーブル形式で可視化するということが行われています。
このように、1つのノートブック上で異なる4つの言語が1つのSparkアプリケーションを共有しているため、このSparkアプリケーションのキャッシュデータに対して、異なる言語やライブラリを使い分けながら、効率的にデータの処理や分析が行えるようになっています。
そうして作成されたノートブックは、OASISで「space」と呼んでいるものの中で、自動的に共有されるようになっています。
このspaceというは、LINEの各サービス1つ1つに割り当てられるノートブックのルートディレクトリのことです。ノートブックは必ずどれか1つのspace内で作成されて、それが自動的にspace内で共有される仕組みになっています。
ユーザーのアクセス権について
ユーザーのアクセス権としては、読み書きできる権限と読み取り専用の権限の2つがあります。ですので、あるspace内で、まずは読み書きできるユーザーがノートブックを作成すると、それが自動的にspace内で共有されて、読み取り専用ユーザーもアクセスできる。そんな仕組みになっています。
もともとApache Zeppelinを運用していたときに課題に感じていたのは、エンドユーザーが各ノートブックに対して、「このノートブックは誰が見られるのか」というアクセス設定をしなければならず、エンドユーザーの数やノートブックの数が増えるにつれて、エンドユーザーのオペレーションコストが高くなってしまうということでした。
ですので、今回OASISを開発する際にはノートブック単位でのアクセス制御をやめて、space内でノートブックが自動的に共有される仕組みを作ることで、エンドユーザーのオペレーションコストをなくす工夫をしています。
作成したノートブックに対して、エンドユーザーがパラメータ入力欄を設置することができます。
このパラメータの値が、ノートブックの各コードの実行時に自動的に埋め込まれるようになっています。
そのため、たとえクエリやアプリケーションコードをまったく書けないリードオンリーのユーザーであっても、単に他の人から共有されたノートブックを眺めるだけではなく、このパラメータの値を変えてノートブックの内容を再描画することによって、ある程度自由にデータの探索が行えるようになっています。
また、作成したノートブックに対してスケジュールを設定することで、そのスケジュールに沿って自動的にノートブックを実行させる、スケジューリングの機能も提供しています。
想定している用途は2つあります。1つ目が、作成済みのノートブックの表示コンテンツを自動的に最新のものに更新するという使い方です。
ですので、エンドユーザーはノートブックのページを開くだけで、とくに追加でクエリを実行する必要はなく、すぐに最新のデータを見られるという用途を想定しています。
2つ目が、エンドユーザー自身の手で自由にETLバッチ処理を登録できるようにするため、スケジューリングの機能を提供しています。
運用段階で発生した問題
先ほどのスケジューリング機能を提供することによって、エンドユーザー自身が自由にETLバッチ処理を登録できるようになったのですが、いざ運用が始まってみますと、1つ問題が発生しました。
それがこの「スモールファイル問題」と呼んでいる問題です。これは、HDFS上に大量の小さいサイズのファイルができてしまう問題です。
これが発生すると、各ファイルの情報をメモリ上で管理しているNameNodeの負荷が上がったり、ブロック数が増えることによって、そのテーブルに対する検索処理の性能が悪化してしまうという問題が発生していました。
仕組みとしては、Spark SQLの設定で、各データセットについて「内部的にいくつパーティションを持つのか」という設定値が、デフォルトで200になっていたため、単純にSpark SQLでINSERT処理を実行すると、どんなに書き込みデータサイズが小さくても必ず200ファイルできてしまうという仕組みになっていました。
この設定値を、例えば1など、一律で小さい値にすればいいかというとそういうわけではありません。そうしてしまうと、今度は、大量のデータを書き込む際に、書き込み処理の並列数が足りずに書き込み処理性能が悪化してしまうという、別の問題が発生してしまいます。ですので、クエリの特性や書き込みデータサイズに応じて、この設定値を調整する必要がありました。
この問題に対しては、OASIS側からはデータ登録処理用のAPIを提供して、エンドユーザーはこのAPIを使ってデータ登録処理を行うだけで、このスモールファイル問題を発生させずにデータ登録処理が行える仕組みを提供することで解決しました。
OASIS側からは、スライドに書いているinsertOverwriteというAPIを提供し、エンドユーザーはデータを書き込みたいテーブル名と書き込みたいデータを抽出するためのクエリの2点を指定して、このAPIを呼び出すだけで、最終的に作成されるデータファイルの数が自動的に調整された状態で、データの登録処理が行えるようになります。
このAPIの内部的な処理としては、まずは一時テーブルを作成して、そこにユーザーから指定されたクエリの実行結果を登録します。
そうすると、最終的に書き込まれたデータのトータルサイズがわかります。そのサイズとHDFSのブロックサイズ……我々のHadoopクラスタの場合は128MBなのですが、これら2つのサイズを比較して、最適なデータファイル数を算出します。
そして、その最適なファイル数になるように、一時テーブルに対してSpark SQLのrepartitionのAPIを使いながら、再度データの登録処理を行います。そうすると、一時テーブル上に最適なファイル数になった状態で、データファイルができあがります。
そして、書き込み先テーブルのファイルパーティションを一度ドロップして、データファイルを一時テーブルから書き込み先テーブルのほうへ移動させて、再度書き込み先テーブルのファイルパーティションを作成し直します。
最後は、一時テーブルをドロップします。
このような手順で、最終的に作成されるデータファイルの数が自動的に調整された状態で、データの登録処理が行えるようになっています。
エンドユーザーのほうは、こういった内部の仕組みをまったく理解する必要なく、単純にOASISから提供されたAPIを使うだけで、このスモールファイル問題の発生を回避しつつ、データの登録処理やETLバッチ処理の登録が行えるようになっています。
スケーラブルに対応できるように
OASISの3つのコンポーネント……フロントAPI、Sparkインタープリター、ジョブスケジューラーは、それぞれ複数台のサーバーで稼働できるようになっています。
その理由としては、エンドユーザーの数やスケジュール実行されるノートブックの数が増えたとしても、サーバーを増やすだけでそれに対応できるようにするというのが1つ。
もう1つは、ジョブスケジューラーに関して、たとえサーバーメンテナンス等で1台のサーバーが使えなくなったとしても、残りのサーバーを使って継続してノートブックのスケジュール実行が行えるようにする。そんなことを目的として、どのコンポーネントも複数台のサーバーで稼働できるようになっています。
ただし、Sparkインタープリターを複数台のサーバーで稼働させるにあたって、1つ工夫が必要になります。
先ほどの説明でもありましたが、同じノートブックからサブミットされるコードは、必ず同じSparkアプリケーションを使わなければいけません。つまり、同じノートブックから送信されるコードは、必ず同じSparkインタープリターサーバーへ送信されなければならないという制約があります。
これに対応するために、Redisを使って、現在どのノートブックがどのSparkインタープリターサーバーを使っているのか、あるいは、どのSparkインタープリターサーバーが現在空いているのかという情報を、Redisに持たせています。
エンドユーザーからコードがサブミットされた場合は、フロントエンドAPIからは一度Redisに問い合わせをして、このノートブックはどのSparkインタープリターサーバーへコードを送信すべきかを特定して、しかるべきSparkインタープリターサーバーへコードを配信する。こういった仕組みを作ることによって、複数台のサーバーでSparkインタープリターを稼働させることができるようになっています。
ジョブスケジューラーの複数台サーバー稼働については、すでにJavaのQuartzというスケジューリングのライブラリで、複数台のサーバーを使って全体として1つのジョブスケジューラーを実現させる機能がありましたので、こちらは単純にQuartzのライブラリを使って、複数台のサーバーでジョブスケジューラーを稼働させることができるようになっています。
こちらが、我々が運用しているHadoopクラスタのスペックの概要です。
500台のDataNode・NodeManagerで稼動しており、データの使用サイズとしては約25ペタバイトほどです。Hiveデータベースの数が150個ほどで、テーブル数が1,500個ほど。このような規模感で、Hadoopクラスタを運用しています。
OASISのユースケース
それでは最後に、「実際にLINEでどのようにOASISが使われているのか?」ということをご紹介します。
こちらが、LINEでのOASISの使用状況の概要になります。アクティブユーザー数としては500名ほどです。このうちデイリーアクティブユーザーの数が150名ほどで、マンスリーアクティブユーザーの数が450名ほどとなっています。
spaceの数が40個ほどあり、OASISを使っているLINEのサービスや部署の数が40個ほどあるという状況になっています。作成されたノートブックの数は1,600個ほどで、このうち約300のノートブックが、スケジュールが登録されて自動的に実行されるノートブックになっています。
LINEでのOASISのユースケースとしては、概ねこれら5つに分類されます。各ユースケースについて、サンプルのノートブックを交えながらご紹介します。
1つ目がレポートです。
LINEの各サービスのKPIや、短期間のキャンペーン施策、A/Bテストの実施結果といったものをレポートにまとめてチーム内で共有するという使い方になります。
ノートブックのスケジュール実行機能を使うことによって、このレポート内の各表示コンテンツを自動的に最新の状態に保つことができるため、エンドユーザー側はこのレポートのページを開くだけで、とくに追加でクエリやアプリケーションコードを実行する必要なく、すぐに最新のグラフやKPIが見られるようになっています。
2つ目のユースケースが、「インタラクティブダッシュボード」です。
ノートブックを作成して、そこにパラメータ入力欄を設置したものをダッシュボードとして使います。パラメータ入力欄があることによって、たとえクエリやアプリケーションコードを書けないユーザーであっても、このダッシュボードを見るだけではなく、このパラメータの値……例えば表示対象期間や、その他の検索条件などを変えて、ダッシュボードの内容を再描画することで、自分の見たい検索条件でダッシュボードの内容を見ることができます。
3つ目のユースケースが、ETLバッチ処理です。
ノートブック上に、ScalaやPySparkでデータの整形・加工を行うようなコードを書き、それをスケジュール設定するだけで、誰でも簡単にETLバッチ処理を登録できるようになっています。
ですので、まずはOASIS上でレポートを作成していて、どうしてもいまのテーブル構成だとクエリのパフォーマンスが出ないという場合は、そのレポートで必要となるデータマートも、OASISを使って自らETLバッチ処理を書いて作る、このような流れでETLバッチ処理が登録されます。
データのモニタリングとアドホックなデータ分析
4つ目のユースケースが、データのモニタリングです。
こちらのサンプルのノートブックでは、あるサービスのログデータの件数をhourlyでグラフ化したものが表示されています。これもスケジュール実行機能を使うことで、hourlyで自動的にノートブックを実行させて、このグラフの内容が自動的に最新になるようにしています。
また、単にデータを可視化するだけではありません。例えば直近のログデータの値が、1日前や1週間前の同一時刻と比べて著しく減っているなど、なにかしら直近のデータに欠損がありそうだと判断した場合に、その旨をユーザーへチャット上で通知するという処理も実装されています。
ですので、こういったログデータのクオリティのモニタリングや、ログ欠損の検知・アラーティングなどで、OASISが使われています。
最後、5つ目のユースケースはデータサイエンティストによるアドホックなデータ分析です。
OASISでPySparkやSparkRをサポートしているので、これらの言語やライブラリを使って、ある程度自由にデータの分析が行えるようになっています。
もしくは、そのデータ分析のプロダクション環境に自分が実装した分析コードをデプロイする前に、一度OASISの画面上を使って簡単に分析コードをテスト実行するといったように、分析コードのサンドボックスのような役割としても、OASISが使われています。
OASISがもたらしたもの
ユースケースのご紹介は以上になります。発表の最後に、今回の私の話のまとめになります。LINE Data Labsでは、今年OASISという社内用のデータ分析用のアプリケーションを開発・リリースしました。
それによって、いままでLINEでは行えていなかった、各社員が自分の手で自由に担当サービスのデータを分析するといったことが可能になりました。
実際のOASISのユースケースとしても、レポーティングやETLバッチ処理、データのモニタリングなど、データ分析に関する多岐にわたるタスクをすべてOASIS上で完結して行えるようになっています。
ですので、今年、OASISをLINE社内に提供することによって、LINEの各サービスの成長をデータ分析業務の効率化という側面から支えることができるようになりました。以上です。ありがとうございました。
(会場拍手)