この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
データ・アナリティクス事業本部の森脇です。
データカタログ/メタデータ管理のための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自体は無いため、データカタログシステムのバックエンドとして使うと便利かなと感じました。