emahiro/b.log

Drastically Repeat Yourself !!!!

GitHub Actions で CloudRun にデプロイする

Overview

CloudRun で開発したアプリケーションを GitHub Actions 経由で CloudRun にデプロイします。
202307時点の公式のドキュメントの記載だと不十分な部分があったので、現時点でのGitHub Actions 経由でのデプロイ方法を記載します。

準備

以下の API を有効化しておいてください。

  • IAM Service Account
  • Cloud Build
  • Artifact Registry

Cloud Run のデプロイ方法

GitHub Actions を経由する前に Cloud Run にはいくつかのデプロイ方法があります。これは以下の公式ドキュメントに記載してあります。

cloud.google.com

Cloud Run にアプリケーションをデプロイする方法は以下の3つがあり、どれを採用しても大丈夫です。

  1. Docker Image をデプロイする
  2. GitHub と連携する
  3. ソースコードからデプロイする
    • ソースコードからデプロイするときにデプロイするソースのディレクトリと同じ階層(ex. Go の場合は go.mod と同じ階層)に Dockerfile がある場合、その Dockerfile を元に Image をビルドするようになります。そのため、デプロイコマンド (gcloud run delpy) 実行時に image option を指定する必要はありません。

今回自分は 3 のソースコードからデプロイする前提で進めます。理由は最新の main ブランチの状態をデプロイするフローにするには、main にマージ時点での最新版のソースコードをベースにしてデプロイするのが、シンプルかなと考えたからです。
1 のDocker Image は毎回作成して Push するのがめんどくさかったので、すべての処理を Cloud Build に任せて乗っかる、という方法を採用した、という流れです。

環境変数を追加する

実行環境で環境変数を追加する場合は公式ドキュメントでいくつか紹介されてます。 ref: 環境変数を構成する  |  Google Cloud Functions に関するドキュメント

簡単に利用できるのは以下かなと思います。

  1. デプロイコマンド (gcloud run deploy) で環境変数を追加する。
  2. Docker Image をビルドするときに環境変数を差し込む。
  3. .env.yaml を追加する。

環境変数の中身にいくつかクレデンシャルの情報を入れますが、それらを GitHub のコミットに追加したくなかったので 1 or 3 で、今回は 3 を採用して、GitHub Actions の実行時に .env.yaml を作成していく方法を採用しました。

権限について

CloudRun に GitHub Actions (外部サービス)経由でデプロイするには専用のサービスアカウントが必要になります。
このサービスアカウントにどの権限を付与するのか、がドキュメントに書いてないことが原因で色々ハマったので、その権限について書いている、というのがこのエントリの内容になるのですが、 結論から先に書いてしまうとデプロイ用のサービスアカウントに対して以下の権限を追加することで GitHub Actions からデプロイできるようになります。

  • roles/run.admin
  • roles/iam.serviceAccountUser
  • roles/cloudbuild.builds.builder
  • roles/artifactregistry.reader

これは最後に載せている参照先の中の 【Cloud Run】 デプロイするために必要なパーミッション(GCP) - Qiita に書いてある内容そのままでした。

権限付与手順

サービスアカウントの作成と借用権限の設定

$ gcloud iam service-accounts create $ServiceAccount

