Netflix社製のメタデータ管理OSS「Metacat」を試してみた
データ・アナリティクス事業本部の森脇です。
データカタログ/メタデータ管理のためのOSSは色々ありますが、今回はNetflixが現在進行形で開発しているOSSの「Metacat」を試してみました。
Metacatとは
Metacatとは、Netflix OSSとして開発されているメタデータ管理サービスです。
REST/Thriftインターフェースを持っており、様々なデータソースに対して統一されたインターフェースでメタデータを参照することが可能です。
https://netflixtechblog.com/metacat-making-big-data-discoverable-and-meaningful-at-netflix-56fb36a53520より引用
Metacatには以下の特徴があります。
- APIのみ提供(Web UIは存在しない)
- Java/Groovy製
- ビジネス要件/ユーザー定義メタデータを保存可能
- データディスカバリー
- データ変更の監視/通知機能
最新の安定版(1.2.2)で、以下のコネクターが用意されています。
- cassandra
- druid
- hive
- jdbc
- mysql
- pig
- postgresql
- redshift
- s3
- snowflake
「こちら」のようなプロパティファイルを作成し、接続情報などを設定を行うことでデータソースとの連携が可能です。
試してみる
デモ用に用意されているDockerコンテナ環境で動作を確認してみます。
この環境にはMetacat本体の他にpostgresql, mysql, hive metastoreなどのデモ用データソースが含まれおり、起動後すぐに動作を確認することが可能です。
事前準備
事前に以下を用意しました。
java
build.gradleを確認すると、対応しているJavaのバージョンは8です。
そのため、Amazon Corretto 8をインストールしました。
$ java -version openjdk version "1.8.0_302" OpenJDK Runtime Environment Corretto-8.302.08.1 (build 1.8.0_302-b08) OpenJDK 64-Bit Server VM Corretto-8.302.08.1 (build 25.302-b08, mixed mode)
docker-compose
docke/docker-composeをインストールします。
$ docker --version Docker version 20.10.7, build f0df350 $ docker-compose --version docker-compose version 1.29.2, build 5becea4c
動作検証
Metacatのソースコードをcloneし、gradleコマンドでコンテナを起動します。
最新バージョンの1.3.0はRCを繰り返しており少し不安だったので、1つ前のバージョンである1.2.2を利用します。
$ git clone https://github.com/Netflix/metacat.git -b v1.2.2 $ cd metacat $ ./gradlew metacatPorts Download http://repo.spring.io/milestone/com/netflix/nebula/gradle-info-plugin/maven-metadata.xml > Configure project : Inferred project: metacat, version: 1.3.0-SNAPSHOT org.ajoberstar.github-pages is deprecated will be removed in gradle-git 2.0.0. Users should migrate to org.ajoberstar.git-publish (https://github.com/ajoberstar/gradle-git-publish). Publication nebula not found in project :. [buildinfo] Not using buildInfo properties file for this build. Publication named 'nebula' does not exist for project ':' in task ':artifactoryPublish'. . . . Creating network "metacat-test-cluster_default" with the default driver Creating metacat-test-cluster_hive-metastore-db_1 ... Creating metacat-test-cluster_postgresql_1 ... Creating metacat-test-cluster_hive-metastore-db_1 ... done Creating metacat-test-cluster_postgresql_1 ... done Creating metacat-test-cluster_storage-barrier_1 ... Creating metacat-test-cluster_storage-barrier_1 ... done Attaching to metacat-test-cluster_storage-barrier_1 storage-barrier_1 | Waiting for postgresql:5432 ...... up! storage-barrier_1 | Waiting for hive-metastore-db:3306 ...... up! storage-barrier_1 | Everything is uptCluster metacat-test-cluster_storage-barrier_1 exited with code 0 > :m+ '[' 0 -ne 0 ']'-tests:startMetacatCluster metacat-test-cluster_hive-metastore-db_1 is up-to-date Creating metacat-test-cluster_hive-metastore_1 ... Creating metacat-test-cluster_hive-metastore_1 ... done Creating metacat-test-cluster_service-barrier_1 ... Creating metacat-test-cluster_service-barrier_1 ... done Attaching to metacat-test-cluster_service-barrier_1 service-barrier_1 | Waiting for hive-metastore:9083 ... up! service-barrier_1 | Everything is up metacat-test-cluster_service-barrier_1 exited with code 0 > :m+ '[' 0 -ne 0 ']'-tests:startMetacatCluster metacat-test-cluster_hive-metastore-db_1 is up-to-date metacat-test-cluster_postgresql_1 is up-to-date metacat-test-cluster_hive-metastore_1 is up-to-date Creating metacat-test-cluster_metacat_1 ... Creating metacat-test-cluster_metacat_1 ... done Creating metacat-test-cluster_metacat-barrier_1 ... Creating metacat-test-cluster_metacat-barrier_1 ... done Attaching to metacat-test-cluster_metacat-barrier_1 metacat-barrier_1 | Waiting for metacat:8080 .. up! metacat-barrier_1 | Waiting for metacat:12001 ........................................................................................................................ up! metacat-barrier_1 | Waiting for metacat:12003 . up! metacat-barrier_1 | Waiting for metacat:12004 . up! metacat-barrier_1 | Everything is up metacat-test-cluster_metacat-barrier_1 exited with code 0 > :m+ '[' 0 -ne 0 ']'-tests:startMetacatCluster > Task :metacat-functional-tests:metacatPorts ++ Container Exposed ports ++ +++ Metacat Web Port:62571 +++ Metacat Web Debug Port:62570 +++ Metacat Hive Thrift Port:62567 +++ Metacat Embedded Hive Thrift Port:62568 +++ Metacat Embedded Fast Hive Thrift Port:62569 +++ Metacat Hive Metastore Port:62564 +++ Metacat Hive Metastore Debug Port:62565 BUILD SUCCESSFUL in 4m 6s 77 actionable tasks: 77 executed
起動が正常に完了すると、ログの最後にポート番号が表示されます。
++ Container Exposed ports ++ +++ Metacat Web Port:62571 +++ Metacat Web Debug Port:62570 +++ Metacat Hive Thrift Port:62567 +++ Metacat Embedded Hive Thrift Port:62568 +++ Metacat Embedded Fast Hive Thrift Port:62569 +++ Metacat Hive Metastore Port:62564 +++ Metacat Hive Metastore Debug Port:62565
これらがMetacatのエンドポイントです。
docker ps
の結果はこんな感じでした。
$ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 32cf33605c6a tomcat:8.0-jre8 "catalina.sh run" 3 minutes ago Up 3 minutes 0.0.0.0:62570->8000/tcp, 0.0.0.0:62571->8080/tcp, 0.0.0.0:62567->12001/tcp, 0.0.0.0:62568->12003/tcp, 0.0.0.0:62569->12004/tcp metacat-test-cluster_metacat_1 e95919639c93 danielbwatson/metacat-test-hive-metastore:1.0.0 "hive --service meta…" 3 minutes ago Up 3 minutes 2122/tcp, 8030-8033/tcp, 8040/tcp, 8042/tcp, 8088/tcp, 19888/tcp, 49707/tcp, 50010/tcp, 50020/tcp, 50070/tcp, 50075/tcp, 50090/tcp, 0.0.0.0:62565->8005/tcp, 0.0.0.0:62564->9083/tcp metacat-test-cluster_hive-metastore_1 02df342f586c postgres:9.6 "docker-entrypoint.s…" 3 minutes ago Up 3 minutes 5432/tcp metacat-test-cluster_postgresql_1 e3abd858a2eb mysql:5.6 "docker-entrypoint.s…" 3 minutes ago Up 3 minutes 0.0.0.0:62556->3306/tcp metacat-test-cluster_hive-metastore-db_1
http://localhost:${Web Port}/swagger-ui.html
にWebブラウザでアクセスしてみます。
Swagger UIを使って、APIの確認/実行が可能です。
いくつかAPIを試してみます。(curlで試しています)
カタログ一覧の取得
$ curl -s -X GET 'http://localhost:62571/mds/v1/catalog' | jq . [ { "catalogName": "embedded-hive-metastore", "connectorName": "hive" }, { "catalogName": "postgresql-96-db", "connectorName": "postgresql" }, { "catalogName": "hive-metastore", "connectorName": "hive" }, { "catalogName": "embedded-fast-hive-metastore", "connectorName": "hive" }, { "catalogName": "mysql-56-db", "connectorName": "mysql" }, { "catalogName": "s3-mysql-db", "connectorName": "s3" } ]
カタログ内の情報を取得
$ curl -s -X GET 'http://localhost:62571/mds/v1/catalog/mysql-56-db' | jq . { "databases": [ "fasthive", "hive", "metacat", "performance_schema", "s3", "sakila", "shardfasthive", "world" ], "definitionMetadata": null, "name": { "catalogName": "mysql-56-db", "qualifiedName": "mysql-56-db" }, "type": "mysql" }
カタログ配下のデータベース一覧が取得できました。
データベース配下の情報を取得
$ curl -s -X GET 'http://localhost:62571/mds/v1/catalog/mysql-56-db/database/world' | jq . { "dateCreated": null, "definitionMetadata": null, "lastUpdated": null, "name": { "catalogName": "mysql-56-db", "databaseName": "world", "qualifiedName": "mysql-56-db/world" }, "tables": [ "city", "country", "countrylanguage" ], "type": "mysql", "metadata": null, "uri": null }
自身の情報、及びデータベースに存在するテーブル一覧が取得できました。
テーブルの情報を取得
$ curl -s -X GET 'http://localhost:62571/mds/v1/catalog/mysql-56-db/database/world/table/city' | jq . { "audit": { "createdBy": null, "createdDate": 1631491200000, "lastModifiedBy": null, "lastModifiedDate": null }, "dataMetadata": null, "definitionMetadata": null, "fields": [ { "comment": "", "name": "ID", "partition_key": false, "pos": 0, "source_type": "INT(10, 0)", "type": "int", "isNullable": false, "size": 10, "defaultValue": null, "isSortKey": null, "isIndexKey": null }, { "comment": "", "name": "Name", "partition_key": false, "pos": 1, "source_type": "CHAR(35)", "type": "chararray", "isNullable": false, "size": 35, "defaultValue": "", "isSortKey": null, "isIndexKey": null }, { "comment": "", "name": "countryCode", "partition_key": false, "pos": 2, "source_type": "CHAR(3)", "type": "chararray", "isNullable": false, "size": 3, "defaultValue": "", "isSortKey": null, "isIndexKey": null }, { "comment": "", "name": "District", "partition_key": false, "pos": 3, "source_type": "CHAR(20)", "type": "chararray", "isNullable": false, "size": 20, "defaultValue": "", "isSortKey": null, "isIndexKey": null }, { "comment": "", "name": "Population", "partition_key": false, "pos": 4, "source_type": "INT(10, 0)", "type": "int", "isNullable": false, "size": 10, "defaultValue": "0", "isSortKey": null, "isIndexKey": null } ], "metadata": null, "name": { "catalogName": "mysql-56-db", "databaseName": "world", "qualifiedName": "mysql-56-db/world/city", "tableName": "city" }, "serde": null, "view": null, "partition_keys": [], "dataExternal": false }
テーブルの作成時刻/スキーマを確認することができました。
まとめ
今回はMetacatのほんの触りの部分を試してみました。
REST APIインターフェースだけでなくThriftインターフェースも提供することで、Spark/Prestとの連携も容易に行えそうです。
他にもテーブルの更新/削除、ユーザータグの設定、パーティション操作などの様々な機能が提供されていました。
WebUI自体は無いため、データカタログシステムのバックエンドとして使うと便利かなと感じました。