Table definition as Codeを実現するflagonを作ったお話

はじめに

これは DMM.com #1 Advent Calendar 2017 の20日目の記事です。

前日は @Hiro2525h さんの Gitのコミットログを綺麗にする でした。

私はビッグデータ部におります @i_szyn です。

早速ですがみなさんテーブル定義、管理していますか? どのように管理されていますか?
アプリケーション単位だとやっぱりマイグレーションの仕組みで管理されていたり、Excel・Google Spreadsheetなどがよく挙げられるのではないでしょうか。
物理名や型、はたまたコメントやどんなデータが入るのかなどの仕様までをしっかりと管理しておきたいとなると…しかもたくさんの人に見られるとなると!なかなか苦労する場面があるのではないかというのが個人的な感想です。

このように私達はテーブル定義の管理に関して色々と課題を抱えていたため、今回 flagon というプロダクトを作りました。
本日は、そのflagonを作るに至った経緯・技術的なところまでまんべんなくご紹介したいと思います。

前提

まずはこの投稿を理解する上で必要となる条件について挙げておきます。

  • ビッグデータ部では色々なログ・データ取り込みを行なっており、それらのテーブル定義を管理したい
    • アナリスト/エンジニアが分析する際に利用するもの

なお、テーブル定義の管理対象は下記のものです。

  • Hiveにあるテーブル定義(取り込み元はRDBMSなど)
  • 物理名・論理名・型・コメント・パーティションの有無

これまでの課題

次に私達が抱えていた課題をご紹介します。

ビッグデータ部内で管理しているシステムのテーブル定義は、基本的にConfluenceで管理されている状態でした。
新規でデータの取り込み依頼されるものも多くあり、その分に関しても取り込みを行う度にテーブル定義を私達が作成していく必要がありました。

ここでの課題はこんなものがありました。

  1. Confluenceでの定義作成に大変時間 を要する
    • 自動化されておらず全て手作業で行うため
    • 取り込み先の定義がExcel等で管理されていた場合は私達でConfluenceに転記する必要がある
    • 取り込み先の定義がわからない場合 show create table 文などでDDLを抽出し定義を作成
      • 論理名が推測でしかなく正確性にかけてしまう
      • 実際にどのようなデータが入るかなどデータの仕様も管理したい
  2. Confluenceにまとめていたが人によってフォーマットが異なる
    • データの取り込み担当になる人が定義も作成するため毎回一定の人ではない
    • テンプレートの機能なども利用していなかったため、書き方が統一されていなかった
  3. 突然、テーブル定義が変更されていて知らないカラム名が増えてしまい情報の乖離が生じる

目指したかったものは、できるだけ自動化しつつ効率良く管理できる方法でした。
そこでまずは、OSSで解決できないかな?と調べはじめました。

OSS という選択肢

OSSを利用する場合は、おそらく下記にあげるものが有力な候補になるのではないかと思います。
実際に私も検証として手元で動かしてみたので、簡単に特徴や所感を書いておきます。

schemaspy

特徴

Javaでできており、JDBC経由でDBからちゃんとドキュメントを生成してくれます。
簡単に動かせるので試してみたい場合オススメです。

所感・良い点

  • CIに組み入れるのも簡単そう
  • ER図やリレーションの情報も標準で簡単に見れる

採用を見送った理由

  1. 複数DBに対応できない
    • 管理できるのが1つのDBに限られている
    • できるとしても結構頑張る必要がありそう
    • 1アプリケーションで利用するDB毎に管理するイメージ
  2. 独自に論理名等の情報を入れるのが難しそう
    • ソースも含めて見てみましたが、改修するにしても結構工数がかかってしまいそうな印象

プラスαで何か情報を追加したい人は向いていないかな〜?と思います。
しかし、もしも既存のDBのテーブル定義をCIなどで回して簡単に可視化したい場合は良いと思いました。

Dmemo

特徴

Ruby on Railsでできており、Rubyの実行環境とPostgreSQLが必須となります。
対応するデータソースは、Redshift・PostgreSQL・MySQLがあり、Active Recordを利用してテーブルの定義を取得していました。同期処理を定期実行させることで自動で取得可能です。(しかし定期実行する仕組みは別途必要)

