小規模チームのMLOps最小構成:Composerを入れず、Cloud Run jobsで4つの要件を満たす
はじめに
MLOpsの解説は、大きなチームと大きなトラフィックを前提にしたものが多く、feature storeもオーケストレータも監視基盤も最初から揃える構成になりがちです。それを数人のチームがそのまま入れると、動かすより維持するほうに手を取られ、半年後には誰も触らないまま放置されます。
小規模のMLOps設計で要になるのは、規模によらず保証すべき4点を最小のコストで満たし、常時課金と維持工数を避けることです。この記事は、その4点をクラウドに依存しない形で挙げ、実際に組んで動かした最小構成を示します。対象はバッチ推論です。オンライン推論、リアルタイム特徴量、LLMのサービングは対象から外します。これらは秒単位の応答や常時稼働が要件になり、設計の勘所がまるごと変わるためです。
対象読者:
- モデルは作ったが、運用に載せきれていないスタートアップのCTO・データサイエンティスト
- MLOpsを入れたいが、フル構成の維持コストに二の足を踏んでいる方
- 外注先の運用設計を評価したい事業側の方
記事のポイント:
- 規模によらず外せない4要件(データのバージョンの特定・再現性・出所・分布監視)を先に決め、そこから逆算します
- Cloud Composerのような常時課金サービスを入れず、Cloud Run jobs+Schedulerで同じ要件を満たす構成を、実際に動かして示します
- cron運用が壊れる場所(冪等性・リトライ・バックフィル)と、後から部品を足す・捨てる順番まで扱います
大きな構成をそのまま入れると、数人では回らない
典型的なフル構成には、feature store、ワークフローのオーケストレータ、モデルレジストリ、監視基盤、自動再学習が並びます。どれも大きな運用では効きますが、小規模で問題になるのは初期構築より維持のほうです。バージョン追従、権限設計の見直し、障害の切り分けが動かし続けるかぎり毎月発生し、チームが小さいほど1人あたりの負担が重くなります。
自動化するかどうかは、2つの量を見積もって比べれば決められます。自動化で浮く工数は、およそ「頻度 × 1回あたりの所要時間 × 期間」です。かかるコストは「構築 + 維持 × 期間」です。頻度が低ければ、前者はいつまでも後者に追いつきません。

