⚒️

Github Actions + Cloud Deploy でモダンな CI/CD 実装

2023/06/20に公開

先日の公式ブログGithub Actions と Google Cloud Deploy の連携が可能になったことがわかりました。

GKE や Cloud Run に対する CI/CD はいくつか選択肢があると思います、Cloud Build を利用したものなど。その上で Github Actions を CI で、Cloud Deploy を CD でシームレスに利用できるということは CI 周りは Github Actions がデファクトになりつつあるって感じでしょうか。 Workload Identity によってクレデンシャルキーなしで連携できるようになったりとセキュアな仕組みもありますし。

ということで、Nginx の Cloud Run サービスに対してのビルド~デプロイまでを Github Actions + Cloud Deploy で実装してみようと思います。

=== 2024.03.18 追記 ===
Cloud Deploy についてはこちらの記事で改めて触れてみました。GA となった便利な機能や自身がハマったポイントについても書いています。Cloud Deploy について、全体像を見ながら知ってみたい という方はぜひお読みください!

Overview

Github Actions と Cloud Deploy を活用した CI/CD について検証

  • GKE や Cloud 向けのコンテナ周りの CI/CD について Cloud DeployGithub Actions からシームレスに呼び出せることができました
  • Github Actions に Cloud Deploy のリリース作成がラッピングされたアクションである google-github-actions/create-cloud-deploy-release によって、適切な環境変数を渡すだけで連携ができました
  • Cloud Deploy は 継続的デリバリーに特化したサービス となっていて、複数環境にデプロイしてテストした時やテスト結果を元に次の環境に移りたい時にデリバリーパイプラインを構築しておくことで便利に利用できることがわかりました

キーワード

CI/CD の実装

Nginx の Cloud Run サービスへのビルド~デプロイまでを題材に手順をまとめていきたいと思います。

最低限の設定を手動

まずは手動で Cloud Run サービスのデプロイまでをやってみます。

Dockerfile は下記で用意します。

Dockerfile
FROM nginx:latest
COPY ./default.conf /etc/nginx/conf.d/default.conf
COPY ./index.html /var/www/index.html
index.html
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="utf-8">
    <title>nginx test</title>
  </head>
  <body>
    <h1>Hello, Nginx!</h1>
  </body>
</html>
default.conf
server {
    listen 8080 default_server;
    server_name localhost;
    location / {
        root /var/www;
        access_log /var/log/nginx/access.log;
        error_log /var/log/nginx/error.log;
    }
}

ビルドして Artifact Registry に格納します。

Terminal
# Artifact Registry の作成
gcloud artifacts repositories create nginx-repo --location=asia-northeast1 --repository-format=docker
# Dockerfile のビルド
docker build . -t asia-northeast1-docker.pkg.dev/{project_id}/nginx-repo/test
# Artifact Registry の認証
gcloud auth configure-docker asia-northeast1-docker.pkg.dev
# Artifact Regisry への格納
docker push asia-northeast1-docker.pkg.dev/{project_id}/nginx-repo/test

Cloud Run サービスにデプロイします。

Terminal
gcloud run deploy nginx-test \
  --image asia-northeast1-docker.pkg.dev/{project_id}/nginx-repo/test \
  --region asia-northeast1

これでデプロイまでは最低限の手順でできました。これを Github Actions と Cloud Deploy を利用して自動化&高度化を実装していきます。

継続的デプロイメント:Cloud Deploy

Cloud Deploy について公開資料から抜粋します。

クイックスタートに沿って実装していきます。

clouddeploy.yaml の作成

デリバリーパイプラインの大枠を作成します。YAML 形式で下記のように記述します。

clouddeploy.yaml
apiVersion: deploy.cloud.google.com/v1
kind: DeliveryPipeline
metadata:
  name: nginx-test
description: main application pipeline
serialPipeline:
  stages:
  - targetId: run-dev
    profiles: [dev]
  - targetId: run-prod
    profiles: [prod]
---

apiVersion: deploy.cloud.google.com/v1
kind: Target
metadata:
  name: run-dev
description: Cloud Run development service
run:
  location: projects/{project_id}/locations/asia-northeast1
---

apiVersion: deploy.cloud.google.com/v1
kind: Target
metadata:
  name: run-prod
description: Cloud Run production service
run:
  location: projects/{project_id}/locations/asia-northeast1

kind: DeliveryPipelineserialPipelinestage にデプロイしたいステップを記述します。今回は run-devrun-prod を定義します。

これらのステージに kind: Target でデプロイされるロケーションを設定しています。

skaffold.yaml と run-dev/prod.yaml の作成

Skaffold プロファイルを利用して Cloud Run サービスを定義ファイルと紐付けを行います。

skaffold.yaml
apiVersion: skaffold/v3alpha1
kind: Config
metadata:
  name: cloud-run-nginx
profiles:
- name: dev
  manifests:
    rawYaml:
    - run-dev.yaml