詳しいことは作者の方がブログに書かれているので、あわせてご覧ください。

所感・良い点

  • ちゃんと認証 (Google Oauth 2.0) に対応している点
  • データの仕様を管理仕組みが非常に素晴らしい
    • Markdownで柔軟に記載することが可能
    • しっかりと履歴の保持ができる

採用を見送った理由

  1. ウェブアプリケーションなので、自分達で運用する必要が出てくる

Hiveに対応させて使わせていただこうかな?と思ったのですが…
要件としてサーバ運用はしたくないということがあったため、今回は断念してしまいました。
ですが、個人的には機会があればぜひ使いたいな!と思ったプロダクトでした。

flagon誕生

と、ここまで見てきたのですが微妙に私達が必要とするものを満たすものがないと判断したため、
flagonを作ることを決意しました。

flagonとは

テーブル定義を管理できるプロダクトの名称です。

名前の由来も紹介しておきましょう。

flagonには、 大きな瓶 という意味があります。
テーブル定義書を花とみたてていて、その大きな瓶をたくさんの花🌷(=テーブル定義書)で満たしたい!
という想いから名付けました(๑•̀ㅂ•́)و✧

…ちょっと気恥ずかしいですね。続けますと、

flagonは、

  • テーブル定義データの扱いをする flagon-cli
  • ドキュメント管理を行う flagon-docs

という2つのコンポーネント(リポジトリ)から成り立ちます。

それぞれの役割を明確化することで、お互いが疎結合になりました。
結果として、メンテナンスがしやすくなっています。

役割は後ほど紹介するとして、まずはflagonの特徴についてご紹介します。

flagonの特徴

Table definition as Code

◯◯ as Code はよく聞くことがあると思いますがそれのテーブル定義版1です。
その名の通りでテーブル定義をコードベース(YAMLファイル) で管理できるようにしました。
これにより可動性が向上 / 誰でもメンテナンスができるようになりました。

Github上で管理しているため、Pull Requestなどでレビューを行うことが可能です。
Github Enterpriseでソースを公開しているので、アカウントがあれば誰でも貢献することができます。

定義の自動取得機能

ドキュメントの提供にあたり、取得元定義を参照してConfluenceに定義を書くのは時間がかかります。
flagon-cli を利用することで、テーブル定義(型・物理名・コメント・パーティション情報)を自動で取得した、雛形(YAMLファイル)を作成してくれます。

あとは定義を埋めるだけで完成です♪

統一されたインターフェース

これまでは Confluence にまとめていることで、フォーマットが微妙に異なるなど問題が起こることがありましたが、flagonでは誰が書いても同じ見た目になります。

低コスト

Github pagesでホスティングされているため余計なコストが掛かりません。
またサーバ運用の手間が省けます。

flagonの各コンポーネント紹介

ではその役割や機能を紹介していきます。

flagon-cli

その名の通りCLIで、主に下記の機能があります。

  1. Hiveのテーブル定義(型・物理名・コメント・パーティション情報)を取得し、YAML形式のファイル雛形を作成
  2. YAML形式のファイルからMarkdown形式のファイルに変換する機能

ここでは、サンプルとして何らかのアプリケーションのマスターのテーブルがあると仮定しましょう。

※ サンプルとして適当に考えただけなので実際に存在している訳ではありません。

CREATE DATABASE IF NOT EXISTS sample;

CREATE TABLE IF NOT EXISTS `sample`.`app`(
  `id` string,
  `name` string,
  `package_name ` string,
  `version` string,
  `platforms` array<string>
);

あらかじめHive上にテーブルがあることが前提ですが、上記のテーブル定義にもとづいて下記のような雛形のYAMLファイルが自動で生成されます。(desc文などをパースしてYAMLにしている)

name: app
logical_name: '未設定'
source: '未設定'
desc: ''
columns:
  - name: id
    type: string
    logical_name: ''
    comment: ''
  - name: name
    type: string
    logical_name: ''
    comment: ''
  - name: package_name
    logical_name: ''
    type: string
  - name: version
    type: string
    logical_name: ''
    comment: '' 
  - name: platforms
    type: array<string>
    logical_name: ''
    comment: ''

