持続可能なデータ基盤を作るにやっていること

善明晃由氏(以下、善明):サイバーエージェント秋葉原ラボの善明です。よろしくお願いします。今日はこういうタイトルで、事例ベースの話をさせていただければなと思います。

簡単に自己紹介をさせていただくと、私は2011年にサイバーエージェントに入社しました。前職はメーカーの研究所で分散コンピューティングの研究をしていたんですが、10年近くデータ解析基盤であるとか、その関連システムの開発・運用を担当しています。

秋葉原ラボについて簡単に紹介させていただくと、大規模データ処理やデータ分析、機械学習などを専門とするエンジニアが在籍する研究開発組織です。

AbemaTVやAmebaブログ、AWAといった、サイバーエージェントのメディア事業において日々生成されるユーザーのアクセスログや行動ログなどを大規模に集積、処理する基盤を整備し、その基盤上のデータを機械学習や自然言語処理技術、データマイニングの技術を使ってサービスの改善に貢献することをミッションにやっています。

本日の発表の概要なんですが、データエンジニアリングの領域って、自分の感想としては、技術的負債がすごい生まれやすいのかなと思っています。専門性が高くて採用が難しいということと、先ほど猿田さんのお話でもありましたが、各社技術の進歩がすごく早くて、新しいアップデートをキャッチアップするのが難しいこと。

それに対してデータの規模が大きいので、進歩は早いけどアップデートが大変で、陳腐化しやすいので、技術的負債になりやすい。

そこで、我々はこういうことを意識して、負債がなるべく生まれないためにどんなことをしているかを紹介させていただければと思います。

本日は、まずは問題意識を詳しく紹介させていただいて、事例の話をしたいと思います。

縦割り組織の弊害

我々の会社はいろいろなサービス、例えばAmebaブログやAbemaTVなど、けっこう縦割りの組織で動いていて、わりと独立にいろんな案件が降ってきます。似た案件も多いんですが、独立で進むと似ていることに気付きづらいので、システムのサイロ化が進んでしまいます。

結果、サイロ化してしまうとそれぞれに対して十分なエンジニアって割り当てられないので、それぞれの担当の人がフルスタックで、開発から運用まで、アプリケーションからインフラに近いところまで、全部見るようになってしまいます。

「フルスタック化というと、なんか良いじゃん」って思うかもしれないですけど、あんまり良くなくて。

フルスタック化してしまうということは忙しくなるということでもあるので、専門性を深めるという意味ではどうしてもやりにくくなってきます。そうなると、アップデートをするための十分な調査や検証の時間が取れなくなるので、陳腐化につながります。

加えて、採用も進まないので、属人化も加速していきます。先ほどいろいろ挙げましたが、「それを全部できる人ってそんなにいないですよね」という話です。加えて、大規模データ処理や機械学習まで含めると、そこまで含めてできる人ってなると本当にいません。

そんななかで退職してシステムを見れる人がいなくなってしまうと、本当再起動ぐらいしかできないみたいな感じで、負債がたまっていってしまうという流れになります。

こうならないようにするために、我々は各システムのプロダクト化をしていきたいと考えています。それは、「何が同じで、何が違うか?」みたいなところをきっちり分析していったうえで、ドメインモデリングなどを活用して、共通化部分のプロダクト化するを促進したいと思っています。

そのうえで役割の分離ですね。専門性を高めて個々の機能を強化していく人と、システムとして統合して機械学習などの成果が活用できるように、役割の分離ができないかということを、現在試みています。

WebAPIの整理と統合

事例の紹介として、WebAPIの整理・統合について紹介したいと思います。

これは比較的昔からあるシステムで、何回かこういう場でも紹介させていただいてるので、知っているかたもいるかも知れません。我々はログ解析基盤でPatriotを開発・運用していて、オープンソースベースにいろいろやってます。AmebaブログであるとかAbemaTVからデータを蓄えて、それをオープンソースの技術を使って活用しています。

そのデータを活用するためのインターフェースとして、LINEさんでいうところのOASISみたいなものかと思いますが、WebAPIを作っています。

今回はここを統合した話ですね。

WebAPIの概要です。大雑把に言うと、Hiveでログを集計して、それをHBaseに書き込んで、そのHBaseにある集計されたデータを可視化する、という機能を備えてます。あまりメジャーではありませんが、HBaseを使う時にはスキーマ設計が非常に重要になってきます。

スキーマ設計とは何かというと、HBaseというのはKeyValueストアで、KeyとColumn FamilyとQualifierとValueという4つのフィールドしかないので、アプリケーションのレコードのそれぞれの属性を、HBaseのどのフィールドに配置するかが重要になってきます。なぜ重要かというと、キーの最初のほうに入っている属性を絞り込まないと、データの取得が早くできません。

