GitHub ActionsでDockerイメージをビルド&プッシュしてCloud Run Jobsを更新するパイプラインを考えてみた

記事タイトルとURLをコピーする

G-gen の武井です。当記事では GitHub Actions を使って Docker イメージをビルド&プッシュして Cloud Run Jobs を更新するパイプランについて説明します。

GitHub Actions

概要

GitHub Actions とは、ソースコード管理ツールである GitHub に包含される機能の一つで、リポジトリで管理されるソースコードをもとに CI/CD (継続的インティグレーション / 継続的デリバリー) を実現します。

ワークフロー

GitHub Actions で自動化したい処理とその内容・条件を定義したものを ワークフロー と言います。

定義ファイルは YAML 形式 で記述して .github/workflows ディレクトリ に格納すれば利用可能です。

構成

当記事で解説するパイプラインと環境構成は下図のとおりです。

Artifact Registry と Cloud Run Jobs は異なるプロジェクトで管理されるため、プロジェクトごとに Workload Identity を設定して GitHub Actions と連携します。

# プロジェクト 説明
1 demo-app-common-repo Artifact Registry でアプリが使用するイメージを一元管理
2 sand1-demo-app-dev Cloud Run Jobs でアプリを実行
3 sand2-demo-app-dev Cloud Run Jobs でアプリを実行

ワークフローの概要

今回ワークフローで定義した処理の概要は以下のとおりです。(詳細は後述)

  1. main ブランチへのマージをトリガーにワークフローが起動
  2. 指定のパス (アプリのソースコードが格納されたパス) の変更有無を確認
  3. 指定のパスに変更があった (アプリに更新があった) 場合、Docker イメージをビルドして Artifact Registry にプッシュ
  4. Cloud Run Jobs のイメージを更新する

ソースコード

概要

ソースコードは以下の形で構成されます。

.github/workflows ディレクトリにはワークフローの定義ファイル、cloud_run_jobs_code ディレクトリにはアプリごとのソースコードを格納します。
(アプリ名と Cloud Run Jobs のジョブ名は統一しています)

# アプリのソースコードの詳細は割愛
.
├── .github
│   └── workflows
│       ├── build-and-push.yaml
│       ├── dev-cd.yaml
│       └── release.yaml
├── cloud_run_jobs_code
│   ├── demo-app-1
│   │   ├── Dockerfile
│   │   ├── main.py
│   │   ├── poetry.lock
│   │   ├── poetry.toml
│   │   └── pyproject.toml
│   └── demo-app-2
│       ├── Dockerfile
│       ├── main.py
│       ├── poetry.lock
│       ├── poetry.toml
│       └── pyproject.toml
└── README.md

dev.yaml

処理内容

このワークフローでは、以下のモジュールを使用してアプリのソースコードが格納されたパスを指定し、それらの変更有無を確認します。

指定したパスに格納されるソースコード (アプリ) に変更があった場合は build-and-push.yaml で処理を継続しますが、アプリに変更がない場合、ワークフローはここで終了します。

その他詳細は以下のとおりです。

# 行数 説明
1 3〜7行目 main ブランチへのマージをトリガーに指定
2 9〜11行目 ワークフローの権限定義
3 13〜29行目 パスの指定、ならびに指定したパスの変更有無を確認
4 31〜37行目 変更があれば後続処理にアプリ名 (demo-app-1) を appName として渡す
5 39〜45行目 変更があれば後続処理にアプリ名 (demo-app-2) を appName として渡す

ファイル定義

name: Deploy to GCP development env
  
on:
  push:
    branches:
      - main
  workflow_dispatch:
  
permissions:
  id-token: write
  contents: read
  