図の高頻度の手作業(赤)は、7〜8か月で自動化のコストを追い越します。一方、低頻度の手作業(緑)は運用期間の終わりまで自動化と交わりません。この場合、手作業を意図的に残すのが正しい設計です。月に1回しか動かさない処理のために常時動くオーケストレータを入れるのは、維持工数を払い続けるだけになります。
以前、立ち上げ期のチームにオーケストレータとfeature storeを一式入れたことがあります。狙いは将来の拡張でしたが、実際にはDAGの定義とバージョン上げに毎月手が取られ、肝心のモデル更新は月1回どまりでした。半年たつ頃には誰もその基盤を触らなくなり、結局スケジューラ起動のジョブに戻しました。将来のために早めに入れた道具が、その将来が来る前に維持コストだけを積み上げます。
GCPの場合 マネージドサービスを使うと、自前で構築する手間は減り、その分が料金に乗ります。ただし維持の工数(権限設計、バージョン追従、障害の切り分け)は自分たちに残ります。さらに小規模では、課金形態が常時か実行ごとかで損益がほぼ決まります。Cloud Composerは待機中も環境が動き続けるため、タスクを1つも実行しなくても最小構成で月数万円規模かかります(2026年7月時点、継続稼働の小規模環境での概算)。対してCloud Run jobs+Cloud Schedulerは実行ごと課金で、月数回の学習なら月数百円未満のオーダーです。以降のGCP側の判断は、この桁差から決まります。(AWSなら常時課金のMWAA ≒ Composer、実行ごとのFargateタスク ≒ Cloud Run jobs)
規模によらず外せない四つ
入れるものを「何を自動化するか」で選ぶと、便利そうなものを足しすぎます。「何が保証されていないと障害になるか」で選ぶと、最小限に絞れます。規模によらず外せないのは次の4点です。
- データのバージョンが特定できる。 再学習で精度が落ちたとき、データが変わったのかコードが変わったのかを切り分けられること。日付分割パスに書き、読んだデータの内容ハッシュを記録します。
- 学習が再現できる。 本番のモデルを、同じ入力からもう一度作り直せること。コンテナ・seed・ライブラリ版を固定して記録します。
- 本番モデルの出所が追える。 いまどのモデルが動いているかを、誰かの記憶ではなく記録で断言できること。成果物に
metadata.jsonを同梱し、ポインタで現行を指します。 - 入力・出力の分布の変化に気づける。 静かな劣化を、半年後ではなくその週に検知できること。基準分布とのずれをPSI(Population Stability Index)で週次に見ます。
4番目の監視は、怠ったときの損失がいちばん大きくなります。あるモデルで、精度が少しずつ落ちているのに誰も気づかず、四半期のレビューで初めて発覚しました。原因は入力データの上流仕様の変更で、ある特徴量の分布がずれていました。モデルもコードも触っていないのに精度が落ちたため、切り分けに時間がかかりました。分布監視が1つでも入っていれば、ずれた週にアラートが鳴り、調査は数時間で済んだはずです。
GCPの場合 いずれもストレージとクエリの従量課金だけで組めて、数千件規模のデータなら月数百円のオーダーに収まります(2026年7月時点)。データのバージョンはGCSのオブジェクトバージョニングと日付分割パスで固定し、学習時に読んだオブジェクトの generation番号 を記録します(BigQuery起点ならタイムトラベルで断面を固定)。再現性は、コンテナをタグではなく digestで固定 して記録します。タグは動くので出所になりません。出所は成果物に
metadata.json(git hash、data generation、image digest、seed)を同梱、分布監視は基準統計をBigQueryに置いてスケジュールドクエリでPSIを計算し、Cloud Monitoringから通知します。 (AWSなら S3バージョニング、ECRのdigest、S3+メタデータ、Athena+EventBridge)
この規模では入れないもの(と、入れる条件)
ここで外すものが不要だという話ではありません。この規模ではコストが価値を超えるというだけで、条件が変われば入れます。それぞれに再導入のしきい値を添えておき、越えた時点で入れます。
- feature store:特徴量を複数チーム・複数モデルで共有するようになったら、あるいはオンライン推論で低遅延の特徴量提供が要るようになったら。
- 専用オーケストレータ:ステップが10を超え、依存の分岐が生まれ、部分再実行が日常になったら。
- 自動再学習:週次以上で再学習し、データ品質のゲートを自動化できてから。順序が逆だと、汚れたデータで自動的にモデルを悪くします。
- 常時稼働のオンライン推論基盤:秒単位の応答が要件になったら。バッチで足りるうちは要りません。
- 実験管理SaaS:試行が数百件に達し、手作業で目的の実験を探せなくなったら。
「それなら最初からフル構成で組んだほうが、あとで移行しなくて済むのでは」という反論はあります。ただ、最初にフルで固めると、合わなかった部品を抜く判断が難しくなります。部品をどの順で足し、自前実装をどう捨てるかは、「最初に壊れる箇所は、捨てやすく作ってあるか」の節で改めて扱います。
GCPの場合
- Cloud Composerは最初に入れません。 常時稼働で月数万円規模に加え、Airflowの追従という維持工数がかかります。代わりはCloud Run jobs+Cloud Schedulerです。
- Vertex AI Pipelinesも急ぎません。 実行ごと課金でComposerより小規模向きですが、KFPでパイプラインを書くコストが増えます。ステップ単位のキャッシュや部分再実行が欲しくなってからで十分です。
- Feature StoreとModel Monitoring は、BigQueryのテーブル関数とスケジュールドクエリで当面代替できます。モデルとエンドポイントが増えてから移します。 常時課金型(Composer、常時起動のエンドポイント、GKE)が、小規模で最初に見るべきコスト項目です。
実際に組んだ最小構成
構成はひとつながりです。データ取得から前処理、学習、成果物の保存、バッチ推論、分布監視までを1本の流れにし、1リポジトリ・1コンテナに収めてスケジューラから起動します。

