Cloud Runの自動デプロイを試してみた

こんにちは、青木です。
最近Cloud Runが盛り上がっているようですので、実際に触ってみました。

この記事に書いてあること

本記事を通して、最終的に以下を実現できるようになると思います。

  • Cloud Runでサービスを稼働させる
  • DockerイメージをArtifact Registryで管理する
  • Cloud Runの自動デプロイをする
ソースコードの詳細は記載しておりません。

使用するサービス

今回使用するGoogle Cloudのサービスは以下です。

  1. Cloud Run
  2. Cloud Source Repositories
  3. Artifact Registry
  4. Cloud Build

Cloud Source Repositoriesはソースコードの管理に使用します。
Artifact RegistryはDockerイメージの管理に使用します。
Cloud BuildはDockerイメージのデプロイに使用します。

それぞれサービスの詳細はここでは控えますが気になる方はドキュメントを参照いただけばと思います。

環境変数の作成

本日の作業はコマンドラインで行い、確認はコンソールでやっていきたいと思います。
最初に環境変数をセットしておきます。

IDEはCloud Shellを利用しました。

PROJECT=[project id]
REGION=asia-northeast1
REGISTRY_NAME=aoki-images
IMAGE_NAME=test
SOURCE_REPO=cloud-run-sample
TRIGGER_NAME=aoki-test-trigger

Artifact Registryの準備

Dockerイメージを管理するためのArtifact Registryを作っていきたいと思います。
今回はaoki-imagesという名前のRegistryを作ります。

gcloud artifacts repositories create $REGISTRY_NAME --repository-format=docker --location $REGION --description="Docker repository" --project $PROJECT

サービスの有効化を求められた場合は、許可します。

Would you like to enable and 
retry (this will take a few minutes)? (y/N)?  y

 無事作成されました。

Cloud Source Repositoriesの準備

ソースコードを管理するためのCloud Source Repositoriesを作っていきたいと思います。
今回はcloud-run-sample という名前のリポジトリを作ります。

gcloud source repos create $SOURCE_REPO --project $PROJECT

無事に作成されました。

ローカルにクローンします。

gcloud source repos clone $SOURCE_REPO --project $PROJECT

クローンしたローカルリポジトリに移動して認証します。

cd cloud-run-sample
gcloud init

ローカルリポジトリにファイルを追加します。

- main.py
- requirements.txt
- .dockerignore
- Dockerfile

main.py

import os
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello_world():
    name = os.environ.get("NAME", "World")
    return "Hello {}!".format(name)
if __name__ == "__main__":
    app.run(debug=True, host="0.0.0.0", port=int(os.environ.get("PORT", 8080)))

requirements.txt

Flask==2.1.0
gunicorn==20.1.0

.dockerignore

Dockerfile
README.md
*.pyc
*.pyo
*.pyd
__pycache__
.pytest_cache

Dockerfile

FROM python:3.10-slim
ENV PYTHONUNBUFFERED True
ENV APP_HOME /app
WORKDIR $APP_HOME
COPY . ./
RUN pip install --no-cache-dir -r requirements.txt
CMD exec gunicorn --bind :$PORT --workers 1 --threads 8 --timeout 0 main:app

リモートリポジトリにファイルをPushしておきます

git add .
git commit -m "first commit"
git push origin master

リポジトリが更新されたことを確認します。

Cloud Buildにトリガーを仕込む

やっと本題です。
Dockerイメージの自動デプロイを実現するにはリポジトリへのPushイベントを拾う必要があります。
Google CloudではCloud Buildでトリガーさせることができます。

今回は、aoki-test-triggerという名前のトリガーを作っていきたいと思います。

gcloud beta builds triggers create cloud-source-repositories --name=$TRIGGER_NAME --repo=$SOURCE_REPO --branch-pattern="^master$" --dockerfile="Dockerfile" --dockerfile-dir="" --dockerfile-image=asia-northeast1-docker.pkg.dev/$PROJECT/$REGISTRY_NAME/$IMAGE_NAME:latest --project $PROJECT --region $REGION

トリガーの確認

トリガーの中身ですが、イメージ名に「asia-northeast1-docker.pkg.dev」が入力されてほしいのですが、なぜか「gcr.io」になってしまいます・・・
今回は手で修正しました。

Dockerイメージをビルド

ではDockerイメージをビルドしていきたいと思います。
main.pyを少し編集して(例:Hello Workd→Hello World!!)から、Pushします。

git add .
git commit -m "build image test"
git push origin master

ビルドの結果を見ると、無事成功しました

Cloud Build

Artifact Registry

