#MLインフラ#デプロイ#git#DevOps

重いMLアセット(data/features)を別サーバへ配る運用 — tar + scp + MD5検証


音声変換アプリを別サーバへ持っていくとき、こんな悩みに突き当たります。

事前計算した特徴量やアンカー埋め込み——数百MBのバイナリを、git には入れたくない。でもサーバ側では確実にこれが要る。

コードは git で配れますが、data/features/ のような重い生成物を git に載せるのは筋が悪い。リポジトリが肥大化し、clone は遅くなり、履歴は取り返しがつかなくなります。かといって手でコピーすると「どのファイルを、壊れずに、全部運べたか」が曖昧になる。この記事は、そうした重いアセットを tar でまとめて scp で配り、MD5 で完全性を検証するという素朴だが確実な運用と、その前提になる .gitignore の設計をまとめたものです。

なお、この記事のホスト名やパスはすべて例示です。実際の配布先や鍵の情報は含みません。

まず前提:何を git に入れ、何を入れないか

原則はシンプルです。

  • git に入れる:コード、設定、少量の公開アセット(フロントで配信する試聴音声など)
  • git に入れない:事前計算した重い生成物(data/features/output/)、モデルの重み(checkpoints/)、外部リポジトリ(external/)、仮想環境

問題は、「重いバイナリ(*.wav*.npz)は全部除外したい。でも、フロントで配信する試聴音声の .wav だけはリポジトリに含めたい」という、除外と例外が混ざるケースです。

.gitignore 設計:広く除外し、必要なものだけ !除外解除する

.gitignore は「まず広く除外 → 必要なものだけ ! で除外解除」という順序で書くのがコツです。実際の設定はこうなっています。

# プロジェクト固有(生成物・データ)
data/
features/
output/
*.wav
*.npz

# ただし仕様書は無視しない。
!memo.md
# フロントで配信するペルソナ試聴音声はリポジトリに含める(*.wav 除外の例外)
!web/public/personas/
!web/public/personas/*.wav
external/

# 追加: ニューラルVC関連の重い生成物
.venv-seedvc/
.venv-vc/
checkpoints/

設計上のポイントがいくつかあります。

  • *.wav を全体で除外したうえで、公開ディレクトリだけ ! で復活させる!web/public/personas/ でディレクトリを除外解除し、続けて !web/public/personas/*.wav で中の wav も解除する——親ディレクトリを解除してから中のファイルを解除するという二段が必要です。ディレクトリが除外されたままだと、中のファイルをいくら ! しても復活しません。
  • 除外は用途で分類してコメントを添える。「生成物」「重いバイナリ」「仮想環境」「モデル重み」と塊で書くと、後から「これは何で無視してるんだっけ」が分かります。
  • ビルド成果物の除外はアンカー(/)で範囲を絞る。たとえば lib/ をそのまま書くとフロントの web/src/lib まで巻き込むので、リポジトリ直下だけを対象にする /lib/ にする、といった配慮も入っています。

この設計で、「重い生成物は git に載らない/公開に必要な最小限だけ載る」という状態を作ります。git に載せないと決めたものを、次のセクションの別ルートで運びます。

配布:tar でまとめて scp、MD5 で検証

git に入れないと決めた重いアセットは、tar でひとまとめにして scp で配ります。ここで完全性の検証を必ず挟むのが要点です。ネットワーク越しのコピーは、途中で切れても「一応ファイルはある」状態になり得るからです。

1. まとめる

配布対象のディレクトリを1つの tar.gz に固めます。

# 配布したい重いアセットだけをまとめる
tar czf ml-assets.tar.gz data/ features/

2. チェックサムを取る

送る前に、アーカイブの MD5 を控えておきます(macOS は md5、Linux は md5sum)。

md5 ml-assets.tar.gz          # macOS
# md5sum ml-assets.tar.gz     # Linux

3. 送る

scp で配布先へ転送します(ホスト名・パスは例示)。

scp ml-assets.tar.gz user@example-host:/srv/app/

4. 届いた先で検証してから展開する

展開する前に、受信側で同じ MD5 を計算して送信側と一致するか確認します。ここが肝で、一致を確認できて初めて「壊れずに全部届いた」と言えます。

# 配布先で
md5sum ml-assets.tar.gz    # 送信側の値と突き合わせる
# 一致したら展開
tar xzf ml-assets.tar.gz -C /srv/app/

MD5 が食い違えば、それは転送が途中で壊れたサイン。展開せずに送り直します。「なんとなくコピーできたっぽい」で先に進まないための、安いが効く保険です。

落とし穴・学び

  • ! の除外解除は親から順に*.wav のような広い除外の下で特定ファイルを復活させたいときは、まずそれを含むディレクトリを ! で解除しないと、中のファイル指定は効きません。
  • 重い生成物は git ではなく別ルートで運ぶと割り切る。git は差分管理が得意なコードのためのもので、再生成可能な巨大バイナリを載せる場所ではありません。tar+scp は素朴ですが、依存が少なく、どの環境でも同じ手順で動きます。
  • 転送には必ずチェックサムを添える。サイズが大きいほど、途中で壊れても気づきにくくなります。展開前に MD5 で突き合わせる一手間が、原因不明の実行時エラーを未然に防ぎます。
  • 「何を配らないか」を .gitignore で明文化しておくと、配布の対象(=git に載っていない重いもの)が自然に定まります。除外設計と配布運用は表裏一体です。

まとめ

  • 重いMLアセット(data/features/checkpoints/ など)は git に入れず、tar + scp の別ルートで配る
  • .gitignore は「広く除外 → 必要なものだけ ! で除外解除」の順で書く。ディレクトリを解除してから中のファイルを解除する二段が要る
  • *.wav を全体除外しつつ、公開する試聴音声だけを !web/public/personas/*.wav で復活させる、という例外設計ができる
  • 配布は tar でまとめ → MD5 を控え → scp → 受信側で展開前に MD5 照合。一致して初めて完全と判断する
  • 除外設計(何を配らないか)と配布運用(何を別で配るか)はセットで考える
← 記事一覧へ