- name: prod
  manifests:
    rawYaml:
    - run-prod.yaml
deploy:
  cloudrun: {}

clouddeploy.yamlprofilesskaffold.yamlprofiles で紐付きます。さらに rawYaml で設定した YAML ファイルを下記で作成します。

run-dev.yaml
apiVersion: serving.knative.dev/v1
kind: Service
metadata:
 name: nginx-dev
spec:
 template:
  spec:
   containers:
   - image: asia-northeast1-docker.pkg.dev/{project_id}/nginx-repo/test
run-prod.yaml
apiVersion: serving.knative.dev/v1
kind: Service
metadata:
 name: nginx-prod
spec:
 template:
  spec:
   containers:
   - image: asia-northeast1-docker.pkg.dev/{project_id}/nginx-repo/test

これらによって各ステージにデプロイされるロケーションとイメージを紐づけることができました。

Cloud Deploy サービスに登録

下記のコマンドでパイプラインとターゲットを Cloud Deploy に登録します。

Terminal
gcloud deploy apply \
  --file=clouddeploy.yaml \
  --region=asia-northeast1 \
  --project={project_id}

Cloud Deply のコンソール画面に行くと nginx-test という名でデリバリーパイプラインが作成されています。

リリースの作成

パイプラインを起動させて実際にデプロイを行います。

下記のコマンドでリリースを作成してパイプラインに流し込みます。
project, region, delivery-pipeline のパラメータを設定して実行します。

Terminal
gcloud deploy releases create test-release-001 \
  --project={project_id} 
  --region=asia-northeast1 
  --delivery-pipeline=nginx-test



test-relese-001 が実行されて run-dev がグリーンになります。run-dev.yaml で定義した Cloud Run サービス名でちゃんとデプロイされています。



Cloud Deploy の特徴でもある前段のステージでデプロイができテストが完了したら、Promote することによって次のステージに移すことができます。同様に定義したサービス名でデプロイ完了です。


パイプラインを一度定義すればパラメータを変えて似たようなコマンドを何回も打つことがなくなるし、順々や並行に特定のステージにデプロイできるためデプロイの安定性も高められると感じました。

継続的インテグレーション:Github Actions

冒頭で紹介した公式ブログにある通り、google-github-actions/create-cloud-deploy-release@v0がポイントみたいですね。

Cloud Deploy でデリバリーパイプラインを作成しておき、上記のアクションを用いることでリリースを流しこんでくれるイメージでしょうか。

サンプルの Github Actions のワークフローを見ていきたいと思います。

環境設定

ワークフロー起動のトリガーと環境変数設定部分です。特に特別な設定はなさそうです。

name: Build app and create a release in Cloud Deploy

on:
  push:
    branches:
      - $default_branch

env:
  PROJECT_ID: YOUR_PROJECT_ID # TODO: update Google Cloud project id
  GAR_LOCATION: YOUR_GAR_LOCATION # TODO: update Artifact Registry location
  REGION: YOUR_APP_REGION # TODO: update Cloud Run service region
  APP: app

ビルドフェーズ

記事に詳細な解説があるので、こちらも特別付け加える点はないですね。
google-github-actions/auth@v1 で Workload Identity との連携設定がなされていてクレデンシャルキーなしで Github Actions から Google Cloud リソースへのアクセスが可能です。

あとは、手動で行った手順が記述されています。

jobs:
  deploy:
    # Add 'id-token' with the intended permissions for workload identity federation
    permissions:
      contents: 'read'
      id-token: 'write'

    runs-on: ubuntu-latest
    steps:
      - name: 'Checkout'
        uses: 'actions/checkout@v3'

      - name: 'Google auth'
        id: 'auth'
        uses: 'google-github-actions/auth@v1'
        with:
          workload_identity_provider: '${{ secrets.WIF_PROVIDER }}' 
          service_account: '${{ secrets.WIF_SERVICE_ACCOUNT }}' 

      - name: 'Set up Cloud SDK'
        uses: 'google-github-actions/setup-gcloud@v1'
        with:
          project_id: '${{ env.PROJECT_ID }}'

      - name: 'Docker auth'
        run: |-
          gcloud auth configure-docker ${{ env.GAR_LOCATION }}-docker.pkg.dev
      - name: 'Build and push container'
        run: |-
          docker build -t "${{ env.GAR_LOCATION }}-docker.pkg.dev/${{ env.PROJECT_ID }}/${{ env.APP }}/${{ env.APP }}:${{ github.sha }}" ./app
          docker push "${{ env.GAR_LOCATION }}-docker.pkg.dev/${{ env.PROJECT_ID }}/${{ env.APP }}/${{ env.APP }}:${{ github.sha }}"

デプロイフェーズ

サンプルにはパイプラインの作成も工程として入っていましたが、ワークフローが起動されるたびにデリバリーパイプラインが構築されるのも違和感がありますね。

