くらげになりたい。

くらげのようにふわふわ生きたい日曜プログラマなブログ。趣味の備忘録です。

Cloud Build+Cloud Runでできた不要なContainer Registryを自動削除する(gcr-cleaner)

前回の続き。Cloud Build+Cloud Runで作られた不要なイメージを削除してくれる
gcr-cleanerを試してみたときの備忘録。

github.com

とりあえず、gcloudをアップデート。

$ gcloud components update

設定の流れ

READMEに書いてある流れ。

  1. Google APIの有効化(GAE/Cloud Scheduler/Cloud Run)
  2. gcr-cleaner用のサービスアカウントの作成
  3. gcr-cleaner用のコンテナサービスをCloud Runにデプロイ
  4. サービスアカウントにCloud Storageの削除権限を付与
  5. デプロイしたCloud Runサービスを呼び出すサービスアカウントを作成
  6. Cloud SchedulerでHTTPジョブを作成

全体像としてはこんな感じで、
サービスアカウント2つと
Cloud Runのサービスが1つと
Schedulerのジョブを1つ作成する

Google APIの有効化

まずは利用するAPIたちを有効化する。
すでに有効化済みの場合は、スキップでOK

gcloud services enable --project "${PROJECT_ID}" \
  appengine.googleapis.com \
  cloudscheduler.googleapis.com \
  run.googleapis.com

gcr-cleaner用のサービスアカウントの作成

gcr-cleaner内で利用するサービスアカウントを作成

gcloud iam service-accounts create "gcr-cleaner" \
  --project "${PROJECT_ID}" \
  --display-name "gcr-cleaner"

gcr-cleaner用のコンテナサービスをCloud Runにデプロイ

用意されているgcr-cleanerのサービスをデプロイ。
サービス名や--region--timeoutは適宜変更する。

gcloud --quiet run deploy "gcr-cleaner" \
  --async \
  --project ${PROJECT_ID} \
  --platform "managed" \
  --service-account "gcr-cleaner@${PROJECT_ID}.iam.gserviceaccount.com" \
  --image "us-docker.pkg.dev/gcr-cleaner/gcr-cleaner/gcr-cleaner" \
  --region "us-central1" \
  --timeout "60s"

サービスアカウントにCloud Storageの削除権限を付与

デプロイしたCloud Runサービス内で、Cloud Storageの削除をするので、
サービスアカウントで書き込み権限を追加する。

gsutil acl ch -u gcr-cleaner@${PROJECT_ID}.iam.gserviceaccount.com:W \
  gs://asia.artifacts.${PROJECT_ID}.appspot.com

recursiveオプションを使う場合は、参照権限も必要。

gcloud projects add-iam-policy-binding "${PROJECT_ID}" \
  --member "serviceAccount:gcr-cleaner@${PROJECT_ID}.iam.gserviceaccount.com" \
  --role "roles/browser"

デプロイしたCloud Runサービスを呼び出すサービスアカウントを作成

まずはサービスアカウントを作成。

gcloud iam service-accounts create "gcr-cleaner-invoker" \
  --project "${PROJECT_ID}" \
  --display-name "gcr-cleaner-invoker"

次に、デプロイしたCloud Runサービスの「Cloud Run起動元」権限を付与。

gcloud run services add-iam-policy-binding "gcr-cleaner" \
  --project "${PROJECT_ID}" \
  --platform "managed" \
  --region "us-central1" \
  --member "serviceAccount:gcr-cleaner-invoker@${PROJECT_ID}.iam.gserviceaccount.com" \
  --role "roles/run.invoker"

Cloud SchedulerでHTTPジョブを作成

まずはGAEを作成。Firebaseを利用している場合はスキップでOK。

gcloud app create \
  --project "${PROJECT_ID}" \
  --region "us-central" \
  --quiet

次に、Cloud Schedulerを作成する。
--schedule--time-zoneは好きなように変更。
以下は、日本時間の毎週土曜の0時0分に実行する例

# 対象リポジトリのパス
export REPO="asia.gcr.io/${PROJECT_ID}/gcf"

# デプロイしたCloud RunのURLを取得:
export SERVICE_URL=$(gcloud run services describe gcr-cleaner --project "${PROJECT_ID}" --platform "managed" --region "us-central1" --format 'value(status.url)')

# Cloud Schedulerを作成
gcloud scheduler jobs create http "gcrclean-myimage" \
  --project ${PROJECT_ID} \
  --description "Cleanup ${REPO}" \
  --uri "${SERVICE_URL}/http" \
  --message-body "{\"repos\":[\"${REPO}\"]}" \
  --oidc-service-account-email "gcr-cleaner-invoker@${PROJECT_ID}.iam.gserviceaccount.com" \
  --schedule "0 0 * * 6" \
  --time-zone="Asia/Tokyo"

--message-body--scheduleなどは、
GCPコンソールからでも操作できるので、
変更などはそっちからのほうが便利。

あとはジョブを実行してみて、うまく削除されてたらOK(*´ω`*)

注意点

gcr-cleanerの仕組みとして、タグがないイメージを削除する形なので、
すでにタグをなにかつけていると削除されない。

その場合は、あらかじめタグを削除しておくか、
tag_filter_anyなどで削除対象を指定しておく必要がある。

合わせて、keepオプションで、残しておく数を指定することもできる。

具体的な処理は、pkg/gcrcleaner/cleaner.goshouldDelete ()あたり。

github.com

流れとしては、以下の感じ。

  1. 更新時間が所定の時間以下の場合は、false
  2. タグがない場合は、true
  3. tagFilterにマッチした場合は、true
  4. それ以外は、false

tag_filter_anyなどが未設定の場合は、
tagFilterにTagFilterNullが渡されるが、常にfalseを返すよう。

github.com

今までは、$COMMIT_SHAをタグにしてたけど、
leastを利用するように変更した。。

    - name: docker
      id: Push
      args:
        - push
-       - "$_GCR_HOSTNAME/$PROJECT_ID/$_SERVICE_NAME:$COMMIT_SHA"
+       - "$_GCR_HOSTNAME/$PROJECT_ID/$_SERVICE_NAME:latest"

以上!! これで毎週勝手に削除してくれるように(*´ω`*)