抽出されて自動で入力されるものは、テーブル名・カラム名・カラムの型です。
この例では空なのですが、もし元のDDL文にコメントが記載されている場合はそれが反映される仕組みになっています。

この時点では、論理名やテーブルのコメントが入っていないので私達が埋めてあげる必要があります。 今回はこんな感じにしてみました。

name: application
logical_name: 'アプリケーション'
source: '未設定'
desc: 'アプリケーションを管理するためのマスターテーブル'
columns:
  - name: id
    type: string
    logical_name: 'アプリID'
    comment: 'ユニークなアプリIDが入っています'
  - name: name
    type: string
    logical_name: 'アプリ名'
    comment: 'アプリケーション名が入ります'
  - name: package_name
    logical_name: 'パッケージ名'
    type: string
    comment: >-
      アプリケーションのパッケージ名が格納されます<br>
      e.g. `com.hoge.app`
  - name: version
    type: int
    logical_name: 'バージョン'
    comment: 'アプリのバージョン情報'
  - name: platforms
    type: array<string>
    logical_name: 'プラットフォーム'
    comment:  >-
      対応するプラットフォームの情報が格納されます<br>
      e.g. `ios` / `android`

flagon-cliを利用してMarkdownに変換するとこうなります。

### application (アプリケーション)

:fa-external-link: Source: 未設定
:fa-calendar-check-o: Last modified: `2017-12-07 05:54:56`

!!! summary
    アプリケーションを管理するためのマスター

---

|Physical Name|Logical Name|Data Type|Partition|Comment|
|---|---|---|---|---|
|id|アプリID|string||ユニークなアプリIDが入っています|
|name|アプリ名|string||アプリケーション名が入ります|
|package_name|パッケージ名|string||アプリケーションのパッケージ名が格納されます<br> e.g. `com.hoge.app`|
|version|バージョン|int||アプリのバージョン情報|
|platforms|プラットフォーム|array<string\>||対応するプラットフォームの情報が格納されます<br> e.g. `ios` / `android`|

余計な情報も入っていますが、これは後述のflagon-docs(mkdocs)上で管理されているので、そこに必要な情報も載っている状態となっているためです。

テーブル定義の部分だけ下記に抜き出してみました。
以下のようにきれいに定義を見ることができます。

Physical Name Logical Name Data Type Partition Comment
id アプリID string ユニークなアプリIDが入っています
name アプリ名 string アプリケーション名が入ります
package_name パッケージ名 string アプリケーションのパッケージ名が格納されます
e.g. com.hoge.app
version バージョン int アプリのバージョン情報
platforms プラットフォーム array 対応するプラットフォームの情報が格納されます
e.g. ios / android

※ 実際には、こちらでで紹介したものは実際は次に紹介する flagon-docs のリポジトリで管理されています。

なぜ取得時にMarkdownにしなかったか?

なんでわざわざYAMLなの?Markdownでいいじゃん!という疑問が上がりそうですが、基本的にテーブル定義はMarkdownのテーブルを用いて表現しており、そのままでは非常にメンテナンスしづらい問題があったためです。
YAMLで管理した方が人間が読みやすく、プログラムでもパースしやすい利点があったためためこの手法を取りました。

採用技術

実際にどんな技術を利用して作ったかご紹介します。

言語

- 備考
Python 3 コマンドラインのインターフェースを提供していますがclickなどは使わずargsparseを利用

採用理由としては、今回ライブラリが非常に優秀で使いやすいものが揃っていたからでした。

ライブラリ

- 備考
PyHive Hive / Presto にThrift経由で簡単にクエリ実行が可能
今回ではテーブル定義の取得時に利用
PyYAML PythonでYAMLをパースするライブラリ
取得したテーブル定義をYAMLファイルにする際に利用
Jinja2 Ansible使っている人なら馴染みがあるテンプレートエンジン
YAMLをMarkdownに変換する際に利用

flagon-cliは社内のGithubにて公開されていて、 pip を利用してインストールするか docker で動かせるようになっています。

そのためテーブル定義の取得時はJenkinsから、ドキュメントのビルド時はCircleCIから簡単に利用できる形になっています。