成功したらトリガーを消しておきます。

gcloud beta builds triggers delete $TRIGGER_NAME --project $PROJECT --region $REGION

ここまでの課題

ここまでで、ソースコードをPushすることで、Dockerイメージをビルドするところまでいけました。
しかし、実際はDockerイメージをビルドして、Cloud Runで稼働させるところまでいきたいですよね。

Cloud Runへ自動デプロイする

Dockerイメージを実行する環境としてCloud Runを使用してみたいと思います。

Artifact Registryに保管されたコンテナを起動してみる

まずは先程保管したものをCloud Runで起動してみたいと思います。

gcloud run deploy helloworld --image asia-northeast1-docker.pkg.dev/$PROJECT/$REGISTRY_NAME/$IMAGE_NAME:latest --allow-unauthenticated --platform managed --region $REGION --project $PROJECT

Cloud Runが稼働したことを確認します。

Cloud Run

Cloud Runのエンドポイント

 Build→Push→Deployを続けてやってみる

単純にデプロイするのは上記のとおりですが、自動デプロイは一工夫いります。
自動デプロイされたDockerイメージをCloud Run環境にデプロイするにはCloud Buildのトリガー内容を変更します。
流れの処理を行うために、以下のファイルを作成します。

YAML形式でステップを定義しており、イメージのビルド、プッシュ、最後にデプロイをするように記載しております。

cloudbuild.yaml

steps:
  # Build the container image
  - name: 'gcr.io/cloud-builders/docker'
    args: ['build', '-t', 'asia-northeast1-docker.pkg.dev/[project_name]/aoki-images/test:latest', '.']
  # Push the container image to Artifact Registry
  - name: 'gcr.io/cloud-builders/docker'
    args: ['push', 'asia-northeast1-docker.pkg.dev/[project_name]/aoki-images/test:latest']
  # Deploy container image to Cloud Run
  - name: 'gcr.io/google.com/cloudsdktool/cloud-sdk'
    entrypoint: gcloud
    args: ['run', 'deploy', 'helloworld', '--image', 'asia-northeast1-docker.pkg.dev/[project_name]/aoki-images/test:latest', '--allow-unauthenticated', '--region', 'asia-northeast1', '--tag', 'blue']
images:
  - asia-northeast1-docker.pkg.dev/[project_name]/aoki-images/test:latest

ファイルを作成したら、以下を実行し、新しくトリガーを作成します。

gcloud beta builds triggers create cloud-source-repositories --name=$TRIGGER_NAME --repo=$SOURCE_REPO --branch-pattern="^master$" --build-config="cloudbuild.yaml" --project $PROJECT --region $REGION

権限の付与

Cloud Runにデプロイにする前に、Cloud Build→Cloud Runでデプロイできるように権限を付与します。

Cloud Build – 設定

Cloud Run管理者を有効にします。

Let’s 自動デプロイ!

ではちゃんと動くか試したいと思います。
main.pyを少し編集してから、Pushします。

git add .
git commit -m "deploy Cloud Run test"
git push origin master


セキュリティ対策

続いて、ビルドされたコンテナイメージをスキャンしてセキュリティを高めてみます。

Artifact Registryにはイメージのセキュリティスキャン機能が備わっていますので、今回はそちら利用していきたいと思います。
こちらの操作にはオンデマンド管理者のロールが必要となるので、プロジェクト内のcloudbuild.gserviceaccount.comのサービスアカウントに権限を付与します。

また、APIの有効化が必要ですので以下にアクセスし有効化しておきます。

Google Cloud Platform
Google Cloud Platform lets you build, deploy, and scale applications, websites, and services on the same infrastructure ...

cloudbuild.yamlを以下のように変更します

steps:
  # Build the container image
  - name: 'gcr.io/cloud-builders/docker'
    args: ['build', '-t', 'asia-northeast1-docker.pkg.dev/[project_name]/aoki-images/test:latest', '.']
  - id: scan
    name: gcr.io/google.com/cloudsdktool/cloud-sdk
    entrypoint: /bin/bash
    args:
    - -c
    - |
      gcloud artifacts docker images scan asia-northeast1-docker.pkg.dev/[project_name]/aoki-images/test:latest \
      --format='value(response.scan)' > /workspace/scan_id.txt
  - id: severity check
    name: gcr.io/google.com/cloudsdktool/cloud-sdk
    entrypoint: /bin/bash
    args:
    - -c
    - |
      gcloud artifacts docker images list-vulnerabilities $(cat /workspace/scan_id.txt) \
      --format='value(vulnerability.effectiveSeverity)' | if grep -Fxq $_SEVERITY; \
      then echo 'Failed vulnerability check' && exit 1; else exit 0; fi
    # Push the container image to Container Registry
  - name: 'gcr.io/cloud-builders/docker'
    args: ['push', 'asia-northeast1-docker.pkg.dev/[project_name]/aoki-images/test:latest']
  # Deploy container image to Cloud Run
  - name: 'gcr.io/google.com/cloudsdktool/cloud-sdk'
    entrypoint: gcloud
    args: ['run', 'deploy', 'helloworld', '--image', 'asia-northeast1-docker.pkg.dev/[project_name]/aoki-images/test:latest', '--region', 'asia-northeast1', '--allow-unauthenticated', '--region', 'asia-northeast1', '--tag', 'blue']
