OpenLineage入門

# はじめに

生成AIの動向ばかりに目を奪われがちな昨今ですが、そろそろAIはAI以外の各種技術領域との連動にシフトし始めている頃合いかなと思います
そこで改めてModern Data Stack周りの技術動向調査をしている際にOpenLineageという単語を見かけました
どうやらベンダー製品にも取り込まれはじめている技術のようで気になったので調べてみて実際に動かしてみましたというメモレベルでまとめたみた系の記事となります

# OpenLineageとは？

Data Lineageの収集・分析において標準化を目的にしたものだそうです
公式サイトは[こちら](https://openlineage.io/)

> OpenLineage is an open platform for collection and analysis of data lineage. It tracks metadata about datasets, jobs, and runs, giving users the information required to identify the root cause of complex issues and understand the impact of changes. OpenLineage contains an open standard for lineage data collection, a metadata repository reference implementation (Marquez), libraries for common languages, and integrations with data pipeline tools.

そもそもデータリネージというのはデータの発生からどのような加工を経てエンドユーザーがそのデータを利活用できるようになっているかということを明示した情報のことでメタデータの一種です
業務ユーザーViewではデータ分析に使うデータに対して、そもそもこのデータどこから来たの？信頼できるの？どこかで計算間違ってない？といった問いに答える上で役に立ち、システムViewではETL/Data Pipelineの保守などで影響分析などに役に立つ情報です

こうしたデータリネージをシステム的に収集・可視化していくためには、従来はETLツールとCatalogツールがセットでサポートしている必要がありました。ただそうなると様々なベンダー製品を複合的に組み合わせたようなシステムではどうしてもデータリネージを全て捉えるきるのに限界があったのではないかと考えられます

OpenLineageはデータリネージの収集と分析を標準化することを目指したオープンなフレームワークと謳っており、この技術が普及していくことでツールに依存しない一貫したデータリネージの収集が可能になっていくと思われます

そんなOpenLineage、実体を一言でいうとAPI仕様です
[API Doc](https://openlineage.io/apidocs/openapi/)や[OpenAPI yaml](https://github.com/OpenLineage/OpenLineage/blob/main/spec/OpenLineage.md)も公開されています
そしてこの仕様を実装しているのが[Marquez](https://github.com/MarquezProject/marquez)というSoftwareでOpenLinage公式のTutorialでも登場します

# ローカルで動かしてみた

実際に動かして感触を掴んでみます
純粋にOpenLineageだけをAPIで試してみるなら公式の[Getting Started](https://openlineage.io/getting-started/)がいいと思います

ただ今回は実践的なイメージを持ちたかったのでdbtとの組み合わせで試してみました
OpenLineageの公式にも[dbtでのTutorial](https://openlineage.io/docs/guides/dbt/)があります
最初はこのガイドを参考に進めようかと思いましたが、BigQueryを使うサンプルのようです
ローカルで完結させたかったので、代わりに目に入った[3NFからDimensional Modelへの変換のtutorial](https://docs.getdbt.com/blog/kimball-dimensional-model#dimensional-modeling)をベースに進めます

## 1. dbtの準備

とりあえず前提でターゲットのDBが必要なのでPostgresをdocker-composeでサクッと上げましょう

```log
$ cat docker-compose.yaml 
services:
  db:
    image: postgres:17
    restart: always
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres
      POSTGRES_DB: postgres
    ports:
      - "5432:5432"
    volumes:
      - pg_data:/var/lib/postgresql/data

volumes:
  pg_data:

$ docker-compose up -d

$ docker compose exec db createdb -U postgres adventureworks
```

上記でDBのできあがりです
あとは[こちらのガイド](https://docs.getdbt.com/blog/kimball-dimensional-model#dimensional-modeling)に沿っていけば特段難しいところはないでしょう[^1]

[^1]: dbtのversionが低かったようなのでpackages.ymlだけ以下のように変更しました
```
$ cat adventureworks/packages.yml   
packages:
  - package: dbt-labs/dbt_utils
    version: ["1.3.0"]
```


## 2. Marquezの準備

Marquezをローカルで上げていきます。こちらもdocker対応しているので非常に簡単です

```bash
git clone https://github.com/MarquezProject/marquez
# dbtのターゲットのPostgresとportが競合するため変更しています
sh ./docker/up.sh --db-port 5433
```


## 3. OpenLineage連携

いよいよdbtからMarquezにOpenLinageで連携です
dbtのwrapperでdbt-olというツールがあるのでこれを使えば簡単にできます[^2]

[^2]: Macユーザーの方はlocalhost:5000は[AirPlayという機能がリザーブ](https://developer.apple.com/forums/thread/682332)していて、Marquezにリクエストが届きませんのでポート番号変えたりAirPlayをoffにしたりしましょう。簡単とか言いながらここでちょっと詰まりました。。

実行してみると以下のようなログが出ますeventが18個emitされたと出てますね
```
$ pip install openlineage-dbt
$ export OPENLINEAGE_URL=http://localhost:5000
$ dbt-ol run  
Running OpenLineage dbt wrapper version 1.27.0
This wrapper will send OpenLineage events at the end of dbt execution.
06:02:56  Running with dbt=1.9.1
06:02:56  Registered adapter: postgres=1.9.0
06:02:57  Found 8 models, 15 seeds, 42 data tests, 548 macros
06:02:57  
06:02:57  Concurrency: 12 threads (target='postgres')
06:02:57  
06:02:58  1 of 8 START sql table model marts.dim_address ................................. [RUN]
06:02:58  2 of 8 START sql table model marts.dim_credit_card ............................. [RUN]
06:02:58  3 of 8 START sql table model marts.dim_customer ................................ [RUN]
06:02:58  4 of 8 START sql table model marts.dim_date .................................... [RUN]
06:02:58  5 of 8 START sql table model marts.dim_order_status ............................ [RUN]
06:02:58  6 of 8 START sql table model marts.dim_product ................................. [RUN]
06:02:58  7 of 8 START sql table model marts.fct_sales ................................... [RUN]
06:02:59  5 of 8 OK created sql table model marts.dim_order_status ....................... [SELECT 1 in 0.76s]
06:02:59  4 of 8 OK created sql table model marts.dim_date ............................... [SELECT 731 in 0.81s]
06:02:59  6 of 8 OK created sql table model marts.dim_product ............................ [SELECT 504 in 0.81s]
06:02:59  2 of 8 OK created sql table model marts.dim_credit_card ........................ [SELECT 1316 in 0.82s]
06:02:59  1 of 8 OK created sql table model marts.dim_address ............................ [SELECT 1675 in 0.82s]
06:02:59  3 of 8 OK created sql table model marts.dim_customer ........................... [SELECT 19820 in 0.86s]
06:02:59  7 of 8 OK created sql table model marts.fct_sales .............................. [SELECT 5675 in 0.87s]
06:02:59  8 of 8 START sql table model marts.obt_sales ................................... [RUN]
06:03:01  8 of 8 OK created sql table model marts.obt_sales .............................. [SELECT 5675 in 2.68s]
06:03:01  
06:03:01  Finished running 8 table models in 0 hours 0 minutes and 4.23 seconds (4.23s).
06:03:02  
06:03:02  Completed successfully
06:03:02  
06:03:02  Done. PASS=8 WARN=0 ERROR=0 SKIP=0 TOTAL=8
Emitting OpenLineage events: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████| 18/18 [00:07<00:00,  2.38it/s]
Emitted 18 OpenLineage events
```

Marquezのログも見てみましょう。以下のようにPOSTが走っています

```
marquez-api     | INFO  [2025-01-31 06:02:52,804] marquez.logging.LoggingMdcFilter: status: 201
marquez-api     | 172.22.0.1 - - [31/Jan/2025:06:02:52 +0000] "POST /api/v1/lineage HTTP/1.1" 201 0 "-" "python-requests/2.32.3" 2508
marquez-api     | INFO  [2025-01-31 06:03:04,141] marquez.logging.LoggingMdcFilter: status: 201
marquez-api     | 172.22.0.1 - - [31/Jan/2025:06:03:04 +0000] "POST /api/v1/lineage HTTP/1.1" 201 0 "-" "python-requests/2.32.3" 662
marquez-api     | INFO  [2025-01-31 06:03:04,555] marquez.logging.LoggingMdcFilter: status: 201
marquez-api     | 172.22.0.1 - - [31/Jan/2025:06:03:04 +0000] "POST /api/v1/lineage HTTP/1.1" 201 0 "-" "python-requests/2.32.3" 393
marquez-api     | INFO  [2025-01-31 06:03:04,892] marquez.logging.LoggingMdcFilter: status: 201
...
```

無事リクエストを受け取れたようなので、最後にLineageの画面を見てみましょう
localhost:3000にアクセスしてみます


![FireShot Capture 024 - Marquez - localhost.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2084334/a2fe3610-e20b-73dd-3a76-0b874154d5be.png)

はい、こんな感じで可視化されたものがでてくるかと思います
UIはかなり綺麗でモダンな感じですね

# 最後に

ひとまずOpenLineageをサクッと試して感触を掴むことができました
こうした標準化された技術というのは、他のOSSのツールなどでもスキルの使い回しがききそうですし、エンジニアとしては今後の発展にも期待したいですね
今後は更にベンダー製品での適用例やAIからのデータ利活用のユースケースなども試していきたいｎと思