📘

GithubActions で CloudRun を CI/CD する

2023/06/05に公開

はじめに

GithubActions で CloudRun を CI/CD する方法を紹介します。
CloudRun を CI/CD する記事はいろいろありますが、情報が古かったり、Registry に Push する過程が説明されていなかったりと、ちょっとしたハマりどころがありました。

そこで今回はハンズオン形式で、GithubActions で CloudRun を CI/CD する方法を紹介します。

事前準備

以下の記事の続きから始めます。
まだ行っていない方は前回の記事から行うとハンズオンが進められます

https://zenn.dev/jinwatanabe/articles/ae0d75444fa056

開発環境

  • go version go1.20.3 linux/amd64
  • VSCode
  • Docker version 23.0.4
  • Google Cloud SDK 418.0.0

1. GithubActions で CI を行う

ここでは Go のテストを作成して、GithubActions で CI を行います。

1-1. テストを作成する

必要なライブラリをいれます

$ go get "github.com/stretchr/testify/assert"

適当なテストを作成します

$ touch sample_test.go
package main

import (
	"testing"

	"github.com/stretchr/testify/assert"
)

func Test_Sample(t *testing.T) {
	assert.Equal(t, 1, 1)
}

テストを実行します

$ go test

PASS
ok      gin-ping        0.011s

1-2. GithubActions の設定を行う

GithubActions につかう設定ファイルを作成します

$ mkdir .github
$ mkdir .github/workflows
$ touch .github/workflows/run.yml

以下の内容を記述します

run.yml
name: Run CI/CD

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v2

      - uses: actions/setup-go@v3
        with:
          go-version: "1.20"

      - name: Cache Go modules
        id: cache-go
        uses: actions/cache@v3
        with:
          path: ~/go/pkg/mod
          key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
          restore-keys: |
            ${{ runner.os }}-go-

      - name: Download Go modules
        shell: bash
        if: ${{ steps.cache-go.outputs.cache-hit != 'true' }}
        run: |
          go mod download

      - name: Run Go tests
        run: |
          go test

GithubActions で Go のテストを実行する設定ができました。

1-3. GithubActions でテストを実行する

実行するために Push を行います
(リポジトリがない人は作成してください)

$ git add .
$ git commit -m "add test"
$ git push origin main

Github のリポジトリにアクセスして、Actions のタブを開きます

しばらく待っていると、テストが実行されます

テストが成功したら、CI の設定は完了です。

2. GithubActions で CD を行う

ここでは GithubActions で CI したイメージを、CloudRun にデプロイします。

2-1. Artifact Registry への Push を自動化する

GithubActions で Artifact Registry に Push するための設定を行います。

まずは GithubActions で利用するサービスを作成します

$ export PROJECT_ID=作成したプロジェクトのID

$ gcloud iam service-accounts create docker-image-ci --display-name="Docker Image CI" --project=$PROJECT_ID

次に Artifact Registry に Push するための権限を付与します

$ gcloud iam roles create artifactRegistryCIRole --project=$PROJECT_ID --title="Docker image CI Role" --description="Push docker image via CI" --permissions=artifactregistry.repositories.uploadArtifacts --stage=BETA

$ gcloud projects add-iam-policy-binding $PROJECT_ID --member=serviceAccount:docker-image-ci@$PROJECT_ID.iam.gserviceaccount.com --role=projects/$PROJECT_ID/roles/artifactRegistryCIRole

次にサービスアカウントの鍵を作成します

$ gcloud iam service-accounts keys create key.json --iam-account=docker-image-ci@$PROJECT_ID.iam.gserviceaccount.com --project=$PROJECT_ID

コマンドを実行した直下に key.json が作成されます。
この内容は環境変数として GithubActions に登録するので Base64 に変換してクリップボードにコピーします

cat key.json | base64 | xclip -selection c

2-2. GithubActions の設定を行う

GithubActions でリポジトリの「Settings」→「Secrets and variables」→「Actions」に移動します

「New repository secret」をクリックして、以下の内容を入力します

Name Value
GCLOUD_AUTH 先ほど Base64 に変換した key.json の内容
SERVICE_ACCOUNT docker-image-ci@$PROJECT_ID.iam.gserviceaccount.com
PROJECT_ID 作成したプロジェクトの ID

次に GithubActions にビルドと Artifact Registry へのプッシュの設定を行います

run.ymlを修正します

run.yml
name: Run CI/CD

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v2

      - uses: actions/setup-go@v3
        with:
          go-version: "1.20"

      - name: Cache Go modules
        id: cache-go
        uses: actions/cache@v3
        with:
          path: ~/go/pkg/mod
          key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
          restore-keys: |
            ${{ runner.os }}-go-

      - name: Download Go modules
        shell: bash
        if: ${{ steps.cache-go.outputs.cache-hit != 'true' }}
        run: |
          go mod download

      - name: Run Go tests
        run: |
          go test

  deploy:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout
        uses: actions/checkout@v2
        with:
          ref: ${{ github.ref }}

      - id: "auth"
        uses: "google-github-actions/auth@v1"
        with:
          credentials_json: "${{ secrets.GCLOUD_AUTH }}"

      - name: Setup Google Cloud
        uses: google-github-actions/setup-gcloud@v1
        with:
          service_account_key: ${{ secrets.GCLOUD_AUTH }}
          project_id: ${{ secrets.PROJECT_ID }}

      - name: Configure docker for artifact registry
        run: |
          gcloud auth configure-docker us-west1-docker.pkg.dev

      - name: set TAG
        run: |
          echo "TAG=$(echo $GITHUB_REF | awk -F/ '{print $NF}')" >> $GITHUB_ENV

      - name: Build
        run: docker build -t us-west1-docker.pkg.dev/${{ secrets.PROJECT_ID }}/go-api/api-image:${{ env.TAG }} .

      - name: Push
        run: |
          docker push us-west1-docker.pkg.dev/${{ secrets.PROJECT_ID }}/go-api/api-image:${{ env.TAG }}

