🦄

Cloud Runの不要なリビジョンを削除するGitHub Actions

2023/12/07に公開

@Colt45s です。
弊社のとあるプロダクトでは、 アプリケーションのデプロイ先に Cloud Run を選択しています。
大雑把な構成図はこちらの記事を参照してください。

https://zenn.dev/monicle/articles/81065e5f015bb4

GitHub で PR を作成した時に、Cloud Run にpr-PRの番号でタグ付けしてデプロイしています。

URL マスクを <tag>-<service>.ドメイン のように設定して NEG を作成し、 外部アプリケーションロードバランサーのバックエンドとしています。
Terraform でも設定できますので、弊社では Terraform を利用して設定しています。
https://cloud.google.com/load-balancing/docs/https/setting-up-https-serverless?hl=ja#create_a_serverless_neg_with_a_url_mask
https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_region_network_endpoint_group#url_mask

SSL 証明書は Certificate Manager でワイルドカード証明書を管理しています。
terraform-google-modules のサンプルが参考になったので紹介します。
https://github.com/terraform-google-modules/terraform-docs-samples/blob/7c85884f1a1f0cec0850b4dfda800c79cb3db675/certificate_manager/dns_managed_zone_with_wildcard_certificate/main.tf

このような構成にすることで <pr-PRの番号>-<Cloud Runのサービス名>.ドメイン でアクセスすると
外部アプリケーションロードバランサー -> NEG -> タグ付けした Cloud Run という経路でトラフィックが流れるようになります。この記事では プレビュー環境 という名称で呼ぶことにします。
このように実装が終わった PR のプレビュー環境を作り、プロダクトオーナーやステークホルダーに爆速でレビューしてもらう文化が弊社の創業期より続いています。

筆者はあまり使ったことがなく詳しくないのですが、利用したことがある読者は Vercel のプレビューデプロイメント をイメージしていただけると分かりやすいかもしれません。

また、弊社のとあるプロジェクトでは PR がマージやクローズされた場合にはプレビュー環境を GitHub Actions でクリーンアップしています。Cloud Run のみの話だと PR 用に作られたリビジョンを削除しています。
ですが、次の条件に一致するリビジョンは削除からプロテクトされています。

  • トラフィックを受信できるリビジョン
  • サービスの唯一のリビジョン
  • サービスの最新リビジョン

プロジェクトによって様々な事情はあると思いますが、弊社のとあるプロジェクトでは最小インスタンスを割り当てたプレビュー環境を用意したいことがあり、デプロイ時にブランチ名の prefix や suffix に特定の文字列が含まれていたら最小インスタンスを割り当ててデプロイする等の工夫を行っています。そのような事情で運用している際に削除からプロテクトされた後に不要なリビジョンが残り続けることは開発費用の増加に繋がるため、トイルを避けつつ不要になったリビジョンを削除したいといったモチベーションがありました。
そこで GitHub Actions の定期実行ワークフローを作成することで狙い通りの効果が得られましたのでサンプルを紹介したいと思います。

ちなみに、Cloud Run の請求については以前、同僚の @shimabukuromeg がまとめてくれた記事が分かりやすいのでそちらを見てください。
https://zenn.dev/monicle/articles/4263809c4c727d

main ブランチまたは Open な PR 以外の最小インスタンスが割り当てられた Cloud Run のリビジョンを削除するワークフローを毎週金曜の 19 時(JST)に実行するサンプルです。

name: Delete Billable Cloud Run Revisions

on:
  workflow_dispatch:
  schedule:
    - cron: "0 10 * * 5"

permissions:
  id-token: write
  contents: write
  pull-requests: write

env:
  DEPLOY_GCP_WIP: <Workload Identity Provider>
  DEPLOY_GCP_SA: <権限を借用するSA>

jobs:
  delete-billable-revisions:
    name: Delete Billable Revisions
    runs-on: ubuntu-latest
    steps:
      - name: Checktout
        uses: actions/checkout@v4
      - name: List Opened PR Number
        id: list_pr_number
        shell: bash
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          pr_numbers=$(gh pr list --state=open --json number --jq 'map(.number | tostring)')
          echo "$pr_numbers"
          echo "pr_numbers=$pr_numbers" >> "$GITHUB_OUTPUT"
      - name: Configure GCP
        uses: google-github-actions/auth@v1
        with:
          workload_identity_provider: ${{ env.DEPLOY_GCP_WIP }}
          service_account: ${{ env.DEPLOY_GCP_SA }}
      - name: Delete
        shell: bash
        env:
          PR_NUMBERS: ${{ steps.list_pr_number.outputs.pr_numbers }}
        run: |
          revisions=$(gcloud run revisions list --region asia-northeast1 --filter 'metadata.annotations."autoscaling.knative.dev/minScale"!=0' --format 'value(metadata.name)')
          while IFS= read -r pr_number; do
            revisions=$(echo "$revisions" | grep -vE "pr-$pr_number\$|main\$")
          done <<< "$(echo "$PR_NUMBERS" | jq -r '.[]')"
          {
            echo "## Delete revisions"
            echo "$revisions"
          } > "$GITHUB_STEP_SUMMARY"
          echo "$revisions" | xargs -I_ gcloud run revisions delete _ --quiet --async --region asia-northeast1

gcloud run revisions listfilter オプションをカスタマイズしていただくとプロジェクトの使い方にマッチした条件で削除できると思います。
GitHub Actions と Google Cloud のキーレスの認証はこの記事が詳しいです。
https://cloud.google.com/blog/ja/products/identity-security/enabling-keyless-authentication-from-github-actions

以上です。
応用して使っていただけると @Colt45s が飛び跳ねて喜びます。


次は弊社 SRE チームの @beaverjr です!
よろしくお願いします!

株式会社モニクル

Discussion