Mímisbrunnr知恵の泉

← MLOps 一覧

🎓 レベル:標準 | 重要度:B(推奨)

📎 前提:オンライン推論サービング | 関連:クラウド・インフラ/SRE・DevOps 全体目次(クラウド)

要点(BLUF)

1. なぜコンテナに固めるのか

再現性とバージョニングで見たとおり、モデルは環境(依存ライブラリ・Python版・OS)の関数でもあります。requirements.txt を配るだけでは、OS差・推移的依存・システムライブラリの違いで「自分の環境では動く」が起きます。コンテナはアプリと環境を丸ごと1つのイメージに封入し、この差を消します。

2. 1イメージにまとめる3点

flowchart TB
  subgraph コンテナイメージ
    A["モデルアーティファクト(変換器+モデル)"]
    B["依存の固定(ロックファイル)"]
    C["サービングコード(FastAPI等)"]
  end
  R["モデルレジストリ"] -.->|"Production版を焼き込み or 起動時取得"| A
  コンテナイメージ -->|"タグ付けして"| REG["イメージレジストリ"]
  REG --> K["どの環境でも同じに起動"]
要素何を固めるか
モデルアーティファクト学習済み Pipeline(前処理込み)。スキュー防止
依存バージョンを完全固定(推移的依存も)
サービングコードAPI・ヘルスチェック・入力検証

3. 動く最小例:サービングを固める Dockerfile

オンライン推論サービングserve.py をコンテナ化する最小 Dockerfile です。

# slim ベースで軽量に。バージョンを固定して再現性を確保
FROM python:3.11-slim

WORKDIR /app

# 依存を先にコピーしてレイヤキャッシュを効かせる
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# サービングコードとモデルアーティファクトを焼き込む
COPY serve.py model.joblib model_meta.json ./

# ヘルスチェック(オーケストレータが生死を判定)
HEALTHCHECK --interval=30s --timeout=3s \
  CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8000/health')"

EXPOSE 8000
CMD ["uvicorn", "serve:app", "--host", "0.0.0.0", "--port", "8000"]

依存は推移的依存まで固定したロックを使います:

# requirements.txt(バージョンを固定する)
fastapi==0.136.3
uvicorn==0.30.0
scikit-learn==1.5.0
joblib==1.4.0
numpy==2.0.0

ビルドとタグ付け、起動:

# モデル版に対応するタグでビルド(例:churn モデルの v3)
docker build -t churn-serving:v3 .

# 起動して疎通確認
docker run -d -p 8000:8000 --name churn churn-serving:v3
curl localhost:8000/health

この構成の意味python:3.11-slim で OS と Python を固定し、ロックした依存をインストールし、serve.py とモデルを焼き込む——これでイメージ churn-serving:v3 はどのマシンでも同一に起動します。タグ v3 をモデルレジストリの版(モデルレジストリとモデルのライフサイクル)と対応づければ、「どのモデルがどのイメージで本番にいるか」が一意に追えます。HEALTHCHECK でオーケストレータが生死を判定し、ロールバックは「前のタグに戻す」だけです。

4. 運用の勘所

なぜそうするのか

コンテナ化する本質的な理由は、**成果物を「環境ごと不変にする」**ことです。モデルは環境の関数なので、環境が変われば挙動が変わりえます。イメージに環境を封入してタグで固定すれば、開発・検証・本番で同一物が動き、「どこかで動かない」「いつの間にか挙動が変わった」を構造的に防げます。タグでの版管理は、障害時に「前のイメージに戻す」だけで即ロールバックできる安全性も生みます。

⚠️ よくある落とし穴

対応 lab

関連ノート