TL;DR
- Dagsterは標準でPrometheusのメトリクスエンドポイントを持っていない
- 公式に案内されている方法(Pushgateway経由)は「実行中クラッシュ」や「Queue詰まり」を観測できないという弱点がある
- なので、DagsterのGraphQL APIを直接ポーリングしてPrometheusメトリクスに変換するexporterを自作した
- GitHubで公開中: https://github.com/HirofumiTsuda/dagster-prometheus-exporter
-
docker compose upだけでDagster+exporter+Prometheus+Grafanaの検証環境が一式立ち上がる
Pushgatewayを避ける理由やメトリクス設計(命名規則、自己計装パターンなど)は、『入門 Prometheus』(Brian Brazil、O'Reilly Japan)を参考にしています。
Motivation
Dagsterでジョブを運用していると、「今アクティブなrunはいくつあるか」「直近失敗したジョブはどれか」「Queueに詰まっていないか」をPrometheus/Grafanaで一元的に見たくなります。しかしDagsterはPrometheusのメトリクスエンドポイントを標準では持っていません。
Dagster公式が案内している方法は、dagster-prometheusリソースを使ってrun内部からPushgatewayにメトリクスをpushするというものです。ただ、これには構造的な弱点があります。
- Prometheus公式ドキュメント自体が、Pushgatewayを「スクレイプできない短命バッチジョブ用の例外的な手段」であり、通常のpull型の代替として使うべきではないと明言している
- pushはrunの中のコードから行われる。 つまりrunがOOM Killなどで途中で落ちたら、pushは一度も行われない。検知できない失敗があるというのは辛い。
- Queueに積まれているだけのrunは、まだユーザーコードが実行されていない。 push型ではそもそもpushのしようがなく、Queue詰まりを検知できない。
そこで、DagsterのGraphQL APIを外側から定期的にポーリングし、Dagster自身が持っているrunの状態からメトリクスを導出する構成にしました。
Architecture
Go製の単一バイナリで、外部の状態ストアは持ちません。scrape(状態の書き込み)とmetrics描画(状態の読み込み)を分離しているので、Dagster側のGraphQL呼び出しが遅い・失敗していても/metricsが壊れることはなく、直近の既知の状態を返し続けます。
completed runsの取得は毎回全件スキャンではなく差分取得(前回のwatermark以降のみ)にしていて、runの件数が多い環境でも継続的にメモリや取得コストが増え続けないようにしています。
Metrics
| Metric | Type | 説明 |
|---|---|---|
dagster_active_runs |
Gauge | ジョブごとの実行中run数(queued/starting/started) |
dagster_completed_runs_total |
Counter | ジョブごとの完了run累計数(success/failure) |
dagster_last_run_info |
Gauge | ジョブごとの直近完了runのステータス(kube_pod_infoと同じ"infoメトリクス"パターン) |
dagster_exporter_scrape_duration_seconds |
Gauge | exporter自身のscrape所要時間(collector別) |
dagster_exporter_last_scrape_success |
Gauge | 直近scrapeが成功したか(collector別) |
dagster_exporter_scrape_errors_total |
Counter | scrape失敗の累計数(collector別) |
最後の3つはDagsterの状態ではなくexporter自身がちゃんと動いているかを可視化するためのものです1。GraphQL呼び出しが詰まっている・失敗しているといった問題を、メトリクスの欠落から推測するのではなく直接検知できます。
PromQL Examples
# 全ジョブ横断でのアクティブrun数
sum(dagster_active_runs)
# 直近1時間の失敗run数(ジョブ別)
sum by (job_name) (increase(dagster_completed_runs_total{status="failure"}[1h]))
# 直近5分の成功率
sum(rate(dagster_completed_runs_total{status="success"}[5m]))
/
sum(rate(dagster_completed_runs_total[5m]))
# 直近runが失敗したジョブ一覧
dagster_last_run_info{status="failure"}
Quick Start
docker compose up一発で、サンプルジョブ入りのDagster + exporter + Prometheus + Grafana(ダッシュボード自動プロビジョニング済み)が立ち上がります。
git clone https://github.com/HirofumiTsuda/dagster-prometheus-exporter.git
cd dagster-prometheus-exporter
docker compose up --build
| サービス | URL |
|---|---|
| Dagster UI | http://localhost:3000 |
Exporter /metrics
|
http://localhost:9101/metrics |
| Prometheus | http://localhost:9090 |
| Grafana | http://localhost:3001 |
既存のDagster環境に対して単体で動かしたい場合は、Docker imageもGHCRで公開しています(linux/amd64/linux/arm64で使用可能)。
docker run -p 9101:9101 \
-e DAGSTER_GRAPHQL_ENDPOINT=http://dagster:3000/graphql \
ghcr.io/hirofumitsuda/dagster-prometheus-exporter:latest
Goが使える環境ならgo installでも:
go install github.com/HirofumiTsuda/dagster-prometheus-exporter/cmd/exporter@latest
Roadmap
以下のことができればと思っています。
- Run duration(直近完了runの所要時間、実行中runの経過時間)
- Schedule / Sensor tick status
- k8s向けのHelm chart
Conclusion
Dagsterを本番運用していて、Prometheus/Grafanaで一元監視したいけど公式のPushgateway方式だとOOM Killを検知できない、という方の助けになれば幸いです。
GitHub: https://github.com/HirofumiTsuda/dagster-prometheus-exporter
-
『入門 Prometheus』(Brian Brazil、O'Reilly Japan)で紹介されている、exporter自身の健全性(スクレイプ時間・成否)を計装するプラクティスを参考にしています。 ↩
