げっとシステムログ

WEB開発メモ

Google Cloud Run にデプロイしてみる

CONTENTS
  1. なぜそんなことをするのか
  2. Cloud Run にデプロイ
  3. Secret Manager で機密情報を管理
  4. distroless イメージを使用してビルド
  5. CI によるデプロイ
  6. まとめ
  7. 参考資料

なぜそんなことをするのか

これまで GKE で K8s のクラスタにデプロイしていた。 Cloud Run はコンテナをそのまま走らせられるサービスなので、Cloud Run でもいいのではないかと考えた。 常時 Node を起動させておく GKE に比べて、実行時間で課金される Cloud Run のほうが安く済む可能性もある。

他の選択肢

  • Azure のコンテナインスタンス
  • Amazon にも似たようなのはあるはず(調べてないけど Fargate ってのがそうかも)

Google のサービスを選択したのは、単に GKE を使っていたから。 あと、サービスが https で公開されるので、証明書を自前で用意しなくていい、というのもポイントが高い。 (必須ではないので、Google だけではなく Asure や Amazon のサービスを使用してデプロイする方法も検討したい)

TOP

Cloud Run にデプロイ

Cloud Run にデプロイするのはドキュメントの通りにすれば特に問題なくデプロイできる。 大まかな流れは以下の通り。

  1. デプロイするイメージを用意
  2. gcr.io にイメージをアップロード
  3. アップロードしたイメージを指定してサービスを作成
  4. 払い出された URL にアクセスしてみて、ちゃんと動いていることを確認

デプロイごとに、どの程度トラフィックを流すか、という設定もできるようだが詳しくは調べていない。

TOP

Secret Manager で機密情報を管理

前のセクションでは、Cloud Run にデプロイする際、プラットフォームとして「フルマネージド」を選択した。 フルマネージドでは、機密情報へのアクセスには Secret Manager サービスを使用しなければならない。

フルマネージド以外の選択肢としては、GKE クラスタを立ててそこにデプロイする、というのがある。 こうすると K8s の Secret を mount できるようになる。 けど、GKE のクラスタを使用しないで Cloud Run したいのでこの方法は却下。

Secret Manager のドキュメントに書いてある通りにすれば問題なくアクセスできる。 大まかな流れは以下の通り。

  1. Secret Manager に Secret を登録
  2. クライアントライブラリで Secret にアクセス

Secret Manager の Secret 名は Cloud Run の環境変数で指定すればいい。

Secret にアクセスする際、以下の権限が必要。

  • secretmanager.versions.access

この権限を持ったサービスアカウントでデプロイする必要がある。

これでアプリケーションから機密情報にアクセスする方法が整った。

TOP

distroless イメージを使用してビルド

golang で書いているので、アプリケーションは scratch にコピーするだけで動く。 しかし、Secret Manager にアクセスする場合、scratch を使用すると動かない。 必要な証明書にアクセスできないため、Secret Manager への接続が失敗する。

このため、ベースイメージとして distroless を使用する必要がある。 Dockerfile は以下のようになる。 (後半の FROM distroless がここでは重要)

FROM golang:1.15.0-buster as builder
COPY . /build
WORKDIR /build
RUN : && \
  CGO_ENABLED=0 \
  GOOS=linux \
  GOARCH=amd64 \
  go build -a -o app main.go && \
  :

FROM gcr.io/distroless/static-debian10
COPY --from=builder /build/app /app
CMD ["/app"]

このサンプルでは gcr.io/distroless/static-debian10 を使用しているが、プロジェクトに合ったものを選択すればいい。

TOP

CI によるデプロイ

デプロイは CI 経由でやりたい。 やることは大きく分けて以下の2つ。

  1. イメージをビルドして gcr に push
  2. 新しいデプロイを作成

イメージをビルドして gcr に push

#!/bin/sh

build_main() {
  if [ ! -f "${GOOGLE_CLOUD_SERVICE_ACCOUNT_KEY_JSON}" ]; then
    echo "key file : GOOGLE_CLOUD_SERVICE_ACCOUNT_KEY_JSON is not exists"
    exit 1
  fi

  local host
  local project
  local image
  local version
  local tag

  host=${HOST} # asia.gcr.io

  cat $GOOGLE_CLOUD_SERVICE_ACCOUNT_KEY_JSON | docker login -u _json_key --password-stdin https://${host}

  project=${PROJECT}
  image=${IMAGE}
  version=${VERSION}

  tag=${host}/${project}/${image}:${version}

  docker build -t $tag . &&
  docker push $tag
}

build_main

必要な権限は以下の通り。

  • storage.buckets.get
  • storage.objects.create
  • storage.objects.delete
  • storage.objects.get
  • storage.objects.list

新しいデプロイを作成

#!/bin/sh

deploy_main() {
  if [ ! -f "${GOOGLE_CLOUD_SERVICE_ACCOUNT_KEY_JSON}" ]; then
    echo "key file : GOOGLE_CLOUD_SERVICE_ACCOUNT_KEY_JSON is not exists"
    exit 1
  fi

  local host
  local region
  local project
  local image
  local version
  local tag
  local account

  host=${HOST} # asia.gcr.io
  region=${REGION} # asia-northeast1

  project=${PROJECT}
  image=${IMAGE}
  version=${VERSION}

  tag=${host}/${project}/${image}:${version}

  service=${SERVICE}

  export HOME=$(pwd)

  cp "${GOOGLE_CLOUD_SERVICE_ACCOUNT_KEY_JSON}" .ci/google_cloud_service_account_key.json

  gcloud auth activate-service-account --key-file=.ci/google_cloud_service_account_key.json
  gcloud run deploy $service --image="$tag" --platform=managed --region="$region" --project="$project"
}

deploy_main

イメージのタグはビルドしたときと同じものにする。

必要な権限は以下の通り。

  • iam.serviceAccounts.actAs
  • run.services.create
  • run.services.delete
  • run.services.get
  • run.services.list
  • run.services.update

デプロイするときに、コンテナにサービスアカウントを関連付けるため iam.serviceAccounts.actAs が必要になる。

デプロイスクリプトでは、サービスアカウントの指定や環境変数の指定をやっていない。 最初のデプロイでこれらを指定するようにして、CI ではこれらの情報にアクセスしなくていいように、と考えた。

TOP

まとめ

Cloud Run でデプロイする方法をまとめた。 これで運用コストが下がるといいな。

TOP

参考資料

TOP