例えば、我々のほうで実際に使っているスキーマの例なんですが、一番最初にサービス名があって(service)、次にユーザーの行動のタイプみたいなものが入ってきて(action)、指標名、PVであるとかユニークユーザー数が入っていて(indicator)、あとはどの日の集計かが入っています(datetime)。

あと、その他の項目があって、集計結果が入ります、という感じなんですけど、これはキーの頭にserviceがあるので、データを取る時にRowKeyに入ってるものを指定しないと、データベースをフルスキャンすることになって、ぜんぜん効率的に取れないかたちになっています。

ここの配置で決まるので、アクセスパターンによってスキーマ設計をする必要があり、それが非常に重要になります。用途によってスキーマ設計しなければいけないので、データの表現のバリエーションがでてしまうという問題があります。

WebAPIのサイロ化

具体的にどうしたか、という話です。最初にあったのは「行動ログのレポートを出してほしい」という話で、これはサービス名や指標名が、RowKeyの頭にくる、先ほどのスキーマですね。

この後、「アクセス解析がしたい」という話があって、これはページごとのPVやレスポンスタイムです。

こうなると、RowKeyにはドメインやURLのパスが入ってくるので、先ほどと同じスキーマでは対応できません。

この次に来たのが、広告の効果測定です。

これはインプレッションやコンバージョン数なので、RowKeyには広告枠や「どういう広告を出したか?」みたいな識別子が入ってくることになります。

これは、集計した結果をHBaseに入れて、HBaseからデータを取って表示するという、基本的に同じことをやってるのですが、ここのスキーマが違うだけでサイロ化してしまうという問題があります。

「これ、なんとかならないか?」と考えました。

HBaseクライアントの分析

HBaseのほうのバラバラのスキーマがどう実装されているかというと、ByteBufferみたいなクラスを呼び出して、そのbyte[]の連結や分割をやるという、だいたいの流れは同じなんですけど、属性をどういう順番で入れるかや、属性の区切りをどう判断するか、区切り文字でやるのか、長さを前に置くのかみたいな、そういうバリエーションが違います。

ここのパターン化をどう組み合わせるかがうまく管理できれば、先ほどのようなサイロ化を統合できるのではないかと考えて、設計モデルに落とし込みました。

先ほど「HBaseに属性が入る」という話をしましたが、フィールドごとに属性がどういう順番で入るのかというレイアウトを、フォーマッタのリストとして管理してあげて、そのフォーマッタ自体を1つのDecoratorパターンを適用して、どのSuffixを付けるかであったりどういうPrefixを付けるをカスタマイズできるように設計したうえで、それぞれのクラスにbyte[]の直列化に関するロールを割り当てることで、うまく統合するツールができるのではないかと考えました。

実際には、標準的なインターフェースでHBaseのKeyValueとアプリケーションのレコードが変換できるようなものを作りました。

標準的なインターフェースは、例えば、HiveのStorage Handlerですね。HBaseのテーブルをHiveと同じように扱えるようにしたり、他にはJDBCドライバで、これを使うと関係データベースと同じようにHBaseが扱えます。

こういったものを標準インターフェースで提供してあげると、集計バッチを作る側やWebアプリを作る人は、裏がHBaseだということを意識しなくてよくなるので、関心事も分離できますし、実装としても統合できるというかたちになります。

こうなった後は、HBaseの専門家にあたる人は、このあたりを統合して実装したツールの対応スキーマを拡張していったり、問い合わせ処理を最適化していくといった、専門性の高いところに注力できるかたちになります。

これでやったんですが「結局ここがサイロ化されたまんまじゃん」という話になりますよね。ここについても、どういう違いがあるかを分析してみました。

データをどう可視化しているか

ここは何が違うかというと、データの表示形式が違いますよね。これ、「時系列のデータをどう可視化しているか?」みたいなイメージなんですけど。

例えば、「時系列の変化を折れ線グラフで見たい」みたいな時は横に時間がきて、縦にその値がくる。それに対してに、「2つの日で時間ごとにどういう変化があったかが見たい」となると、横軸に時間がきて、それぞれの日付ごとに系列ができるといったかたちになります。

このあたりの違いがうまく吸収できれば、Webアプリケーションのサイロ化していたところも統合できるはずです。

ここでちょっと考えたのが、結局、描画してるんですけど、裏元になってるデータというのは表ですよね。実際これはJavaScriptのNVD3といったライブラリで可視化してるんですが、その裏にあるのは同じ表形式のデータですよね。ただ、表形式の並びが違うだけなので、その表にどう成形するかというところだけ体系立てれば、その描画ロジック自体は再利用可能になると考えました。