images:
  - asia-northeast1-docker.pkg.dev/[project_name]/aoki-images/test:latest

これを実行することで、コンテナイメージの脆弱性検知ができるそうです。
いいですね。

git add .
git commit -m "security scan test"
git push origin master

SCANは以下のようにscanステップで確認できます。

成功したらトリガーを消しておきます。

gcloud beta builds triggers delete $TRIGGER_NAME --project $PROJECT --region $REGION

承認プロセスを追加

ここまで自動デプロイしてしまいましたが、
本番環境へのデプロイは承認プロセスを追加したいものですね。
以下のように新しいトリガーを作成し、承認プロセスを追加してみたいと思います。

gcloud beta builds triggers create cloud-source-repositories --name=$TRIGGER_NAME --repo=$SOURCE_REPO --branch-pattern="^master$" --build-config="cloudbuild.yaml" --project $PROJECT --require-approval --region $REGION
git add .
git commit -m "approval test"
git push origin master

承認はCloud Buildのダッシュボードから確認ができます。

Blue/Greenデプロイに変えてみる

さらに、本番環境にデプロイすることを考えると、段階的なデプロイにしてみたいですね。
Cloud Runでの段階的なデプロイにチャレンジしてみたいと思います。

既存バージョンをBlue、新しいバージョンをGreenと前提とし、新しいバージョンをデプロイするために以下のようにcloudbuild.yamlを書き換えます。

steps:
  # Build the container image
  - name: 'gcr.io/cloud-builders/docker'
    args: ['build', '-t', 'asia-northeast1-docker.pkg.dev/[project_name]/aoki-images/test:latest', '.']
  # Push the container image to Container Registry
  - name: 'gcr.io/cloud-builders/docker'
    args: ['push', 'asia-northeast1-docker.pkg.dev/[project_name]/aoki-images/test:latest']
  # Deploy container image to Cloud Run
  - name: 'gcr.io/google.com/cloudsdktool/cloud-sdk'
    entrypoint: gcloud
    args: ['run', 'deploy', 'helloworld', '--image', 'asia-northeast1-docker.pkg.dev/[project_name]/aoki-images/test:latest', '--allow-unauthenticated', '--region', 'asia-northeast1', '--no-traffic', '--tag', 'green']
images:
  - asia-northeast1-docker.pkg.dev/[project_name]/aoki-images/test:latest

ポイントは新しいバージョンにタグを追加し、no-trafficオプションを指定しているところです。
これがデプロイされてもトラフィックは転送されません。

ではデプロイしてみます。

git add .
git commit -m "blue/green test"
git push origin master

新しいバージョンにデプロイできましたね。
しかしこれでは新しいバージョンのテストができないですよね・・・

実は新しいバージョンをテストしたい場合は以下のようなFQDNでアクセスできます。tagname-がFQDNにつくようです。

404 Page not found

新しいバージョンが正しく操作することが確認できた際は、少しづつトラフィックを増やしていきたいですね。
新しいバージョンにトラフィックを寄せていく方法は以下です。

gcloud beta run services update-traffic helloworld --to-tags green=10 --region $REGION --project $PROJECT

成功したらトリガーを消しておきます。

gcloud beta builds triggers delete $TRIGGER_NAME --project $PROJECT --region $REGION

リソースの削除

最後に作成したリソースを削除しておきたいと思います。
リポジトリを削除します。

gcloud source repos delete $SOURCE_REPO --project $PROJECT

Artifact Registryを削除します。

gcloud artifacts repositories delete $REGISTRY_NAME --location $REGION --project $PROJECT

Cloud Runのサービスを削除します。

gcloud run services delete helloworld --region $REGION --project $PROJECT

さいごに

今回はCloud Runにリリースする方法を調べてみました。
次回はGKEと絡めてみたいな、と思っています。

ありがとうございました。

タイトルとURLをコピーしました