くりにっき

フルスタックキュアエンジニアです

Cloud Functionsのデプロイ時に使われるArtifact Registryで古いタグを削除する

前提

Google Cloudの Cloud Functions の第2世代ではデプロイする時には自動的に Artifact Registry にDockerイメージが作られます。

cloud.google.com

デプロイ時に自動的にDockerイメージを作ってくれるので利用者側では全然気にすることはないのですが、作られたイメージは消えないので頻繁にデプロイしているとDockerイメージが増えてArtifact Registryのストレージの費用が増えて気になっていました。

具体的にどれくらい費用かかっていたのかというと、週1回デプロイを1年間続けたらArtifact Registryだけで月1ドル前後かかるようになっていました。(Cloud Functions部分は無料枠におさまっているので0円)

業務だと全然無視していい費用感なんだけど個人プロダクトだと削りたかったので自動的に消す方法を仕込みました。

基本的にはTerraformでやる前提なので他の方法でやる場合には適宜読み替えてください。

手順1. Cloud Functionsが作ったArtifact RegistryのリポジトリをTerraformにimportする

TerraformでArtifact Registryの設定変更をするためには最初にimportする必要があります。

まずは適当なtfファイルに下記を書きます。

import {
  id = "asia-northeast1/gcf-artifacts" # TODO: 東京リージョン以外を使ってる場合は要修正
  to = google_artifact_registry_repository.gcf-artifacts
}

その後 terraform plan -generate-config-out=_generated.tf のようにコマンドを実行すればimport結果が _generated.tf に出力されます。

_generated.tf の中身はこんな感じ。

# __generated__ by Terraform
# Please review these resources and move them into your main configuration files.

# __generated__ by Terraform from "asia-northeast1/gcf-artifacts"
resource "google_artifact_registry_repository" "gcf-artifacts" {
  cleanup_policy_dry_run = true
  description            = "This repository is created and used by Cloud Functions for storing function docker images."
  format                 = "DOCKER"
  kms_key_name           = null
  labels                 = {}
  location               = "asia-northeast1"
  mode                   = "STANDARD_REPOSITORY"
  project                = "XXXXXXXXXXXXXXX"
  repository_id          = "gcf-artifacts"
}

生成されたリソースを別のtfファイルに移動させれば_generated.tf は削除していいです。

手順2. Artifact RegistryにCleanup Policyを適用する

Cloud FunctionsでしばらくデプロイしているとArtifact Registryのリポジトリ

  • latestタグ
  • UUID形式のタグ
  • タグ無し

のような構成になります。

そのためlatestタグだけ残して他は全部消すような Cleanup Policy のルールを適用します。

Terraformだと下記のようになります。

locals {
  # "0" 〜 "f" の文字列を生成する
  hex_chars = [for i in range(0, 16) : format("%x", i)]
}

resource "google_artifact_registry_repository" "gcf-artifacts" {
  location      = "asia-northeast1"
  repository_id = "gcf-artifacts"
  description   = "This repository is created and used by Cloud Functions for storing function docker images."
  format        = "DOCKER"
  mode          = "STANDARD_REPOSITORY"

  cleanup_policy_dry_run = false

  # latest tagは常に残す
  cleanup_policies {
    id     = "keep-latest"
    action = "KEEP"

    condition {
      tag_state    = "TAGGED"
      tag_prefixes = ["latest"]
    }
  }

  # tag無しは全て削除する
  cleanup_policies {
    id     = "delete-all-untagged"
    action = "DELETE"

    condition {
      tag_state = "UNTAGGED"
    }
  }

  # UUID形式のタグを全て削除する
  cleanup_policies {
    id     = "delete-hex-prefix-tags"
    action = "DELETE"

    # FIXME: Cleanup Policyでは正規表現が指定できないので0〜fで始まるものは全てUUIDとみなす(残したいlatestはマッチしないので一応問題ない)
    condition {
      tag_state    = "TAGGED"
      tag_prefixes = local.hex_chars
    }
  }
}

コメントにも書いてるけどCleanup Policyでは正規表現が使えないのでUUID形式のタグを消す部分はかなりの力技です...(UUIDで取りうるアルファベットはa〜fなのでlatestが消えずに済んでいる)

Terraform読めない人向けに一応 gcloud artifacts repositories list-cleanup-policies コマンドの結果も載せておきます。

$ gcloud artifacts repositories list-cleanup-policies gcf-artifacts --location asia-northeast1
Listing items under project XXXXXXXXXXXXX, location asia-northeast1, repository gcf-artifacts.

Dry run is disabled.
[
  {
    "action": {
      "type": "DELETE"
    },
    "condition": {
      "tagState": "UNTAGGED"
    },
    "name": "delete-all-untagged"
  },
  {
    "action": {
      "type": "DELETE"
    },
    "condition": {
      "tagPrefixes": [
        "0",
        "1",
        "2",
        "3",
        "4",
        "5",
        "6",
        "7",
        "8",
        "9",
        "a",
        "b",
        "c",
        "d",
        "e",
        "f"
      ],
      "tagState": "TAGGED"
    },
    "name": "delete-hex-prefix-tags"
  },
  {
    "action": {
      "type": "KEEP"
    },
    "condition": {
      "tagPrefixes": [
        "latest"
      ],
      "tagState": "TAGGED"
    },
    "name": "keep-latest"
  }
]

他のソリューション

Artifact RegistryにCleanup Policyが導入されるまでは https://github.com/GoogleCloudPlatform/gcr-cleaner を使っていましたが、こっちはもうメンテされていないので基本的にはCleanup Policyを使うのがいいと思います。

とはいえgcr-cleanerは正規表現でタグ指定ができるのが便利なのでどうしても正規表現を使いたい場合にはこっちを使うことになりそう。(早くCleanup Policyでも正規表現対応されてほしい...)