うまくいきました

Artifact Registry にイメージが Push されました

2-3. CloudRun に自動でデプロイする」

まずはサービスにデプロイするための権限を付与します

GCP を開いて、「IAM と管理」→「IAM」をクリック

作成したサービスアカウントを選択して、一番右の鉛筆マークをクリックします

「別のロールを追加」をクリックして以下を追加します

  • Cloud Run 管理者
  • Secret Manager 管理者
  • サービス アカウント ユーザー

「保存」をクリックします

run.ymlを修正します

run.yml
name: Run CI/CD

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v2

      - uses: actions/setup-go@v3
        with:
          go-version: "1.20"

      - name: Cache Go modules
        id: cache-go
        uses: actions/cache@v3
        with:
          path: ~/go/pkg/mod
          key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
          restore-keys: |
            ${{ runner.os }}-go-

      - name: Download Go modules
        shell: bash
        if: ${{ steps.cache-go.outputs.cache-hit != 'true' }}
        run: |
          go mod download

      - name: Run Go tests
        run: |
          go test

  deploy:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout
        uses: actions/checkout@v2
        with:
          ref: ${{ github.ref }}

      - id: "auth"
        uses: "google-github-actions/auth@v1"
        with:
          credentials_json: "${{ secrets.GCLOUD_AUTH }}"

      - name: Setup Google Cloud
        uses: google-github-actions/setup-gcloud@v1
        with:
          service_account_key: ${{ secrets.GCLOUD_AUTH }}
          project_id: ${{ secrets.PROJECT_ID }}

      - name: Configure docker for artifact registry
        run: |
          gcloud auth configure-docker us-west1-docker.pkg.dev

      - name: set TAG
        run: |
          echo "TAG=$(echo $GITHUB_REF | awk -F/ '{print $NF}')" >> $GITHUB_ENV

      - name: Build
        run: docker build -t us-west1-docker.pkg.dev/${{ secrets.PROJECT_ID }}/go-api/api-image:${{ env.TAG }} .

      - name: Push
        run: |
          docker push us-west1-docker.pkg.dev/${{ secrets.PROJECT_ID }}/go-api/api-image:${{ env.TAG }}

      - name: Deploy
        run: |-
          gcloud run deploy api-image \
            --project=${{ secrets.PROJECT_ID }} \
            --image=us-west1-docker.pkg.dev/${{ secrets.PROJECT_ID }}/go-api/api-image:${{ env.TAG }} \
            --region=us-west1 \
            --service-account=${{ secrets.SERVICE_ACCOUNT }} \
            --allow-unauthenticated

新しいイメージでデプロイされたことを確認するために、main.go を修正します

main.go
package main

import (
	"fmt"
	"os"

	"github.com/gin-gonic/gin"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
)

type User struct {
	gorm.Model
	Id  int `gorm:"primaryKey" json:"id"`
	Name string `json:"name"`
	Age  int `json:"age"`
}

func main() {
	user := os.Getenv("DB_USER")
	password := os.Getenv("DB_PASSWORD")
	dsn := fmt.Sprintf("%s:%s@tcp(aws.connect.psdb.cloud)/[DB名]?tls=true", user, password)
	db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
	if err != nil {
		panic("failed to connect database")
	}

	r := gin.Default()

	r.GET("/ping", func(c *gin.Context) {
		c.JSON(200, gin.H{
			"message": "Deployed",
		})
	})

	r.GET("/users", func(c *gin.Context) {
		var users []User
		db.Unscoped().Find(&users)
		c.JSON(200, users)
	})

	r.Run() // listen and serve on 0.0.0.0:8080
}

/pingのレスポンスが変えてみました

実際に GitHub Actions を回してデプロイされるか確認します

$ git add .
$ git commit -m "change response"
$ git push origin main

GitHub Actions は成功したことを確認したら、前回した Curl をもう一度実行してみます

$ curl https://go-api-xxxxxx-an.a.run.app/ping
{"message":"Deployed"}

レスポンスが変わっていました
CD ができていることが確認できました

おわりに

今回は GithubActions で CloudRun の CI/CD を実装しました
あまり GCP は使い慣れていないのですこし権限周りが大変でしたがなんとか快適に使えるようになったので今後利用していきたいです

今回作成したリポジトリは以下になります

https://github.com/jinwatanabe/cloudrun-cicd-handson/tree/main/cloudrun-cicd-handson

参考

GitHubで編集を提案

Discussion