クラウドに依存しない要点は3つです。成果物はタイムスタンプ付きのディレクトリに置き、current ポインタの書き換えで現行を切り替えます(ロールバックはポインタを戻すだけです)。実験ログは1試行1行で追記し、あとで検索・集計できる最小の形にしておきます。そしてジョブは冪等に作ります。
cron運用は冪等性・リトライ・バックフィルで壊れる
スケジューラ起動の運用で障害の原因になりやすいのは、冪等性・リトライ・バックフィルの3点です。
まず冪等性です。ジョブは「同じ日付の引数で再実行しても結果が同じ」になるように作ります。出力先を日付キーにして上書きすれば、二重起動や再送があっても結果は重複しません。この構成では推論の出力を pred_YYYYMMDD.npz のように日付で固定し、同じ日付なら同じ場所へ書き直します。リトライが安全になるのはこの前提があるからで、ネットワークや上流の遅延といった一時的な失敗は自動リトライで吸収し、それでも駄目なときだけ通知します。
バックフィルは、過去のある日を後から流し直す作業です。上流データの修正、監視の見落とし、初回の取り込み漏れで必ず必要になります。日付を引数で渡すだけで流し直せる形にしておけば、特別な作業になりません。実行日時が暗黙に「今日」へ固定されていると、過去を再実行するたびにコードへ手を入れることになり、その手直しが障害の入り口になります。
PSIの計算と実験ログのスキーマ
推論側は current.json が指すディレクトリからモデルを読むだけなので、切り替えもロールバックもこのポインタの書き換えで済みます。残る要点は監視とログの2つで、どちらも短く書けます。デプロイのコマンド類は付随リポジトリのREADMEに置きました。
分布監視は、学習時に固定した基準分布と当日の分布を突き合わせ、ビンごとの割合の乖離をPSIとしてまとめます。BigQueryのスケジュールドクエリで日次に回し、しきい値を超えた特徴量だけをアラートに送ります。
SELECT feature,
SUM((actual_ratio - expected_ratio)
* LN(actual_ratio / GREATEST(expected_ratio, 1e-4))) AS psi
FROM joined -- 基準分布 expected と当日分布 actual を結合済み
GROUP BY feature
HAVING psi > 0.25 -- 要対応のしきい値を超えた特徴量だけ通知
実験ログは1試行1行のJSONです。学習も推論も同じスキーマで追記するので、あとから「いつ・どのデータのバージョンで・どのモデルが動いたか」を1つのテーブルで追えます。
{"kind": "train", "run_date": "2026-01-05", "git_hash": "1e7cbed",
"data_version": "0717d6a344ef", "seed": 42,
"image_digest": "sha256:...", "artifact": "train_20260105", "n_train": 4000}
分布がずれたときの挙動を例で確かめる
典型的な例として、入力分布が途中でずれるデータでこの構成を動かしてみます。学習は週0の基準分布で行い、以降は毎週バッチ推論しながらPSIを見ます。

