MLflowとMinIOを追加

Dagster + NATS イベントパイプラインが動くdevstackに、MLflow Tracking ServerとMinIOを追加しました。Dagsterは処理全体、MLflowは個々の実験を記録し、correlation_idで結びます。AIシステム開発の実験を追跡する基盤です。

8ファイルを変更し、MinIO、minio-init、MLflowの3サービスを追加しました。AirPlayのポート衝突、psycopg2不足、既存PostgreSQLへのDB追加を修正して起動しています。


なぜ MLflow が必要になったか

DagsterはNATS JetStreamからイベントをpullし、sensorでジョブを起動してPostgreSQLへ保存していました。個々の実験のパラメータ、メトリクス、アーティファクトを追う機能としてMLflowを加えます。

観測の役割を分けます。

  • Dagster: 俯瞰ビュー(何が起きたか)
  • MLflow: 詳細ビュー(中で何が起きたか)
  • correlation_id で両者を紐づける
  • Dagster asset metadata に experiment_id, run_id, tracker_url, summary を記録
  • MLflow run の tags に correlation_id を設定

Phase 2以降のfine-tuningと投資予測モデル生成に備えた構成です。


既存インフラの構成

devstack 全体の構成は以下の通り。

Core サービス:

サービス役割
NATS (JetStream)メッセージング
PostgreSQL 18 (pgvector + JIT, 4GB SHM)データストア
Dagster 3コンテナ (webserver + daemon + user-code gRPC)オーケストレーション
Vectorテレメトリ収集
FastAPI Reranker (ColBERT)リランキング

Lakehouse profile(オプション): Nessie、Trino、dbt-fusion

3ホスト構成:

  • storage server
  • desktop mac — NATS を配置し gateway と同居
  • compute server — Dagster と PostgreSQL を配置

READMEにはMinIOによるバイナリ保存方針がありましたが、podman-compose.ymlにサービスはありませんでした。MLflowのS3互換artifact storeとして追加します。


変更計画

  1. podman-compose.yml に MLflow Tracking Server を追加。backend store は既存 PostgreSQL に mlflow DB を追加して流用、artifact store は MinIO の s3://mlflow/ を使う
  2. init.sql に CREATE DATABASE mlflow; を追加
  3. Dagster 側に dagster-mlflow resource を追加し、experiment_name と mlflow_tracking_uri を設定可能にする
  4. 環境変数に MLFLOW_TRACKING_URI と MLFLOW_S3_ENDPOINT_URL を追加
  5. README.md を更新

実験記録はMLflow、オーケストレーションはDagsterです。


dagster-mlflow の API 調査

Dagsterは1.12.14に固定されているため、対応バージョンを確認しました。

dagster 1.12.Xにはdagster-mlflow 0.28.Xが対応します。今回はdagster-mlflow==0.28.14を使い、依存はdagster==1.12.14、mlflow、pandas<3.0.0、protobuf!=5.29.0です。

一時venvにインストールし、ソースで確認しました。

  • mlflow_tracking は old-style の @resource デコレータによる ResourceDefinition。既存の PostgresResource や EmbeddingResource が使っている ConfigurableResource パターンではない
  • ops から使うには required_resource_keys={"mlflow"} を指定して context.resources.mlflow でアクセスする
  • end_mlflow_on_run_finished フックを job に適用しないと MLflow run がハング状態になる。これは必須
  • MlflowMeta メタクラスが mlflow.* の全メソッドをプロキシするため、resource オブジェクトから直接 log_params(), log_metric(), log_artifact() を呼べる
  • env config で S3 認証情報を渡せるが、コンテナ環境変数として設定済みなら不要

dagster-mlflowは既存コードと異なるold-style resourceを使うパッケージです。


実装

8ファイルを変更・追加しました。

podman-compose.yml: 3サービス追加

MinIO(docker.io/minio/minio:latest)を9000/9001とminio-data volumeで追加しました。minio-initはmcでs3://mlflow/を作るone-shotコンテナです。

MLflow Tracking Server (ghcr.io/mlflow/mlflow:v2.21.3, port 5000):

  --backend-store-uri=postgresql://postgres:postgres@postgres:5432/mlflow
--default-artifact-root=s3://mlflow/
  

backend storeはPostgreSQL、artifact storeはMinIOです。depends_onでpostgresのservice_healthyとminio-initのservice_completed_successfullyを待ちます。

dagster-user-codeとdagster-daemonに環境変数を追加します。

  MLFLOW_TRACKING_URI: "http://mlflow:5000"