jobs:
  check-paths:
    runs-on: ubuntu-latest
    outputs:
      demo-app-1-changed: ${{ steps.filter.outputs.demo-app-1 }}
      demo-app-2-changed: ${{ steps.filter.outputs.demo-app-2 }}
    steps:
      - uses: actions/checkout@v4
      - uses: dorny/paths-filter@v3
        id: filter
        with:
          list-files: shell
          filters: |
            demo-app-1:
              - 'cloud_run_jobs_code/demo-app-1/**'
            demo-app-2:
              - 'cloud_run_jobs_code/demo-app-2/**'
  
  demo-app-1:
    needs: check-paths
    if: >-
      needs.check-paths.outputs.demo-app-1-changed == 'true'
    uses: ./.github/workflows/build-and-push.yaml
    with:
      appName: 'demo-app-1'
  
  demo-app-2:
    needs: check-paths
    if: >-
      needs.check-paths.outputs.demo-app-2-changed == 'true'
    uses: ./.github/workflows/build-and-push.yaml
    with:
      appName: 'demo-app-2'

build-and-push.yaml

処理内容

このワークフローは dev.yaml から appName が渡された (アプリに変更があった) 場合にのみ起動します。

そして、以下のモジュールを使用して各プロジェクトと Workload Identity で連携してタグ付き Docker イメージのビルド & プッシュと Cloud Run Jobs の更新を行います。

その他詳細は以下のとおりです。

# 行数 説明
1 3〜8行目 appName のインプットをトリガーに指定
2 10〜26行目 ランナーのセットアップ
4 28〜30行目 環境変数の定義
4 32〜58行目 demo-app-common-repo との WI 連携、Docker イメージのビルド&プッシュ
5 60〜71行目 sand1-demo-app-dev との WI 連携、Cloud Run Jobs の更新
5 60〜71行目 sand2-demo-app-dev との WI 連携、Cloud Run Jobs の更新

ファイル定義

name: Build and push to GCP development env.
  
on:
  workflow_call:
    inputs:
      appName:
        required: true
        type: string
  
jobs:
  sdk:
    runs-on: ubuntu-latest
    steps:
      - name: 'Set up Cloud SDK'
        uses: 'google-github-actions/setup-gcloud@v2'
        with:
          version: '>= 363.0.0'
  
  build-and-push:
    runs-on: ubuntu-latest
    timeout-minutes: 300
    needs: [sdk]
  
    permissions:
      id-token: write
      contents: read
  
    env:
      REPO: 'demo-app-common-repo-dev
      GAR: 'asia-northeast1-docker.pkg.dev/demo-app-common-repo'
  
    steps:
      - uses: actions/checkout@v4
  
      - uses: google-github-actions/auth@v2
        with:
          project_id: demo-app-common-repo
          workload_identity_provider: 'projects/1111111111/locations/global/workloadIdentityPools/demo-app-github-actions/providers/demo-app-github-actions'
          service_account: 'github@demo-app-common-repo.iam.gserviceaccount.com'
  
      - name: 'Set up app dir name'
        run: echo "APP_DIR=cloud_run_jobs_code/${{ inputs.appName }}" >> $GITHUB_ENV
  
      - name: 'Set up short sha'
        run: echo "SHORT_SHA=$(echo ${{ github.sha }} | cut -c1-8)" >> "$GITHUB_ENV"
  
      - name: 'Set up app name variable'
        run: echo "IMAGE_NAME=${{ inputs.appName }}" >> "$GITHUB_ENV"
  
      - name: Build and Push image to Google Cloud Artifact Registry
        run: |
          gcloud auth configure-docker asia-northeast1-docker.pkg.dev --quiet
          docker build --platform linux/amd64 -t "${REPO}/${IMAGE_NAME}" ${APP_DIR}
  
          docker tag "${REPO}/${IMAGE_NAME}:latest" "${GAR}/${REPO}/${IMAGE_NAME}:latest"
          docker push "${GAR}/${REPO}/${IMAGE_NAME}:latest"
  
          docker tag "${REPO}/${IMAGE_NAME}:latest" "${GAR}/${REPO}/${IMAGE_NAME}:${SHORT_SHA}"
          docker push "${GAR}/${REPO}/${IMAGE_NAME}:${SHORT_SHA}"
     
      - uses: 'google-github-actions/auth@v2'
        with:
          project_id: sand1-demo-app-dev
          workload_identity_provider: 'projects/2222222222/locations/global/workloadIdentityPools/demo-app-github-actions/providers/demo-app-github-actions'
          service_account: 'github@sand1-demo-app-dev.iam.gserviceaccount.com'
  
      - name: Deploy to Cloud Run (sand1-demo-app-dev)
        uses: 'google-github-actions/deploy-cloudrun@v2'
        with:
          job: ${{ env.IMAGE_NAME }}
          image: ${{ env.GAR }}/${{ env.REPO }}/${{ env.IMAGE_NAME}}:latest
          region: 'asia-northeast1'
  
      - uses: 'google-github-actions/auth@v2'
        with:
          project_id: sand2-demo-app-dev
          workload_identity_provider: 'projects/3333333333/locations/global/workloadIdentityPools/demo-app-github-actions/providers/demo-app-github-actions'
          service_account: 'github@sand2-demo-app-dev.iam.gserviceaccount.com'
  
      - name: Deploy to Cloud Run (sand2-demo-app-dev)
        uses: 'google-github-actions/deploy-cloudrun@v2'
        with:
          job: ${{ env.IMAGE_NAME }}
          image: ${{ env.GAR }}/${{ env.REPO }}/${{ env.IMAGE_NAME}}:latest
          region: 'asia-northeast1'

