MLflowをDocker ComposeとMySQLとRustFSを用いてオンプレサーバで構築する
- 2025/12/16
- Update: 2025/12/16
- Tool
Note
この記事はMLflow をオンプレサーバで構築するの更新版です. 前回は artifact ストレージを MinIO を用いて構築していますが,今回は RustFS を用いて構築しています.
こんにちは.今回は実験・モデル管理などの MLOps プラットフォームである MLflow を Docker Compose と MySQL と RustFS を用いてオンプレミスで構築する方法についてご紹介します.
前回書いた記事では,OSS の S3 互換オブジェクトファイルストレージである MinIO を用いて構築していましたが,OSS 版の MinIO は Docker イメージの配布が終了し,ソースコード自体もメンテナンスモードに移行してしまいました. そこで,代替として今回は Rust で書かれた OSS のオブジェクトファイルストレージである RustFS を用いて構築していきます.
Warning
2025.12.16 現在,RustFS はまだ正式リリースされていないため, 本記事では RustFS の alpha 版を用いて構築しています. 開発中のため予期せぬ不具合が発生する可能性があります. 予めご了承ください.
本記事の構築例は以下の Github リポジトリにて公開しています.
公式ドキュメントは以下です.
MLflow
RustFS
導入の背景
機械学習プロジェクトでは,ハイパーパラメータやモデル,データセットを変更しながら様々な実験を行うことになります.この際,結果の比較を効率的に行える実験管理ツールを導入することで,モデルの開発に集中できます. この手の実験管理ツールは,Tensorboard や Weight and Bias(wandb)など様々ありますが,データの外部送信を行わずに扱える「オンプレミス」という点に着目すると選択肢はさほど多くないです. そこで,オンプレミスで構築可能な代表的なプラットフォームである MLflow を構築してみようと思います.
MLflow とは
Mlflow はオープンソースの MLOps プラットフォームで,以下の 5 つのケースをサポートしています.
- Tracking & Experiment Management: 実験の結果を管理し,比較を行う
- Model Registry: 機械学習モデルのバージョン管理を行う
- Model Deployment: 機械学習モデルのサービングを行う
- ML Library Integration: 機械学習ライブラリとの統合
- Model Evaluation: 機械学習モデルの性能評価
これらのケースで MLflow を利用するためには,backend store としてパラメータを保存するデータベースと,artifact store としてモデルの重みやログファイル等を保存するオブジェクトファイルストレージが必要になります.そこで今回は,Docker Compose を用いて以下のような構成を取ることで,完全オンプレミスで MLflow を構築していきます.
- backend store: MySQL
- artifact store: RustFS
私は主に実験の管理を行う Tracking Server を利用するため,その部分を中心に記事を書いていますが,他のケースについても今回の構築例をベースとして利用できるはずです.
構成図
今回は Docker Compose を用いて,以下のような構成で MLflow Server を構築していきます.