公式ブログにある通り、デリバリーパイプラインは Terraform などの IaC で管理するのが良さそうです。

このワークフロー例では、デリバリー パイプラインのサンプルを作成しましたが、実際には、アプリケーション ビルド パイプラインの外でパイプラインやターゲットを管理することをおすすめします。

そして、最後にリリースの作成を下記の記述でできるようです。
サンプルには images の設定項目がありますが、上述したリリース作成のコマンド時にはイメージの指定がなくても問題がなかったのでなくても良さそうです。

実際には skaffold.yaml で設定した run-dev/prod.yaml でイメージの指定していていますし。

- name: 'Create release name'
   run: |-
     echo "RELEASE_NAME=${{ env.APP }}-${GITHUB_SHA::7}-${GITHUB_RUN_NUMBER}" >> ${GITHUB_ENV}
      
- name: 'Create Cloud Deploy release'
  id: 'release'
  uses: 'google-github-actions/create-cloud-deploy-release@v0'
  with:
    delivery_pipeline: '${{ env.APP }}'
    name: '${{ env.RELEASE_NAME }}'
    region: '${{ env.REGION }}'
    description: '${{ env.GITHUB_COMMIT_MSG }}'
    skaffold_file: 'config/skaffold.yaml'
    images: 'app=${{ env.GAR_LOCATION }}-docker.pkg.dev/${{ env.PROJECT_ID }}/${{ env.APP }}/${{ env.APP }}:${{ github.sha }}'

- name: 'Report Cloud Deploy release'
  run: |-
   echo "Created release ${{ steps.release.outputs.name }} "
   echo "Release link ${{ steps.release.outputs.link }} "

実際に試したワークフロー

Workload Identity の設定ができている前提で、手動での作業に合わせて下記を変更しました。

  • リポジトリ名を ${{ env.GAR_LOCATION }}-docker.pkg.dev/${{ env.PROJECT_ID }}/nginx-repo/test
  • google-github-actions/create-cloud-deploy-release のパラメータから image を削除

こちらで想定通りの挙動となりました。

jobs:
  deploy:
    permissions:
      contents: 'read'
      id-token: 'write'

    runs-on: ubuntu-latest
    steps:
      - name: 'Checkout'
        uses: 'actions/checkout@v3'

      - name: WorkloadIdentityAuth
        id: WorkloadIdentityAuth
        uses: google-github-actions/auth@v1
        with:
          workload_identity_provider: '${{ secrets.WIF_PROVIDER }}' 
          service_account: '${{ secrets.WIF_SERVICE_ACCOUNT }}' 

      - name: 'Set up Cloud SDK'
        uses: 'google-github-actions/setup-gcloud@v1'
        with:
          project_id: '${{ env.PROJECT_ID }}'

      - name: 'Docker auth'
        run: |-
          gcloud auth configure-docker ${{ env.GAR_LOCATION }}-docker.pkg.dev

      - name: 'Build and push container'
        run: |-
          docker build -t "${{ env.GAR_LOCATION }}-docker.pkg.dev/${{ env.PROJECT_ID }}/nginx-repo/test:${{ github.sha }}" ./config
          docker push "${{ env.GAR_LOCATION }}-docker.pkg.dev/${{ env.PROJECT_ID }}/nginx-repo/test:${{ github.sha }}"

      - name: 'Create release name'
        run: |-
          echo "RELEASE_NAME=${{ env.APP }}-${GITHUB_SHA::7}-${GITHUB_RUN_NUMBER}" >> ${GITHUB_ENV}

      - name: 'Create Cloud Deploy release'
        id: 'release'
        uses: 'google-github-actions/create-cloud-deploy-release@v0'
        with:
          delivery_pipeline: '${{ env.APP }}'
          name: '${{ env.RELEASE_NAME }}'
          region: '${{ env.REGION }}'
          description: '${{ env.GITHUB_COMMIT_MSG }}'
          skaffold_file: 'config/skaffold.yaml'

      - name: 'Report Cloud Deploy release'
        run: |-
          echo "Created release ${{ steps.release.outputs.name }} "
          echo "Release link ${{ steps.release.outputs.link }} "

まとめ

GKE や Cloud Run 向けのコンテナ周りの CI/CD の実装として Github Actions + Cloud Deploy を試してみました。Cloud Deploy をあらかじめ設定できていれば、Github Actions からスムーズに呼び出せることがわかりました。

コンテナ周りの CI/CD の選択肢は色々ある中で CD として Cloud Deploy を中心にした時は CI を Github Actions を寄せやすくなりましたね。

さいごに

AWS と Google Cloud で構築したデータ基盤の開発・運用に携わっているデータエンジニアです。5 年くらい携わっていて、この業務がきっかけで Google Cloud が好きになりました。

Google Cloud 関連の情報を発信をしています。

https://twitter.com/pHaya72

Discussion