分布をずらす前の8週間、PSIは0.01を下回って動きませんでした。8週目にずらし始めると翌週から立ち上がり、10週目に要対応のしきい値(0.25)を越えてアラートが出ました。四半期のレビューを待たず、ずれた週のうちに気づけます。推論ジョブは日付キーで冪等に作ったので、同じ日を二度流しても出力は重複せず、モデルの切り替えとロールバックは current.json の1回の書き換えで済みました。
分布がずれると、モデルの精度だけでなく、判定に使う閾値の最適点も古くなります。その閾値を金額で引き直す考え方は精度改善の事業価値を見積もる記事で扱っていて、監視でずれを捕まえたら閾値を引き直す、という形で対にして回します。
費用は、この規模(数千件/日の推論、週次の学習)をCloud Run jobs+Cloud Schedulerで運用して月数百円未満のオーダーです(実行ごと課金の料金モデルから見積もり、2026年7月時点の東京リージョン想定)。
GCPの場合
- 学習・推論はCloud Run jobs。タスクタイムアウトはCPUなら最大7日まで延ばせます。ただしGPUを使う場合は1時間まで(2026年7月時点)。数千件規模の短時間ならCloud RunのGPUで足り、長時間のGPU学習が要るならVertex AI custom trainingです。GPUはVertex一択、と決め打たないほうがよいところです。
- 起動はCloud Scheduler → Cloud Run jobs。日付をペイロードで渡すのが冪等性とバックフィルの実装点です。
- 監視はスケジュールドクエリでPSIを計算し、しきい値超過をCloud Monitoringのアラートへ。通知先はSlack等のwebhookまで書きます。メール通知は埋もれて見られません。
- 権限はジョブごとに専用のサービスアカウントで最小権限に(学習SAはGCS書込+BQ読取、推論SAはBQ書込)。小規模でも最初にやります。障害が起きたときの影響範囲が、サービスアカウント単位で確定するからです。
- IaCはこの規模でもTerraformを入れます。リソースが少ないうちに始めるほうが、あとから足すより安く済みます。「この規模では入れないもの」の原則に対する、意図的な例外です。
最初に壊れる箇所は、捨てやすく作ってあるか
最小構成は、いつか手狭になります。大事なのは、そのときに部品を独立に足せて、古い自前実装を捨てられることです。経験上、限界が来る順番はだいたい決まっていて、最初に来るのは学習と推論で特徴量の作り方が食い違う問題です。以降はジョブ依存の複雑化、実験ログの検索性、学習規模、モデル数と続きます。
| 兆候 | 追加するもの | その時点で捨ててよい自前実装 |
|---|---|---|
| 学習・推論で特徴量がずれる | 特徴量の共通化、やがてfeature store | 各スクリプトに散った特徴量生成 |
| ジョブ依存の分岐・部分再実行 | ワークフローのオーケストレータ | Schedulerの直列起動 |
| 実験ログを検索できない | 実験管理ツール | runs テーブルへの追記 |
| 学習が1台に収まらない | 分散・専用の学習基盤 | 単一コンテナの学習 |
| モデル数の増加で追跡が破綻 | モデルレジストリ | current.json とメタデータ同梱 |
最小構成の値打ちは、安さそのものより、各部品を独立に捨てられることにあります。最初にフルスタックを固めてしまうと、合わなかった部品を抜くときに、絡んだ依存ごとほどく必要が出て、そこでかえって大きな代償を払うことになります。
GCPの場合 特徴量の乖離はBigQueryのテーブル関数で定義を一元化し、さらに進めばVertex AI Feature Storeへ移します。ジョブ依存の複雑化はVertex AI Pipelines(実行ごと課金を維持)で受け、SLAや外部連携が絡んだ段階でComposerに切り替えます。学習が1台に収まらなくなったらVertex AI custom training、モデル数が増えたらModel Registry+Model Monitoringへ移します。どの段階でも、移した先で不要になる自前実装をセットで捨てます。
まとめ
小規模のMLOpsで押さえるのは、保証すべき4点(データのバージョンの特定、再現性、出所、分布監視)を最小コストで満たすこと、常時課金と維持工数を避けること、各部品を独立に捨てられるように作ることです。常時課金のオーケストレータを入れる代わりにCloud Run jobs+Schedulerで組めば、費用は月数万円規模から月数百円未満のオーダーへ、桁で下がります。
もっとも、この構成が合わない場面もあります。秒単位の応答が要るオンライン推論、規制でマネージドな監査証跡が求められる領域、あるいはモデルとエンドポイントが数十を超える規模などです。ここまで来れば、常時課金を払ってでもマネージドに寄せる判断が正しくなります。自分の現場がまだその手前にいるなら、やることはひとつです。いま動いているモデルについて、データのバージョン・再現性・出所・分布監視の4点が記録で追えるかを点検し、追えない項目から順に、最小の手段で1つずつ埋めていくことです。