$ gcloud iam workload-identity-pools create  $PoolName --location="global"`

$ gcloud iam workload-identity-pools providers create-oidc $ProviderName \
   --location="global" \
   --workload-identity-pool=$PoolName \
   --attribute-mapping="google.subject=assertion.sub,attribute.repository=assertion.repository,attribute.actor=assertion.actor,attribute.aud=assertion.aud" \
   --issuer-uri="https://token.actions.githubusercontent.com"

# 権限の借用
$ PoolID=$(
gcloud iam workload-identity-pools describe $PoolName \
      --location="global" \
      --format="value(name)" \
      --project=$ProjectName )
 
$ gcloud iam service-accounts add-iam-policy-binding "${ServiceAccount}@${ProjectName}.iam.gserviceaccount.com" \
   --role="roles/iam.workloadIdentityUser" \
   --member="principalSet://iam.googleapis.com/${PoolID}/attribute.repository/${GitHubRepo}"

権限の追加

gcloud projects add-iam-policy-binding ${PROJECT_ID} \
 --member="serviceAccount:${SERVICE_ACCOUNT}@${PROJECT_ID}.iam.gserviceaccount.com" \
 --role="roles/run.admin"

※ 上記の role をそれぞれ追加する。

GitHub Actions の設定ファイル

source からデプロイする場合

jobs:
  deploy:
    strategy:
      matrix:
        os: [ubuntu-latest]
    runs-on: ${{ matrix.os }}

    permissions:
      contents: "read"
      id-token: "write"

    steps:
      - name: Checkout
        uses: actions/checkout@v3

      - id: "auth"
        name: "Authenticate to Google Cloud"
        uses: "google-github-actions/auth@v1"
        with:
          workload_identity_provider: ${{ secrets.WORKLOAD_IDENTITY_PROVIDER }}
          service_account: ${{ secrets.GCP_DEPLOY_SERVICE_ACCOUNT }}@${{ secrets.GCP_PROJECT_ID }}.iam.gserviceaccount.com

      - name: Set up Cloud SDK
        uses: google-github-actions/setup-gcloud@v1

      - name: Deploy to Cloud Run
        run: |-
          echo "GCP_PROJECT_ID: ${{ secrets.GCP_PROJECT_ID }}" > .env.yaml
          echo "LINE_MESSAGE_CHANNEL_SECRET: ${{ secrets.LINE_MESSAGE_CHANNEL_SECRET }}" >> .env.yaml
          echo "LINE_PUBLIC_KEY_ID: ${{ secrets.LINE_PUBLIC_KEY_ID }}" >> .env.yaml
          echo "LINE_CHANNEL_ID: '${{ secrets.LINE_CHANNEL_ID }}'" >> .env.yaml
          echo "LINE_PRIVATE_KEY: ${{ secrets.LINE_PRIVATE_KEY }}" >> .env.yaml
          echo "LINE_CHANNEL_ACCESS_TOKEN: ${{ secrets.LINE_CHANNEL_ACCESS_TOKEN }}" >> .env.yaml
          gcloud run deploy $SERVICE_NAME \
            --project=$GCP_PROJECT_ID \
            --region=$REGION \
            --service-account=$GCP_DEPLOY_SERVICE_ACCOUNT@$GCP_PROJECT_ID.iam.gserviceaccount.com \
            --allow-unauthenticated \
            --cpu=2 \
            --memory=512Mi \
            --timeout=60 \
            --platform managed \
            --env-vars-file ./.env.yaml \
            --source ./$SourceRepo

Docker Image からデプロイする場合

jobs:
  deploy:
    strategy:
      matrix:
        os: [ubuntu-latest]
    runs-on: ${{ matrix.os }}
    defaults:
      run:
        working-directory: ./$SourceRepoName

    permissions:
      contents: "read"
      id-token: "write"

    steps:
      - name: Checkout
        uses: actions/checkout@v3

      - id: "auth"
        name: "Authenticate to Google Cloud"
        uses: "google-github-actions/auth@v1"
        with:
          workload_identity_provider: ${{ secrets.WORKLOAD_IDENTITY_PROVIDER }}
          service_account: ${{ secrets.GCP_DEPLOY_SERVICE_ACCOUNT }}@${{ secrets.GCP_PROJECT_ID }}.iam.gserviceaccount.com

      - name: Set up Cloud SDK
        uses: google-github-actions/setup-gcloud@v1

      - name: Authorize Docker push
        run: gcloud auth configure-docker

      - name: Build Images
        run: |
          echo $EnvNameKey=$EnvNameVal > .env
          docker build -t asia.gcr.io/${{ secrets.GCP_PROJECT_ID }}/$ImageName:$TagName --platform linux/amd64 .

      - name: Push Images
        run: docker push asia.gcr.io/${{ secrets.GCP_PROJECT_ID }}/$ImageName:$TagName

      - name: Deploy to Cloud Run
        run: |
          gcloud run deploy $SERVICE_NAME \
          --project=$GCP_PROJECT_ID \
          --region=$REGION \
          --service-account=$GCP_DEPLOY_SERVICE_ACCOUNT@$GCP_PROJECT_ID.iam.gserviceaccount.com \
          --image asia.gcr.io/${{ secrets.GCP_PROJECT_ID }}/$ImageName:$TagName \
          --allow-unauthenticated \
          --cpu=2 \
          --memory=512Mi \
          --timeout=60 \
          --platform managed

See Also

以下のエントリをいくつか参考にさせてもらいました。