インフラ構成

Workload Identity

今回の構成では、GitHub Actions との連携に Workload Identity を用いています。

設定方法の説明は割愛しますが、以下の記事の Workload Identiry 連携の設定 にならい同様の設定をします。

blog.g-gen.co.jp

その際、Workload Identity に紐づけるサービスアカウントには以下の権限を付与します。

# プロジェクト 付与する権限
1 demo-app-common-repo Artifact Registry 書き込み
2 sand1-demo-app-dev Cloud Run デベロッパー
3 sand2-demo-app-dev Cloud Run デベロッパー

Artifact Registry

今回の構成では、Artifact Registry と Cloud Run Jobs が異なるプロジェクトに配置されています。

他のプロジェクトの Docker イメージをデプロイする場合、以下にならい Cloud Run サービス エージェント に対して権限を付与します。

サービスエージェントについては以下の記事で説明しています。

blog.g-gen.co.jp

動作確認

条件

demo-app-1demo-app-2 のソースコードを編集して main ブランチにマージした際の動作を確認します。

main ブランチへのマージとワークフローの起動

main ブランチにマージをするとワークフロー (dev.yaml) が起動します。

demo-app-1demo-app-2 を更新したため、check-paths ジョブで変更を検知して後続の build-and-push.yaml にアプリ名を渡しています。

main ブランチにマージ

パスの変更有無を確認するジョブ

2つのパスで変更を確認

後続のワークフローの起動

アプリ名が渡され、次のワークフロー (build-and-puysh.yaml) が起動します。

今回は2つのアプリが更新されたので、それぞれのアプリイメージをビルド&プッシュし、各プロジェクトの Cloud Run Jobs を更新するフローが走ります。

Docker イメージのビルドとプッシュ

demo-app-1demo-app-2 の Docker イメージ (タグ付き) がビルドされリポジトリにプッシュされました。

demo-app-1 の Docker イメージ (タグ付き)

Artifact Registry

demo-app-2 の Docker イメージ (タグ付き)

Aritifact Registory

Cloud Run Jobs の更新

Docker イメージがリポジトリにプッシュされると、各プロジェクトで稼働する Cloud Run Jobs が gcloud run jobs deploy コマンドで最新のタグ付きイメージに更新されました。

両プロジェクトの Cloud Run Jobs (demo-app-1) が更新

両プロジェクトの Cloud Run Jobs (demo-app-2) が更新

Cloud Logging にも google.cloud.run.v1.Jobs.ReplaceJob メソッドにより Cloud Run Jobs が更新された旨が記録されていました。

Cloud Logging からも確認可能

関連記事

blog.g-gen.co.jp

武井 祐介 (記事一覧)

クラウドソリューション部所属。G-gen唯一の山梨県在住エンジニア

Google Cloud Partner Top Engineer 2024 に選出。IaC や CI/CD 周りのサービスやプロダクトが興味分野です。

趣味はロードバイク、ロードレースやサッカー観戦です。