表と値のモデリングをしてみて、実際表がどういうものから構成されてるかというと、2種類の軸があって、値の絞り込みに使う軸ですね、日付ごとのレポートを出す時は、「1ヶ月分のデータがほしい」みたいな時に、最初の日と最後の日を指定して、「そういうデータを取ってきてください」というかたちになると、そういうものに使う軸があると思います。

それとは別に、ただ値が入ってくるだけの列があります。

それぞれの値がどうなってるかというと、よくよく考えてみると、1つ以上のタグで識別されるものですね。この「9500」という値だと、日付が「3月6日」で、サービスが「ゲームのサービス」で、アクションが「ログイン」というタグが付いてるデータとみなせます。

こうした絞り込みに使う軸を「キー軸」を呼んでるんですが、「キー軸に使うところからどういったタグが付いた値を取ってくればいいか?」というアルゴリズムが整理できれば、Webアプリケーションのサイロ化もなんとかなると考えました。

これ、うまく説明できないので、あきらめんたんですけど(笑)。

(会場笑)

Grafana、タイムラインの時系列のデータをモニタリングする、可視化する機能のツールがあります。

これは時系列ベースで「どのサーバーのどのメトリクスを出しますか?」を下で選択してあげると、グラフがいろいろできるがツールがあるんですが、ここが時系列じゃなくてもいいような一般化したツールを作りました。

作った結果どうなったかというと、Webアプリケーションのところもうまくまとめられました。我々はデータ分析基盤がメインで、フロントができる人はすごく貴重なんですが、そういう人が1個のWebアプリの上のUIの改善だけをやればいいようになるので、非常に効率的になります。

表に出すというところをまとめる時に、「裏のデータソースとしてどんな表現形式だったらいいか?」みたいなところも整理できてくるので、そこがうまくいくとHBase以外、他のシステムのデータを可視化する部分の連携もしやすくなりました。

あとは先ほども言ったように、データストアの担当は、相変わらずフロントの改修はなにも意識しなくていいようになります。その結果、現在こういう構成で動いています。

実はここまでの話は、2年前3年前ぐらいにやっていた話です。国内の学会でも発表していて、今日は技術的な細かいところは省いてしまいました。論文はタダで読めると思うので、もしご興味のある方がいたらこちらも見ていただければと思います。

非構造化データ参照のAPIのプロダクト化

いまどういうことをやってるかという話なんですけど、いまやってきたことをもうちょっと広く適用できないかと考えています。

HBaseは我々のところではいろいろ使っていて、例えば、いまは社内のアクセス解析システムの話でしたが、エンドユーザー向けのアクセス解析システム、例えばオウンドメディアみたいなサービスがを使っていただいているユーザー向けのアクセス解析システムであるとか。あとは、機械学習のオフラインシステム、……オフライン推論をした結果を返すみたいなところでもHBaseが使われているので。

もちろんそれに対しても、データを取るためのWebインターフェースが必要なので、そこでもこれまで培ってきたものをベースにうまく整理・統合できないかと考えています。

具体的な方法としては、いままでやってきたことをDockerコンテナ化して、設定ファイルさえ書けばAPIが上がりますよ、というDockerコンテナとしてプロダクト化しようとしています。

Dockerファイルとして、例えばこのように「スキーマやデータソース、どこ使いますか?」みたいな設定を書いたものだけ特定のディレクトリに置いてあげて、コンテナを作って、このコンテナを上げたら、そこからデータを取るAPIが上がる、みたいなものを作ろうとしています。

もちろんデータ取るだけでなく、ロジックを埋め込んだりする必要がありますが、そういった場合は、別にjar作ってあげて、特定のところに置いてあげれば、ロジックをカスタマイズできるようなことも考えています。

今回は時間がないので紹介していませんが、他にも「似たようなことをやってるけど、いろんなデータがあるよね」という話はいろいろなところであります。

例えばアノテーション管理ツールですね。機械学習で正解データのアノテーションを人手で作る必要があるケースがあるかと思いますが、そういうところについても、「アノテーションとは何か?」みたいなことからしっかり考えて作ってもらっています。

あとはデータの転送の管理であるとか、機械学習のモデルの管理なども共通で整理・統合していかなければいけないので、こういうところに上流工程やドメインエンジニアリングをしっかりやって、持続可能なものを作っていくことを心がけています。

まとめとしましては、多様性をうまく管理して、技術的負債を生まないための取り組みを紹介しました。とくにHBase関連システムの統合をした事例を紹介させていただきました。以上で発表を終わります。

(会場拍手)