第130回 実務で使えるモデルとデータのバージョン管理 — Pythonで作る軽量アーティファクト管理とトレーサビリティの手順

はじめに — こんなつまずきはありませんか?

モデルや学習データが増えると、「いつ」「誰が」「どのコード/どの前処理で」作ったか分からなくなりがちです。現場ではファイル名だけで管理して上書きされたり、マニフェストが無いままオブジェクトだけが残って再現できなくなることがよくあります。ここでは、実務で使える具体的な手順と、すぐ試せるPythonスクリプト例を示します。

この記事の狙い

抽象論ではなく、現場で「再現」「差し戻し」「プロモーション(staging→prod)」ができるレベルの運用手順を示します。第129回で触れたスケジューリング/オーケストレーションの次の一手として、ワークフローで生成される成果物の管理方法を扱います。

概要

基本方針はシンプルです。

  • 成果物(モデル、前処理スクリプト、データダンプ)はオブジェクトとして保存する
  • それらを指す「マニフェスト(JSON)」を作成し、メタデータ/チェックサム/作成元情報を残す
  • マニフェストは検索可能なストレージか、オブジェクトと同じ場所に置く
  • プロモーションやロールバックはマニフェスト単位で扱う(ファイル名だけに依存しない)

アーティファクト命名規則とストレージレイアウト

まずは運用が続くように簡潔な命名とレイアウトを決めます。現場で守りやすいことが最優先です。

項目 推奨例 備考
アーティファクトID projectA-model-v20260814-001 一意となるシーケンスを含める
ストレージパス artifacts/projectA/model/YYYY/MM/DD/
artifact_id/
マニフェストとアセットを同ディレクトリに置く
マニフェスト名 manifest.json 常に同じ名前にして読み取りを簡単に

マニフェスト(JSON)の必須フィールド

マニフェストは必須フィールドを決めておきます。まずはこれだけあれば実務で再現できます。

フィールド 説明
artifact_id 一意の識別子 projectA-model-v20260814-001
created_at 作成日時(ISO8601) 2026-08-14T10:23:00Z
created_by 作成者(ユーザ名/CI名) ci/pipeline-42
objects 関連ファイルとチェックサム一覧 [{“path”:”model.tar.gz”,”sha256″:”…”}]
git コード起点のコミット情報 {“commit”:”abc123″,”branch”:”main”,”remote”:”git@…”}
env 実行環境情報(Pythonなど) {“python”:”3.10.6″,”packages”:”requirements.txtハッシュ”}
notes 補足(前処理のパラメータ等) scaler:standard, seed:42
promotion 状態(staging/prod)と履歴 [{“to”:”staging”,”at”:”…”,”by”:”…”}]

やること(手順ベース)とPythonの最低限サンプル

各要点に対して「やること」と「Pythonサンプル」を示します。コードはそのまま貼って実行できるように簡潔にしています。

1) アセットをパッケージ化(zip/tar)してチェックサムを取得

やること: モデルファイルや前処理スクリプトを1つのアーカイブにまとめ、SHA256を計算する。

from pathlib import Path
import hashlib
import tarfile

def make_tar(src_paths, dest_path):
    with tarfile.open(dest_path, "w:gz") as tf:
        for p in src_paths:
            tf.add(p, arcname=Path(p).name)

def sha256_of_file(path):
    h = hashlib.sha256()
    with open(path, "rb") as f:
        for chunk in iter(lambda: f.read(8192), b""):
            h.update(chunk)
    return h.hexdigest()

# 使い方
# make_tar(["model.pkl","preprocess.py"], "artifact.tar.gz")
# print(sha256_of_file("artifact.tar.gz"))

2) Git情報を取得してマニフェストに含める

やること: 実行時のコミットハッシュやブランチをマニフェストに残す。

import subprocess

def git_info():
    try:
        commit = subprocess.check_output(["git","rev-parse","HEAD"]).decode().strip()
        branch = subprocess.check_output(["git","rev-parse","--abbrev-ref","HEAD"]).decode().strip()
        return {"commit":commit, "branch":branch}
    except Exception:
        return {"commit":None, "branch":None}

3) マニフェストを作成して保存(JSON)

やること: 必須フィールドを埋めてmanifest.jsonとして保存する。

import json
from datetime import datetime

def create_manifest(artifact_id, objects, created_by, env, notes=None):
    manifest = {
        "artifact_id": artifact_id,
        "created_at": datetime.utcnow().isoformat() + "Z",
        "created_by": created_by,
        "objects": objects,
        "git": git_info(),
        "env": env,
        "notes": notes or "",
        "promotion": []
    }
    return manifest

# 保存例
# manifest = create_manifest("id-123", [{"path":"artifact.tar.gz","sha256":"..."}], "ci/pipeline", {"python":"3.10"})
# with open("manifest.json","w") as f:
#     json.dump(manifest, f, indent=2)

4) マニフェストを読み取って復元するスクリプト

やること: マニフェストを検証し、チェックサムが一致するか確認してから展開する。

import json
import tarfile