MLFLOW_S3_ENDPOINT_URL: "http://minio:9000"
AWS_ACCESS_KEY_ID: minioadmin
AWS_SECRET_ACCESS_KEY: minioadmin
  

init.sql

  CREATE DATABASE mlflow;
  

dagster/pyproject.toml

dagster-mlflow==0.28.14と、S3アクセス用のboto3を追加しました。

dagster/project/resources/mlflow.py(新規)

  mlflow_tracking.configured({
    "experiment_name": os.getenv("MLFLOW_EXPERIMENT_NAME", "agent-gateway"),
    "mlflow_tracking_uri": os.getenv("MLFLOW_TRACKING_URI", "http://mlflow:5000"),
    "extra_tags": {"project": "agent-gateway"},
})
  

dagster/project/defs.py

Definitionsのresourcesへ"mlflow": mlflow_resourceを追加しました。

.envrc

MLFLOW_TRACKING_URIとMLFLOW_S3_ENDPOINT_URLを追加しました。


トラブルシューティング

ポート 5000 の衝突

podman compose up -d mlflowで起動に失敗しました。

  Error response from daemon: "listen tcp :5000: bind: address already in use"
  

lsof -i :5000で、macOS ControlCenterのAirPlay Receiverが5000を使用していると分かりました。

ホストの割り当てを"${MLFLOW_PORT:-5050}:5000"へ変更しました。内部は5000のままなので、DagsterのMLFLOW_TRACKING_URI: http://mlflow:5000 も維持します。ホストからは5050です。

内部のMLFLOW_TRACKING_URIも一度5050に変えましたが、コンテナ間通信のため5000へ戻しました。.envrcはMLFLOW_PORT="5050"と"http://${COMPUTE_HOST}:${MLFLOW_PORT}"を外部アクセスに使います。

公式イメージに psycopg2 がない

ポート修正後も、起動直後にクラッシュしました。

  ModuleNotFoundError: No module named 'psycopg2'
  

使用したghcr.io/mlflow/mlflow:v2.21.3にpsycopg2がありませんでした。SQLite/MySQLを想定したイメージのため、PostgreSQLドライバを追加します。

devstack/mlflow/Dockerfileを追加しました。

  FROM ghcr.io/mlflow/mlflow:v2.21.3
RUN pip install --no-cache-dir psycopg2-binary boto3
  

podman-compose.ymlのimage:指定をbuild: context: ./devstack/mlflowへ変更し、image: localhost/agent-gateway/mlflow:2.21.3を付けました。

既存ボリュームへの DB 追加

再ビルド後はPostgreSQLのエラーでした。

  FATAL: database "mlflow" does not exist
  

init.sqlにCREATE DATABASE mlflow;を追加しても、docker-entrypoint-initdb.dは初回にしか動きません。3週間前からあるpostgres-dataには反映されませんでした。

DBを手動作成しました。

  podman exec agent-gateway-postgres-1 psql -U postgres -c "CREATE DATABASE mlflow;"
  

init.sqlの追加は、新規構築やボリューム再作成時のために残します。


結果

Alembicマイグレーション後、gunicornが4ワーカーで起動しました。

  [2026-03-12 02:07:58 +0000] [24] [INFO] Starting gunicorn 23.0.0
[2026-03-12 02:07:58 +0000] [24] [INFO] Listening at: http://0.0.0.0:5000 (24)
  

experiments/searchでDefault experimentを確認しました。artifact_locationはs3://mlflow/0でMinIOを参照しています。

変更ファイル一覧

ファイル変更内容
podman-compose.ymlMinIO, minio-init, mlflow の3サービス追加 + Dagster 環境変数追加
devstack/postgres/init.sqlCREATE DATABASE mlflow
devstack/dagster/pyproject.tomldagster-mlflow==0.28.14, boto3 追加
devstack/dagster/project/resources/mlflow.py新規: mlflow_tracking configured resource
devstack/dagster/project/defs.pymlflow resource 登録
devstack/mlflow/Dockerfile新規: psycopg2-binary + boto3 追加
.envrcMLFLOW_PORT, MLFLOW_TRACKING_URI, MLFLOW_S3_ENDPOINT_URL
README.mdサービス一覧、環境変数、トポロジー図更新

設計上の注意点

  • dagster-mlflow は old-style resource(@resource デコレータ)であり、ConfigurableResource ではない。job に @end_mlflow_on_run_finished フックを必ず適用すること
  • コンテナ間通信は内部ポート 5000、ホストからのアクセスはポート 5050(macOS AirPlay Receiver 回避)
  • PostgreSQL ボリュームが既存の場合、init.sql の新規 DB 作成は手動で反映が必要