MLflow の Tracking Server は,実験のパラメータや結果を MySQL の DB に保存し,artifact を RustFS に保存します. また,MLflow の WebUI は Nginx Proxy を用いて Basic 認証をかけることで,一部のユーザーのみがアクセスできるようにします.
Quick Start
手取り早く構築する場合は,以下の GitHub リポジトリをクローンして,README.mdの手順に従うか,以下のコマンドを実行してください.
git clone https://github.com/mjun0812/MLflow-Docker.git
cd MLflow-Docker
cp env.template .env
vim .env
.envファイルを編集して,待ち受けドメインと MLflow のバージョンを指定してください.
# 待ち受けドメインを指定.
# localhostのみだと,ローカルからのみアクセスできるようになります.
VIRTUAL_HOST=localhost
# MLflowのバージョンを指定したい場合はここに指定
# 指定しない場合は最新版が使用されます
MLFLOW_VERSION=
(Optional) Basic 認証をかける場合は,nginx/htpasswd/localhostファイルにユーザー名とパスワードを設定してください.
htpasswd -c nginx/htpasswd/localhost [username]
次に,以下のコマンドを実行して,イメージのビルドとコンテナの起動を行います.
docker compose up -d
これで,localhost:15000で MLflow の WebUI にアクセスできるようになります.
Python のコードから MLflow を利用する場合は,以下のようにしてください.
import os
import mlflow
# Basic認証をかける場合
os.environ["MLFLOW_TRACKING_USERNAME"] = "username"
os.environ["MLFLOW_TRACKING_PASSWORD"] = "password"
# 環境変数で設定する場合
os.environ["MLFLOW_TRACKING_URI"] = "http://localhost:15000"
mlflow.set_tracking_uri("http://localhost:15000")
mlflow.set_experiment("example")
with mlflow.start_run():
mlflow.log_param("param1", 1)
mlflow.log_metric("metric1", 1)
構築の詳細
次に,構築の詳細について説明します.まずファイルの全体を示し,その後,各コンテナの設定を見ていきます.本記事の例では,以下のコンテナを立てて構築しています.
- Nginx Proxy(jwilder/nginx-proxy)
- MLflow Server (自前 Dockerfile)
- MySQL
- RustFS
services:
nginx-proxy:
image: jwilder/nginx-proxy:latest
restart: unless-stopped
ports:
- "15000:80"
volumes:
- ./nginx/htpasswd:/etc/nginx/htpasswd
- ./nginx/conf.d/proxy.conf:/etc/nginx/conf.d/proxy.conf
- /var/run/docker.sock:/tmp/docker.sock:ro
networks:
- mlflow-net
mlflow:
build:
context: .
dockerfile: Dockerfile
args:
MLFLOW_VERSION: ${MLFLOW_VERSION}
expose:
- "80"
restart: unless-stopped
depends_on:
db:
condition: service_healthy
rustfs-init:
condition: service_completed_successfully
env_file:
- .env
environment:
TZ: Asia/Tokyo
VIRTUAL_HOST: "${VIRTUAL_HOST:-localhost}"
MLFLOW_S3_ENDPOINT_URL: http://rustfs:9000
AWS_ACCESS_KEY_ID: rustfs-mlflow
AWS_SECRET_ACCESS_KEY: rustfs-mlflow
MLFLOW_BACKEND_STORE_URI: mysql+mysqldb://mlflow:mlflow@db:3306/mlflow
command: >
mlflow server
--backend-store-uri 'mysql+mysqldb://mlflow:mlflow@db:3306/mlflow'
--artifacts-destination 's3://mlflow/artifacts'
--serve-artifacts
--host 0.0.0.0
--port 80
networks:
- mlflow-net
- mlflow-internal-net
db:
image: mysql:latest
restart: unless-stopped
environment:
MYSQL_USER: mlflow
MYSQL_PASSWORD: mlflow
MYSQL_ROOT_PASSWORD: mlflow
MYSQL_DATABASE: mlflow
TZ: Asia/Tokyo
volumes:
- ./mysql/data:/var/lib/mysql
- ./mysql/my.cnf:/etc/mysql/conf.d/my.cnf
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 5s
timeout: 10s
retries: 5
networks:
- mlflow-internal-net
rustfs:
image: rustfs/rustfs:latest
security_opt:
- "no-new-privileges:true"
# ports:
# - "9000:9000" # S3 API port
environment:
- RUSTFS_VOLUMES=/data/rustfs
- RUSTFS_ADDRESS=0.0.0.0:9000
- RUSTFS_CONSOLE_ENABLE=false
- RUSTFS_EXTERNAL_ADDRESS=:9000
- RUSTFS_CORS_ALLOWED_ORIGINS=*
- RUSTFS_ACCESS_KEY=rustfs-mlflow
- RUSTFS_SECRET_KEY=rustfs-mlflow
- RUSTFS_OBS_LOGGER_LEVEL=info
# Object Cache
- RUSTFS_OBJECT_CACHE_ENABLE=true
- RUSTFS_OBJECT_CACHE_TTL_SECS=300
volumes:
- ./rustfs:/data/rustfs
restart: unless-stopped
healthcheck:
test: ["CMD", "sh", "-c", "curl -f http://localhost:9000/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
networks:
- mlflow-internal-net
# - mlflow-net
rustfs-init:
image: amazon/aws-cli:latest
depends_on:
rustfs:
condition: service_healthy
environment:
- AWS_ACCESS_KEY_ID=rustfs-mlflow
- AWS_SECRET_ACCESS_KEY=rustfs-mlflow
- AWS_DEFAULT_REGION=us-east-1
- AWS_REGION=us-east-1
entrypoint: /bin/sh
command: -c "aws --endpoint-url http://rustfs:9000 s3api create-bucket --bucket mlflow || true"
restart: "no"
networks:
- mlflow-internal-net
# RustFS volume permissions fixer service
volume-permission-helper:
image: alpine
volumes:
- ./rustfs:/data
command: >
sh -c "
chown -R 10001:10001 /data &&
echo 'Volume Permissions fixed' &&
exit 0
"
restart: "no"
networks:
mlflow-net:
driver: bridge
mlflow-internal-net:
internal: true
nginx-proxy
Nginx Proxy は,MLflow の WebUI を Nginx で Proxy するために用いています.ここで用いているjwilder/nginx-proxyの Docker イメージは,Nginx の設定ファイルをほとんど書かずにcompose.ymlの記述と特定のボリュームのマウント,環境変数の編集を行うだけで,Basic 認証をかけた Nginx Proxy を立てることができます.
nginx-proxy:
image: jwilder/nginx-proxy:latest
restart: unless-stopped
ports:
- "15000:80"
volumes:
- ./nginx/htpasswd:/etc/nginx/htpasswd
- ./nginx/conf.d/proxy.conf:/etc/nginx/conf.d/proxy.conf
- /var/run/docker.sock:/tmp/docker.sock:ro
networks:
- mlflow-net
今回は proxy したいサービスが MLflow Server 1 つだけなので,nginx-proxy の利用は過剰に見えますが,ファイルの配置だけで,簡単に待ち受けドメインの指定や Basic 認証の on/off を切り替えることができるため利用しています.
まず,nginx 全体の設定として,nginx/conf.d/proxy.confに以下のような設定を追加しておきます.
client_max_body_size 100g;
これは,MLflow Server が送信するファイルのサイズが大きい場合に備えて,巨大なファイルを送信できるようにするための設定です.
次に待ち受けするドメインの指定と Basic 認証の設定を mlflow のコンテナの環境変数VIRTUAL_HOSTで記述しておきます.
mlflow:
expose:
- "80"
environment:
VIRTUAL_HOST: "example.com,localhost"
この環境変数の値はカンマ区切りで複数のドメインを指定できます.また,proxy したいポートをexposeで指定しておきます.ここでexposeで指定したポートは,nginx-proxy のポートにマッピングされるため,nginx-proxy側で以下のようにポートを指定しておきます.
nginx-proxy:
ports:
- "15000:80"
これで,外からexample.com:15000とlocalhost:15000で MLflow の WebUI にアクセスできるようになります.
Basic 認証を設定する場合は,nginx-proxy のボリュームにマウントしたnginx/htpasswdファイルにユーザー名とパスワードを設定しておきます.このとき,Basic 認証のファイル名はドメイン名と同じになるようにしておきます.
cd nginx/htpasswd
htpasswd -c example.com [username]
cp example.com localhost
これで,example.comとlocalhostから MLflow の WebUI にアクセスできるようになります.待ち受けドメインの変更や Basic 認証が不要になった場合でも,nginx の設定ファイルはコンテナ起動時に更新されるため,手動で変更する必要はありません.
MLflow
MLflow Server は,以下の Dockerfile と compose.yml で構築しています.MLflow のバージョンを環境変数MLFLOW_VERSIONで指定できるようにしています.指定を行わない場合は最新版が使用されます.
MLflow は DB との接続に SQLAlchemy を使用しているため,DB に合わせたドライバーの MySQL のクライアントライブラリであるmysqlclientが必要になります.また,S3 互換のオブジェクトファイルストレージである RustFS にアクセスするために,boto3をインストールしておきます.
FROM python:3.13
ARG MLFLOW_VERSION=""
RUN if [ -n "$MLFLOW_VERSION" ]; then \
pip install --no-cache-dir mlflow=="$MLFLOW_VERSION" mysqlclient boto3; \
else \
pip install --no-cache-dir mlflow mysqlclient boto3; \
fi
コンテナ内でmlflow serverコマンドを実行し,MLflow Server を立てています.
ここで,--backend-store-uriオプションで MySQL の接続情報を指定し,
--artifacts-destinationオプションで RustFS 内のバケットやフォルダのパスを指定しています.
また,--serve-artifactsオプションで,artifact を MLflow Server が動作しているコンテナから RustFS に保存するようにしています.この設定がない場合は,クライアントが S3 に直接アクセスして,artifact を保存することになります.
mlflow:
build:
context: .
dockerfile: Dockerfile
args:
MLFLOW_VERSION: ${MLFLOW_VERSION}
expose:
- "80"
restart: unless-stopped
depends_on:
db:
condition: service_healthy
rustfs-init:
condition: service_completed_successfully
env_file:
- .env
environment:
TZ: Asia/Tokyo
VIRTUAL_HOST: "${VIRTUAL_HOST:-localhost}"
MLFLOW_S3_ENDPOINT_URL: http://rustfs:9000
AWS_ACCESS_KEY_ID: rustfs-mlflow
AWS_SECRET_ACCESS_KEY: rustfs-mlflow
MLFLOW_BACKEND_STORE_URI: mysql+mysqldb://mlflow:mlflow@db:3306/mlflow
command: >
mlflow server
--backend-store-uri 'mysql+mysqldb://mlflow:mlflow@db:3306/mlflow'
--artifacts-destination 's3://mlflow/artifacts'
--serve-artifacts
--host 0.0.0.0
--port 80
networks:
- mlflow-net
- mlflow-internal-net
RustFS
RustFS は起動時のみ動作するrustfs-init, volume-permission-helperの 2 つのコンテナと,Serve を行うコンテナのrustfsの 3 つのコンテナで構成されています.
rustfs-init: RustFS の起動時にバケットを作成するためのコンテナ.volume-permission-helper: RustFS のボリュームのパーミッションを修正するためのコンテナ.RustFS のボリュームのパーミッションが不適切な場合に動作する.rustfs: RustFS のコンテナ
rustfs:
image: rustfs/rustfs:latest
security_opt:
- "no-new-privileges:true"
# ports:
# - "9000:9000" # S3 API port
environment:
- RUSTFS_VOLUMES=/data/rustfs
- RUSTFS_ADDRESS=0.0.0.0:9000
- RUSTFS_CONSOLE_ENABLE=false
- RUSTFS_EXTERNAL_ADDRESS=:9000
- RUSTFS_CORS_ALLOWED_ORIGINS=*
- RUSTFS_ACCESS_KEY=rustfs-mlflow
- RUSTFS_SECRET_KEY=rustfs-mlflow
- RUSTFS_OBS_LOGGER_LEVEL=info
# Object Cache
- RUSTFS_OBJECT_CACHE_ENABLE=true
- RUSTFS_OBJECT_CACHE_TTL_SECS=300
volumes:
- ./rustfs:/data/rustfs
restart: unless-stopped
healthcheck:
test: ["CMD", "sh", "-c", "curl -f http://localhost:9000/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
networks:
- mlflow-internal-net
# - mlflow-net
rustfs-init:
image: amazon/aws-cli:latest
depends_on:
rustfs:
condition: service_healthy
environment:
- AWS_ACCESS_KEY_ID=rustfs-mlflow
- AWS_SECRET_ACCESS_KEY=rustfs-mlflow
- AWS_DEFAULT_REGION=us-east-1
- AWS_REGION=us-east-1
entrypoint: /bin/sh
command: -c "aws --endpoint-url http://rustfs:9000 s3api create-bucket --bucket mlflow || true"
restart: "no"
networks:
- mlflow-internal-net
# RustFS volume permissions fixer service
volume-permission-helper:
image: alpine
volumes:
- ./rustfs:/data
command: >
sh -c "
chown -R 10001:10001 /data &&
echo 'Volume Permissions fixed' &&
exit 0
"
restart: "no"
上記の例では,RustFS の WebUI を無効化していますが,必要に応じて以下のように設定し,有効化することもできます.
nginx-proxy:
ports:
- "15001:9001"
rustfs:
image: rustfs/rustfs:latest
security_opt:
- "no-new-privileges:true"
ports:
# - "9000:9000" # S3 API port
# Nginx Proxyの設定を追記
expose:
- "9001"
environment:
# 待ち受けドメインの指定
- VIRTUAL_HOST=example.com,localhost
- RUSTFS_VOLUMES=/data/rustfs
- RUSTFS_ADDRESS=0.0.0.0:9000
- RUSTFS_EXTERNAL_ADDRESS=:9000
- RUSTFS_CORS_ALLOWED_ORIGINS=*
- RUSTFS_ACCESS_KEY=rustfs-mlflow
- RUSTFS_SECRET_KEY=rustfs-mlflow
- RUSTFS_OBS_LOGGER_LEVEL=info
# WebUIの設定を追記
- RUSTFS_CONSOLE_ADDRESS=0.0.0.0:9001
- RUSTFS_CONSOLE_ENABLE=true
- RUSTFS_CONSOLE_CORS_ALLOWED_ORIGINS=*
# Object Cache
- RUSTFS_OBJECT_CACHE_ENABLE=true
- RUSTFS_OBJECT_CACHE_TTL_SECS=300
volumes:
- ./rustfs:/data/rustfs
restart: unless-stopped
healthcheck:
test: ["CMD", "sh", "-c", "curl -f http://localhost:9000/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
networks:
- mlflow-internal-net
上記の設定では,RustFS の WebUI をexample.com:15001とlocalhost:15001からアクセスできるようにしています.
RustFS への移行方法
RustFS へのデータの移行には,MinIO が開発しているmcコマンドを用いると便利です.
mcコマンドにはバケットのミラー(rsync)機能があるため,これを用いて簡単にデータを移行することができます.
- 移行元と移行先,両方の S3 互換ストレージへアクセスできる状態にします。
- MinIO Client(mc)のコンテナを
--net hostで起動します。
docker run --rm -it --net host --entrypoint sh minio/mc
- コンテナ内で、移行元と移行先の接続情報の設定を行います。
# 移行元
mc alias set src http://host.docker.internal:10000 <ACCESS_KEY> <SECRET_KEY>
# 移行先
mc alias set dst http://host.docker.internal:9000 <ACCESS_KEY> <SECRET_KEY>
mc mirrorコマンドでデータをコピーします。
mc mirror src/mlflow/artifacts dst/mlflow/artifacts