def verify_and_extract(manifest_path, artifact_dir):
    with open(manifest_path) as f:
        m = json.load(f)
    for obj in m["objects"]:
        path = obj["path"]
        expected = obj.get("sha256")
        if expected and sha256_of_file(path) != expected:
            raise ValueError("checksum mismatch for " + path)
    # 展開例(最初のオブジェクトを展開)
    tar = m["objects"][0]["path"]
    with tarfile.open(tar) as tf:
        tf.extractall(artifact_dir)

マニフェストの実例

{
  "artifact_id": "projectA-model-v20260814-001",
  "created_at": "2026-08-14T10:23:00Z",
  "created_by": "ci/pipeline-42",
  "objects": [
    {"path": "artifact.tar.gz", "sha256": "012345..."}
  ],
  "git": {"commit": "abc123def", "branch": "main"},
  "env": {"python": "3.10.6", "packages_hash": "..."},
  "notes": "preproc: scale=standard, seed=42",
  "promotion": []
}

ストレージ対応と現実的な選択肢

まずは「マニフェスト+オブジェクト保存」で十分なケースが多いです。下表は簡単な比較です。

選択肢 長所 短所 / 向き不向き
ローカル/NFS 導入が簡単、低コスト 可用性・スケールは限定的。複数拠点では同期が課題
オブジェクトストレージ(S3互換) 耐久性・スケール性に優れる。署名URL等運用が楽 小さいファイルが多い場合は効率が悪い。アクセス制御設計が必要
DVC的ワークフロー データ差分管理が可能。Git連携で履歴がとれる 導入コストと学習コストがある。まずはマニフェスト方式で開始が現実的

運用面のチェックリスト

まず守るべき運用ルールを短く示します。

項目 運用ルール(例)
マニフェスト必須フィールド artifact_id, created_at, created_by, objects, git, env
保存ポリシー 90日でstagingを削除、prodは365日保持(要業務設計)
アクセス権限 書き込みはCIのみ、手動プロモーションは管理者承認
容量・保持 定期的に容量報告を行い、古いアーティファクトをアーカイブ
監査ログ マニフェスト更新・プロモーションは履歴を残す
失敗時の自動クリーンアップ 冪等性を考え、登録に失敗したオブジェクトはTTLで自動削除

CI/スケジュール連携の実務例

代表的な流れと注意点を簡潔に示します。

段階 処理 注意点
1 Gitコミット→CI起動 コミットハッシュを確実にマニフェストに含める
2 CIでアーティファクト生成(テスト含む) 生成は一時領域で行い、成功時のみ登録
3 ストレージ登録+マニフェスト作成 整合性チェック(チェックサム)を必須にする
4 Orchestrator(cron/Prefect)でプロモーション プロモーションはマニフェストの状態遷移で管理(ロック注意)

CIスニペット(概念)

# (1) アーカイブ作成
python -m scripts.package_artifact --src model.pkl --out /tmp/artifact.tar.gz
# (2) チェックサム生成 + manifest作成
python -m scripts.create_manifest --artifact /tmp/artifact.tar.gz --out /tmp/manifest.json
# (3) アップロード
aws s3 cp /tmp/artifact.tar.gz s3://mybucket/artifacts/.../
aws s3 cp /tmp/manifest.json s3://mybucket/artifacts/.../

注意点: アップロードが複数に分かれる場合は、全て成功してからmanifestを”登録済み”にするフラグを更新するなどの整合性確保が必要です。

よくある失敗例と回避策

  • ファイル名だけで管理して上書きされる —> 一意IDとチェックサムを必須にする
  • マニフェストとオブジェクトが不整合 —> アップロード後に整合性チェックを実行、整合性が取れなければロールバック
  • 環境情報を残さず再現不可 —> Pythonバージョン、requirementsハッシュ、主要ライブラリバージョンは必ず保存
  • ルールが厳しすぎて守られない —> 最初はシンプルにしてCIで自動化して運用負荷を下げる

ハンズオン(最小限の流れ) — 次に実行するコマンド

この手順はローカル環境で素早く試せます。リポジトリに以下のスクリプトを置いている想定です(上記サンプルをscriptsにまとめる)。

  1. アセットをアーカイブする
    python -c "from pathlib import Path; import tarfile; tf=tarfile.open('artifact.tar.gz','w:gz'); tf.add('model.pkl'); tf.add('preprocess.py'); tf.close()"
  2. チェックサムとマニフェストを作る
    python -c "import hashlib,json; h=hashlib.sha256(); open('artifact.tar.gz','rb').read(); print('sha')"

    ※ 上のコマンドは例です。実運用ではscriptsを使ってください。

  3. マニフェストを確認して展開する
    python -c "import json; print(open('manifest.json').read())"
    python -c "# verify_and_extract関数を呼ぶコードを実行"

まとめ

現場で実際に続く運用にするためには、まずシンプルなルールと自動化(CI)を作ることが大切です。今回示した「アーカイブ化→チェックサム→マニフェスト作成→ストレージ登録→プロモーション」の流れは、DVCやフルマネージド製品を導入する前の現実的な第一歩です。まずは小さなサンプルで試し、問題点を洗い出してから拡張してください。

シリーズ: AIとPythonの実務 — 第129回のワークフロー回りの次の一手として、定期実行・リトレーニングで生成される成果物を安定して管理する運用設計の参考にしてください。