Artifact Registry が86GBに膨らんだ(「30日より古いものを削除」が効かない理由とクリーンアップポリシー)
- GCP
- Cloud Run
- Artifact Registry
- コスト削減
GCP の Artifact Registry に クリーンアップポリシーを入れたのに、2週間後には容量が元に戻っていました。
ポリシーの「30日より古いイメージを削除する」という条件が、ビルドの頻度に対して長すぎたのが原因です。保持件数を基準にしたポリシーに見直したところ、ようやく容量が落ち着きました。
前提:画像生成 Web アプリ Moeter の GPU ワーカーを Cloud Run(GPU)で動かしています。イメージは Cloud Build でビルドし、Artifact Registry に置いています。
最初の状態
9月半ばに確認したとき、この GCP プロジェクトのストレージは次のようになっていました。
- Artifact Registry:82GB
- Cloud Storage:78GB
Artifact Registry の保存料金は、無料枠(0.5GB)を超えた分が1GBあたり月 $0.10 です。82GB なら月 $8 ほどになります。個人開発としては無視できない額です。
そこで、Artifact Registry にクリーンアップポリシー、Cloud Storage にライフサイクルルールを設定しました。「最新5件は残し、30日(バケットによっては7日)より古いものは削除する」という内容です。これで約58GB減りました。
2週間後、86.7GB に戻っていた
月末に請求を見直したところ、Artifact Registry は 86.7GB まで膨らんでいました。最初より増えています。
原因は GPU ワーカーのイメージでした。
- イメージ1個が11.5GB:画像生成モデル(6.5GB + 1.5GB)をイメージに直接焼き込んでいる
- 月に8回くらいビルドする:機能追加や調整のたびに再ビルドしてデプロイしている
30日分を残すと、11.5GB × 8回 ≒ 92GB が常に残ります。「30日より古いものを削除」という条件では、削除対象がほとんど出てこない のです。
古いイメージを消すポリシーを入れていても、ビルドの頻度とイメージのサイズによっては何も消えません。期間で区切るなら、「その期間に何回ビルドするか × 1個のサイズ」を見積もる必要がありました。
保持件数を基準にしたポリシーに変える
ポリシーを「最新2件だけ残し、7日より古いものは削除する」に変えました。
[
{
"name": "keep-latest-2",
"action": { "type": "Keep" },
"mostRecentVersions": { "keepCount": 2 }
},
{
"name": "delete-older-than-7d",
"action": { "type": "Delete" },
"condition": { "tagState": "any", "olderThan": "7d" }
}
]
gcloud artifacts repositories set-cleanup-policies <リポジトリ名> --project=<プロジェクトID> --location=us-central1 --policy=policy.json --no-dry-run
Keep と Delete の両方に当てはまるバージョンは、Keep が優先 されます。最新2件は7日を過ぎても消えないので、稼働中のイメージと、1つ前に戻すためのイメージは残ります。
なお、ポリシーは設定してすぐに実行されるわけではありません。バックグラウンドで定期的に評価されるので、容量が減るまで1日ほどかかります。急ぐ場合は、古いバージョンを手動で消します。
keepCount を小さくしすぎると稼働中のイメージが消える
別のアプリ(こちらは普通の Web アプリ)にも同じポリシーを入れようとして、気づいたことがあります。
そのリポジトリでは、1回のデプロイでタグなしのバージョンが2件、一緒に作られていました。keepCount: 2 にすると、最新のデプロイで作られた付属物だけで2件の枠が埋まり、肝心の稼働中のイメージが削除対象になってしまいます。
こちらは「最新10件を残し、14日より古いものは削除」にしました。keepCount を決める前に、一度リポジトリの中身を一覧して、1回のデプロイで何件のバージョンが増えるかを確認しておくと安全です。
Cloud Build のソースバケットも膨らむ
Artifact Registry と一緒に、Cloud Storage の方も見直しました。
gcloud builds submit や gcloud run deploy --source を使うと、ビルド用のソースが tar にまとめられて、Cloud Storage のバケット(<プロジェクトID>_cloudbuild や run-sources-...)にアップロードされます。このプロジェクトでは 1回のビルドあたり7.3GB ありました。
ビルドが終われば不要なものなので、この2つのバケットはライフサイクルルールで 1日経ったら削除 にしました。
もう1つ注意したいのが ソフトデリート です。Cloud Storage のバケットは、デフォルトで削除したオブジェクトを7日間保持し、その間も保存料金がかかります。1日で消しても、ソフトデリートが有効だと結局7日分の料金がかかります。ビルド用のバケットでは復元する必要もないので、無効にしました。
gcloud storage buckets update gs://<バケット名> --clear-soft-delete
ついでに:使っていない Cloud Run サービス
調べている途中で、実験用に作った GPU の Cloud Run サービスが残っているのを見つけました。イメージも4.7GB 残っていました。
GPU の Cloud Run は、リクエストを処理していない待機時間も含めて、インスタンスが起動している間ずっと課金されます。この実験サービスでは、3日間で合計11時間ほど起動していて、それだけで約 $15 かかっていました。サービスごと削除しました。
まだやっていないこと
根本的な対策は、モデルをイメージに焼き込まず、Cloud Storage に置いて起動時に読み込む構成にすることです。イメージから8GB分が抜けるので、何回ビルドしても容量はあまり増えなくなります。
ただ、起動のたびに8GBのモデルを読み込むことになるので、コールドスタートが遅くなります。利用者の待ち時間に直結するので、今のところは保持件数を絞る対策にとどめています。
まとめ
- 「N日より古いものを削除」は、ビルド頻度 × イメージサイズ によっては何も消さない。大きなイメージを頻繁にビルドするなら、保持件数(
keepCount)を基準にする。 keepCountを決める前に、1回のデプロイで何件のバージョンが増えるか を確認する。- Cloud Build のソースバケットは短期で削除し、ソフトデリートも無効 にする。
GCP の費用見直し全体の流れは、Claude Code に GCP の課金を棚卸しさせたら、月1万円が半分になった にまとめています。
この記事をシェア