さて、ここまでがCLIの説明です。 次にflagon-cliで生成したドキュメントを管理する flagon-docsの説明をしたいと思います。

flagon-docs

こちらは下記の機能があります

  • テーブル定義YAMLの管理
  • Github Pagesにて実際にテーブル定義を公開

先程のflagon-cliで紹介したMarkdownが、Github Pages上でホスティングされているflagonからどう見えるかというと…?

flagon

こんな感じになります!では実際に技術的なところを見ていこうと思います。

採用技術

- 備考
Mkdocs 静的サイトジェネレータでMarkdownをHTMLに変換してくれます。
同じ用途だとHugoが有名かと思いますが、こちらもシンプルで使いやすくてオススメです。
PythonのMarkdown拡張に対応していたり、テーマも色々あります。
CircleCI よく知れたCircleCI。flagon-cliでYAMLからMarkdownに変換・mkdocsでドキュメントビルド・Githubページの公開までもはやお世話になりっぱなしです。

それぞれの役割がわかったところで、どのような運用をしているかご紹介します。

運用イメージ

現状の運用は以下の図に示す通りです。

flagon

Jenkins

テーブル定義の取り込み部分を担っています。

ジョブ(実際にはPipeline)が用意されており(図の1~2)

  • 必要なパラメータを入力してビルド
  • Hiveテーブルの定義を抽出し雛形のYAMLファイル作成・コミット
  • Github上にPull Request作成

をしてくれます。レビューして問題なければマージします。

テーブル定義を埋める

ここが重要です。

Jenkinsから抽出した定義は雛形ともいうべき状態で論理名などが埋まっていないので、
ブランチをきって適宜人の手で更新してPull Requestを送る運用になっています。 ちゃんとPRとコードベースでのやり取りができるのは大きなメリットだと感じております。

CircleCIでドキュメント更新

こちらは、masterにマージされたら自動でビルドされてドキュメントが公開(更新)される形になっています。

下記のようなフローです(図の3~4)

  1. YAMLからMarkdownに変換
  2. MarkdownをHTMLに変換
  3. Github Pagesで公開 (ビルドした成果物(HTML)をgh-pagesブランチに反映)

ここでのポイントはMarkdownはGitの管理対象になっておらず、
CircleCIで 毎回YAMLからMarkdownに変換 しているという点です。

残念なところ

Markdownテーブル内でMarkdownがかけない

長い文章を書きたい時に不便です。
コメントではよくあることですが改行したかったり複数行書きたかったりすることがありますがMarkdownのテーブル内ではMarkdownが書けないため、改行したい場合は直接HTMLのタグ書いてもらうような仕組みになっています。

どういうことかというと改行したい時はこんな感じにしてもらっています。

- name: package_name
  logical_name: 'パッケージ名'
  type: string
  comment: >-
    アプリケーションのパッケージ名が格納されます<br>
    e.g. `com.hoge.app`

もしPythonのMarkdownの拡張機能でオススメとかあれば教えて欲しいです。

flagon-docsの検索の精度が微妙

これはmkdocsが原因で起こっている話なのですが、デフォルトでは日本語検索に対応していないようで利用者にはテーブル名で検索をしてもらっています。

現状大きな問題にはなっていないのですがユーザービリティーの観点からはあまりよくないと思っています。

今後の展望

複数データソースに対応させてみたい

現状対応できるのは、Presto / Hiveだけなのが現状です。
MySQL / PostgreSQLなどデータソースを対応させることでより汎用的な仕組みになると思っています。

まとめ

今回はflagonについて紹介させていただきました。(OSSでないのが心残りでありますが…)

すごい急いで作ったこともあり正直にいって、まだまだ出来の悪い部分も多いのですが、仕組みとしてはなかなか良いものになったと信じております。

もし面白いなぁと少しでも思ってもらえたら嬉しいです!


さて、明日は@usagi-fさんで「GUI構築でオブジェクト指向を考えてみる」です。

カレンダーのURLはコチラ - DMM.com #1 Advent Calendar 2017 - DMM.com #2 Advent Calendar 2017


  1. 実際に存在する訳ではなく、私が言っているだけの造語です [return]