LinkedIn製のOSSデータカタログ「DataHub」の概要とチュートリアル
どうも!DA部の春田です。
先日までre:Invent2020のAnalytics系のセッションレポートを書いていたのですが、海外企業のほとんどがデータポータルなるものを構築・稼働させていた点が印象的でした。このデータポータルを構成する要素の中でも、最近データカタログというサービスが注目を集めており、まだ成熟しきっていない分野ですが、探してみると新興OSSが結構見つかるんですよね。
さて、その中でも今回はLinkedIn製のOSSデータカタログ、DataHubについてご紹介していきたいと思います。
DataHubとは?
DataHubは一言で言うと、データソースのメタデータの検索とディスカバリーを実現するツールです。LinkedIn社の長年のメタデータ管理の経験の末、設計思想として以下の5点が掲げられています。
DataHub: A generalized metadata search & discovery tool | LinkedIn Engineering
1. Push is better than pull
- それぞれのデータ・プロバイダーが、各自でメタデータを中央のリポジトリへPushする方式を採用
- データソースを直接クローラーで解析する方式(=Pull)は、開発・メンテナンスコストが高い
- Pushベースはスケーラブルで、リアルタイムにメタデータを更新・反映できる
2. General is better than specific
- 連携するデータベースやシステムのそれぞれの仕様に合わせず、普遍的なメタデータ・モデルを追求する
- 普遍的なメタデータ・モデルを作成しておけば、それの拡張だけに集中することができる
3. Online is as important as offline
- データにはオフラインで済むデータと、オンラインである必要があるデータの2種類
- オフラインデータはHadoopのようなシステムに格納できる
- 対して、アクセス権限やプライバシーといったデータは、最新のメタデータである必要があり、オンラインが求められる
4. Relationships really matter
- メタデータは、リネージや所有者、依存関係といった、重要なリレーションを表現する
- これらのデータは、影響分析やデータ要約、検索機能などに活用できる
- リレーションのモデル化を最優先し、分析の効率化を支援することが重要
5. Multi-center universe
- メタデータを一つのデータセットにまとめるだけでは不十分
- データ、ソースコード、人材といったエコシステム全体を統合して、一つのメタデータグラフとして表現する必要がある
おおよそ、データ分析に携わる人にとっては「確かに」という感想を抱くのではないでしょうか?
DataHubは複数のコンテナで構成されており、KafkaやNeo4j、ElasticSearchといったオープンソースのツールが上手く組み込まれています。
LinkedIn社内で稼働しているDataHubから、汎用的なコア機能をスピンアウトしたOSSであるため、特定の機能についてはユーザーやデベロッパーのの要望に合わせて拡張していくとのことです。詳しくは下記事の表をご覧ください。
Open Sourcing DataHub: LinkedIn’s Metadata Search and Discovery Platform | LinkedIn Engineering
その実力はいかに…?早速セットアップしてみました!
簡単にセットアップしてみる
セットアップはこちらのDataHub Quickstart Guideに沿って行います。Docker環境があれば動くので、今回はローカルのMac OSで立ち上げてみました。
Docker環境はすでに準備てきているとします。インストールされてない方は、ググったらいっぱい出てくると思うのでそちらをご参照ください。
まず、必要なスペックを確保します。現在はTested & confirmed config: 2 CPUs, 8GB RAM, 2GB Swap area
が必要とされているので、Docker Desktopの設定を以下のように変更しておきます。
次に、CLIで任意のディレクトリに移動し、DataHubのGitリポジトリをcloneしてquickstart.sh
を実行します。quickstart.sh
ではdocker-composeがフォアグラウンドで実行されるので、必要であればdocker-compose -p datahub up -d
と、-d
オプションを付与しておいてください。
$ git clone https://github.com/linkedin/datahub.git remote: Enumerating objects: 5, done. remote: Counting objects: 100% (5/5), done. remote: Compressing objects: 100% (5/5), done. remote: Total 50805 (delta 0), reused 3 (delta 0), pack-reused 50800 Receiving objects: 100% (50805/50805), 41.66 MiB | 6.28 MiB/s, done. Resolving deltas: 100% (27048/27048), done. $ cd ./datahub/ $ ./docker/quickstart.sh
コンテナが立ち上がったかどうかを判断するために、docker logs
で主要サービスを確認しておきます。docker logs datahub-gms
では、
2020-02-06 09:20:54.870:INFO:oejs.Server:main: Started @18807ms
docker logs datahub-frontend
では、
09:20:22 [main] INFO play.core.server.AkkaHttpServer - Listening for HTTP on /0.0.0.0:9001
のようなログが確認できればOKです。http://localhost:9001 からUIに入りましょう。デフォルトのユーザー名とパスワードはdatahub
です。
ログイン確認はできましたが、データは何も入っていません。DataHubでは親切なことにサンプルデータも用意されているので、こちらもロードしておきましょう。ingestion.sh
を実行するだけでOKです。
$ ./docker/ingestion/ingestion.sh 2815ms 2021-01-19 09:59:51 WARNING: Native build is an experimental feature and could change at any time WARNING: Found orphan containers (schema-registry, datahub-gms, kafka-setup, datahub-mce-consumer, datahub-mae-consumer, broker, elasticsearch-setup, zookeeper, kafka-topics-ui, elasticsearch, schema-registry-ui, neo4j, kafka-rest-proxy, kibana, mysql, datahub-frontend) for this project. If you removed or renamed this service in your compose file, you can run this command with the --remove-orphans flag to clean it up. Building ingestion [+] Building 213.1s (12/12) FINISHED => [internal] load build definition from Dockerfile 0.0s => => transferring dockerfile: 37B 0.0s => [internal] load .dockerignore 0.0s => => transferring context: 35B 0.0s => [internal] load metadata for docker.io/library/openjdk:8 2.9s => [internal] load metadata for docker.io/library/openjdk:8-jre-alpine 2.7s => [internal] load build context 0.6s => => transferring context: 390.20kB 0.5s => [prod-build 1/3] FROM docker.io/library/openjdk:8@sha256:b253b93dc528967eff64ade00a9079dc464fb75b9d27a7a578c8006ca0645de8 0.0s => CACHED [base 1/1] FROM docker.io/library/openjdk:8-jre-alpine@sha256:f362b165b870ef129cbe730f29065ff37399c0aa8bcab3e44b51c302938c9193 0.0s => CACHED [prod-build 2/3] COPY . datahub-src 0.0s => [prod-build 3/3] RUN cd datahub-src && ./gradlew :metadata-ingestion-examples:mce-cli:build 208.6s => [prod-install 1/2] COPY --from=prod-build datahub-src/metadata-ingestion-examples/mce-cli/build/libs/mce-cli.jar /datahub/ingestion/bin/mce-cli.jar 0.2s => [prod-install 2/2] COPY --from=prod-build datahub-src/metadata-ingestion-examples/mce-cli/example-bootstrap.json /datahub/ingestion/example-bootstrap.json 0.1s => exporting to image 0.4s => => exporting layers 0.4s => => writing image sha256:f9c455c29e8e51f59452328bcba782669805f7e191bbd70f39e285accb26ee45 0.0s => => naming to docker.io/library/datahub-ingestion 0.0s Successfully built f9c455c29e8e51f59452328bcba782669805f7e191bbd70f39e285accb26ee45 Creating ingestion ... done Attaching to ingestion ingestion | ingestion | . ____ _ __ _ _ ingestion | /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ingestion | ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ ingestion | \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ingestion | ' |____| .__|_| |_|_| |_\__, | / / / / ingestion | =========|_|==============|___/=/_/_/_/ ingestion | :: Spring Boot :: (v2.1.2.RELEASE) ingestion | ingestion | SLF4J: Class path contains multiple SLF4J bindings. ingestion | SLF4J: Found binding in [jar:file:/datahub/ingestion/bin/mce-cli.jar!/BOOT-INF/lib/logback-classic-1.2.3.jar!/org/slf4j/impl/StaticLoggerBinder.class] ingestion | SLF4J: Found binding in [jar:file:/datahub/ingestion/bin/mce-cli.jar!/BOOT-INF/lib/slf4j-log4j12-1.7.25.jar!/org/slf4j/impl/StaticLoggerBinder.class] ingestion | SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation. ingestion | SLF4J: Actual binding is of type [ch.qos.logback.classic.util.ContextSelectorStaticBinder] ingestion | 10:09:57.735 [main] INFO c.l.metadata.examples.cli.MceCli - Producing record 1 of 6 ingestion | 10:09:58.403 [main] INFO c.l.metadata.examples.cli.MceCli - Produced record. ingestion | 10:09:58.404 [main] INFO c.l.metadata.examples.cli.MceCli - Producing record 2 of 6 ingestion | 10:09:58.411 [main] INFO c.l.metadata.examples.cli.MceCli - Produced record. ingestion | 10:09:58.412 [main] INFO c.l.metadata.examples.cli.MceCli - Producing record 3 of 6 ingestion | 10:09:58.436 [main] INFO c.l.metadata.examples.cli.MceCli - Produced record. ingestion | 10:09:58.436 [main] INFO c.l.metadata.examples.cli.MceCli - Producing record 4 of 6 ingestion | 10:09:58.440 [main] INFO c.l.metadata.examples.cli.MceCli - Produced record. ingestion | 10:09:58.441 [main] INFO c.l.metadata.examples.cli.MceCli - Producing record 5 of 6 ingestion | 10:09:58.447 [main] INFO c.l.metadata.examples.cli.MceCli - Produced record. ingestion | 10:09:58.447 [main] INFO c.l.metadata.examples.cli.MceCli - Producing record 6 of 6 ingestion | 10:09:58.454 [main] INFO c.l.metadata.examples.cli.MceCli - Produced record.
サンプルデータのロードが終われば、先ほどのUIにもデータが反映されていると思います。例えば、SampleHdfsDataset
のRelationships
の画面はこんな感じです。
スタートアップについては、100点満点の導入のしやすさでした。さて、これをどう使いこなしていくかが問題です。
メタデータの作成・ロード方法
現在の公式チュートリアルでは上記のサンプルデータのロードまでで、後は各自で必要な箇所を参照してくださいというDIYな感じになっています。機能が豊富で切り口が多いため、まずは先ほどロードしたサンプルデータから辿っていくことにします。
先ほどの元データは、example-bootstrap.jsonにあるJSONで、以下のMetadataChangeEvent (MCE)というイベントを発火するコンテナアプリmce-cli
がJSONを指定の形式に変換した後、ロードする仕組みになっています。
datahub/mce-cli at master · linkedin/datahub 上記はJava製のアプリですが、同じようなデータロード用のスクリプトがPythonでもあったので、次はこちらを読み解いていきます。
datahub/mce_cli.py at master · linkedin/datahub
mce_cli.py
は、CLIからパラメータを指定して実行すればbootstrap_mce.datがロードされるようなので、datahub/metadata-ingestion at master · linkedin/datahubに沿って試しに実行してみました。
まず、MetadataChangeEvent
に使用するスキーマを作成しておきます。(※ここでCould not initialize class org.codehaus.groovy.reflection.ReflectionCache
のエラーが出る方は、ローカルのJDKのバージョンを11に下げて下さい。JDK14とgladleの5系が原因みたいです。)
cd ~/datahub/ ./gradlew :metadata-events:mxe-schemas:build
ビルドが完了したら、必要なPythonライブラリをインストールします。Python環境を汚したくない方は、venv等で切っておいてください。
cd ./metadata-ingestion/mce-cli/ pip install -r requirements.txt
環境の準備はOKです。mce_cli.py
でproduce
を実行させましょう。
python mce_cli.py produce
mce_cli.py
実行前後の状態でわかりやすい差分は、SampleKafkaDataset
のDataset Properties
に以下のようなタグがあるかないかです。
以上からわかることは、PythonやJavaなどのKafkaに対応しているプログラミング言語を噛ませれば、どんなDBのメタデータでも格納できる、ということでしょうか。
さらにDataHubでは、SQLベースのDBであれば自動でスキーマ情報を取ってくるPythonスクリプトも用意されています。噛ませているSQLAlchemyが参照できるDBなら何でも可能、ということですね。
datahub/metadata-ingestion/sql-etl at master · linkedin/datahub
次のステップ
DataHubを今後使いこなしていくためには、少なくとも以下が必要なことがわかりました。
- DataHub独自のメタデータ・スキーマ構造の理解
- KafkaのConsumeとProduceを、DataHubがどう使用しているかの理解
- DataHubが提供している専用のSDKやライブラリがないため、用途に合わせたコードをスクラッチで実装
当初はAPIベースでメタデータをやりとりできると聞いていたのですが、まだドキュメントが整理しきれておらず、気軽には使えなさそうなOSSでした。しかしながら、使いこなせたら厳格にメタデータを自動更新できるツールに化そうなので、個人的にもう少し掘り下げていけたらなと思います。