Cloud RunとGCPの他サービスを使って簡単なシステムを作ってみました
こんにちは!オールアバウト の @y_hideshiです。
今回は、昨年GA(General Availability)された Cloud RunとGCPの各種サービスを連携させて簡単なシステムを構築、その際に得られた知見を記事にしたいと思います。
作成したシステム
今回作成したシステムは「GCP確約利用割引の期日を定期的にチェックして、期日の近いものをslackに通知する」というものです。
GCP確約利用割引は1年間または3年間の支払いを確約する代わりに、特定の量のvCPU、メモリ、GPU、ローカルSSDを割引価格で利用できる仕組みです
使用したサービス
※ 今回は連携するサービスの詳細については触れませんが、各サービスごとの採用理由やメリットなどを解説して行きたいと思います。
Cloud Run について
公式ページより
Cloud Run は、ステートレス コンテナを自動的にスケールするフルマネージド型のコンピューティング プラットフォームです。サーバーレスなのでインフラストラクチャ管理が一切不要で、最も重要な作業であるアプリケーション構築に集中できます。
記載の通りCloud RunはGCPが用意してくれた環境上に、自分で用意したコンテナイメージをデプロイすることができ、ネットワークの経路などはGCPが自動で用意してくれます。そのため、Dockerなどを用いて開発を行った後、すぐにデプロイをすることができます。また、GCPが管理するプラットフォームにデプロイするため、デプロイする環境自体のメンテナンスコストなども削減することできます。
では、どのようにデプロイするのでしょうか。
Cloud Runへのデプロイ
Cloud Runをデプロイするには、GCPのWeb画面、またはgcloud
コマンドを用いてデプロイすることができます。
Webからのデプロイ
Webからデプロイする際は下記の画像のように、デプロイするイメージや各種変数を設定することでCloud Run環境にコンテナをデプロイすることができます。コマンドの設定値については後ほど記載します。
gloudコマンドによるデプロイ
Cloud Runは gcloud run deploy コマンドでデプロイすることができます。 基本的にはWebからみたときと同じような項目をオプションとして設定してデプロイします。
gcloud コマンドのサンプル
gcloud run deploy cloud-run-service-name \ --image gcr.io/cloudrun/hello \ --platform managed \ --port 8080 \ --max-instances 2 \ --cpu 1 \ --memory 128Mi \ --region asia-east1 \ --allow-unauthenticated \ --service-account サービスアカウントのメールアドレス \ --set-env-vars ENVHOGE=hogehoge,ENVFUGA=fugafuga
設定値について
「web画面での設定項目」と「gcloud コマンドのオプション」において、自分が使用した主要なオプションの対応表を作ってみました。
Web | gcloud | 説明 |
---|---|---|
サービス名 | 第一引数 | デプロイされるときの名前。生成されるURLなどにも利用されます |
デプロイメントプラットフォーム | --region | コンテナが動作するリージョンを指定します |
認証 | --[no-]allow-unauthenticated | コンテナへのアクセス時に認証を儲けることができます |
コンテナポート | --port | コンテナリクエストを流す際のポートを指定することができます |
サービスアカウント | --service-account | 作成されるリビジョンで使用されるID ※1 |
自動スケーリング | --max-instances | 自動スケーリング時の最大インスタンス数 |
割り当てられているCPU | --cpu | コンテナ実行時に割り当てるCPU |
割り当てられたメモリ | --memory | コンテナ実行時に割り当てるメモリ |
VARIABLES | --set-env-vars | コンテナに登録する環境変数 |
※1 サービスアカウントを設定することで、こちらのページ Understanding service accounts で記載されている通り、GCPの中で認証を行い権限さえ割り当てていればリソースにアクセスすることができます。
メモリやCPU、自動スケーリングの値を設定する理由としては、意図しない大量のアクセスが来た際に大量のCPU, メモリを使用してしまいお金が余分にかからないようにするためです。
コンテナ インスタンスで次の処理が行われたときに請求対象時間が開始します。 ・コンテナ インスタンスが開始したとき ・コンテナ インスタンスで 1 つ以上のリクエストが処理されたとき コンテナ インスタンスでリクエストがアクティブな間に割り当てられた CPU とメモリが請求対象になります。時間は 100 ミリ秒単位で切り上げられます。 次の図のように、コンテナ インスタンスが同時に多くのリクエストを受信した場合、請求対象時間は、最初のリクエストの処理が始まってから、最後のリクエストの処理が完了するまでです。
Cloud Run:まとめ
記載した通りCloud Runでは、開発したイメージをGCPマネージド環境にデプロイすることで自動的にスケーリングやネットワーク経路を用意してもらえます。 また、Cloud Runではデプロイされるリビジョンごとにサービスアカウントを設定することができ、コンテナ内でGoogle APIを実行する際には指定したサービスアカウントを利用することができます。 そのため、イメージに機密情報を含むことなく簡単に認証を行うことができます。 今回は「GCPの確約利用割引の期日を取得する」「Pub/Subにメッセージを公開する」といったGCPリソースを操作する処理を行なっており、これらの処理はgo言語で記述しGoogleのパッケージを利用していたので、この機能は非常に役立ちました。
関連サービスを利用することによるメリット
これまでCloud Runについて話してきましたので、これからはCloud Runと別のサービスを組み合わせることのメリットについて話していきます。
Cloud Build
オールアバウトでは通常CI/CDツールにCircleCIを使用しています。 しかし、今回は下記の理由からCloud Buildを利用しました。
- GCR、Cloud Runへのデプロイをするための認証をなるべく手軽に済ませたい
- プロジェクトが変わってもすぐにデプロイしやすいようにしたい
GCR、Cloud Runへのデプロイをするための認証をなるべく手軽に済ませたい
Cloud Buildでは、Cloud Build専用のサービスアカウントが存在しており、Buildを実行する際にはこのサービスアカウントが cloudbuild.yaml
に記述された処理を実行します。
Cloud Buildのサービスアカウントは下記の画像のように簡単に権限を設定できるため、手軽に権限設定 & 認証を行うことができるのに加え、機密情報をGoogle側に持たせることができました。
プロジェクトが変わってもすぐにデプロイしやすいようにしたい
GCPの別プロジェクトでも同様のbuildを行いたい、といったときにプロジェクトIDは変数化されGoogle側がプロジェクトごとに保持しているので設定する変数やyamlの行数を少なくすることができました(Cloud Buildにて予め設定されている変数一覧)。
また、Cloud Runで指定するサービスアカウントは、同じプロジェクトにサービスアカウントが存在する必要があります。Cloud Buildでは、トリガーと呼ばれる実行条件ごとに変数を用意することができるので、プロジェクトに依存するものはシステムのコード外(今回はトリガー)に書き出して移植しやすいようにしました。
cloudbuild.yamlのsample
- name: 'gcr.io/cloud-builders/gcloud' args: ['run', 'deploy', '${_CLOUD_RUN_SERVICE_NAME}', '--image', 'asia.gcr.io/$PROJECT_ID/${_CLOUD_RUN_SERVICE_NAME}:latest', '--platform', 'managed', '--port', '8080', '--max-instances', '2', '--memory', '128Mi', '--region', '${_CLOUD_RUN_SERVICE_REGION}', # _CLOUD_RUN_SERVICE_ACCOUNT_EMAIL: cloudbuild triggerで設定しています '--service-account', '${_CLOUD_RUN_SERVICE_ACCOUNT_EMAIL}', '--no-allow-unauthenticated', '--set-env-vars', 'NOTIFY_SLACK_WEBHOOK_URL=${_NOTIFY_SLACK_WEBHOOK_URL},TOPIC_PROJECT=$PROJECT_ID,TOPIC_NAME=${_TOPIC_NAME}'] # check deploy service - name: 'gcr.io/cloud-builders/gcloud' args: ['run', 'services', 'list', '--platform', 'managed', '--region', '${_CLOUD_RUN_SERVICE_REGION}'] timeout: 600s substitutions: _CLOUD_RUN_SERVICE_NAME: cloud-run-service-name _CLOUD_RUN_SERVICE_REGION: asia-east1 _NOTIFY_SLACK_WEBHOOK_URL: slackのIncommingWebhookURL _TOPIC_NAME: slack-topic
Cloud Pub/Sub
Pub/Subを挟むことによるメリットは下記の通りです。
- サービスアカウントを利用することで、認証をGCPに任せることができる
Pub/SubからHTTPリクエストを送信する際、サービスアカウントを指定することができ、下記のような構成をとることができます。
- Cloud Runでは認証必須にすることでアクセス制限を行う
- HTTPリクエスト送信時に指定したサービスアカウントに「Cloud Run起動者」と言うCloud Runを起動できる権限をつけ認証を通す
上記のように、作成するシステムに認証機能を持たせることなく、簡単に認証ありのセキュアなシステムを作ることができるので、機能の開発に集中できました。(自分のために便利システムを作ったと思ったら、そのシステムがセキュリティ的によろしくないものになっていたら嫌ですよね。)
また、Pub/Subを挟むことで裏側の処理が切り替わった時もHTTPリクエスト送信先を切り替えるだけで良いので、パブリッシャー(メッセージを公開する)側のコードに修正を加える必要がありません。
Cloud Scheduler
Schedulerを利用したメリットは下記の通りです。
期日を定期的にチェックするためにチェック処理を自分でキックしたくないのに加えて、cron用のサーバなどは管理したくなかったのでフルマネージドのcronジョブスケジューラを利用しました。 Cloud SchedulerもPub/Subと同様にサービスアカウントを指定することができ、セキュアな状態でシステムを実行することができます。
所感
GCP内で処理が完結していたので、各種サービスを利用し認証機能を持たせたシステムを簡単に実装することができました。 個人的にとても嬉しかったのは認証に必要な情報を全てGoogle側に持たせることができ、こちら側で管理する情報が少ないことでした。機密情報を扱うためにCI/CDツールなどに情報が集中するのは避けたかったので、今回はその辺りがシンプルにできてよかったなあと思っています。
また、今回作成したリソース「サービスアカウント」「Cloud Build」「Cloud Pub/Sub」「Cloud Scheduler」は全てTerraformで作ってみました。そうすることで、GCPの各種サービスの理解にもつながり、プロジェクトが異なる場合でも簡単に同じリソースを作成できる移植性が高いものができました。
今度はCloud RunとCloud DNSを利用して、もっと色々遊んでいきたいですね。
閲覧ありがとうございました!今後ともよろしくお願いいたします。