Cloud Run の運用と知見について

ke-no
Belong blog
Published in
14 min readJan 24, 2022

--

Belong inc の ke-no@ です。

以前、弊社 CTO の tty@ が 「Cloud Run の利用からの半年を振り返る」という Cloud Run のリリースから発展、Belong inc で Cloud Run を活用するに至るまでをまとめた記事を公開しました。

私も Belong inc で1年半ほど Cloud Run を触っていますが、とても素晴らしいプロダクトだと感じています。
実際に社内でも Cloud Run をベースとしたサービスは増えてきており、Belong inc としても注目のプロダクトになっています。

今回は実際に Cloud Run でのアプリケーション構築、運用を行うにあたって得られたいくつかの知見や難所、 Belong inc での対応方法などをいくつかご紹介しようと思います

前提として、当記事の内容は 2022年1月公開時点のものであり、各種リリースなどで最新の状態と異なる可能性がありますので、ご了承ください。

目次

  • Cloud Run へのデプロイと権限
  • Cloud Run サービスに割り当てられるID
  • リビジョンで使用する Service Account の運用
  • リビジョントラフィック移行時の注意点
  • コールドスタートと CPU freeze
  • Cloud Runでの特定文字を含む環境変数の利用
  • Secret Manager との連携
  • おわりに

Cloud Run へのデプロイと権限

Cloud Runへのデプロイはコンテナイメージ単位で行われ、デプロイの度にリビジョンというデプロイバージョンが作成されます。
リビジョンには使用されるコンテナイメージへのリンクや Port、Service Account他など様々な設定が個別で保持されています。
リビジョン詳細に記載されているコンテナイメージはデプロイ時に取得されたものがリビジョン単位で保持されるため、Container Registry や Artifact Registry などからイメージを消してしまった場合でも動作します。

※実際のコンテナライフライクルについては Google Cloud が公開している「Cloud Run 上のコンテナのライフサイクル」に詳細が記載されているのでそちらも合わせて確認ください。

公式ドキュメントの「コンテナ イメージのデプロイ」に記載の通り、Cloud Run のデプロイに必要な権限は下記のいずれかです。

  • オーナー(roles/owner)
  • 編集者(roles/editor)
  • Cloud Run 管理者ロール(roles/run.admin)とサービス アカウント ユーザー(roles/iam.serviceAccountUser) ロールの両方

この内、オーナーと編集者はプロジェクトレベルの権限になるため通常Developerが持つべきではありません。
実際の運用では Cloud Run 管理者とサービスアカウントユーザーを付与して作業を行うケースが多いと思います。(2つを含んだカスタムロールを作成するのも良いと思います)

デプロイされたリビジョンが認証ありに設定されている場合は、呼び出す際に下記の権限が必要になります。

  • Cloud Run 起動元(roles/run.invoker)

ローカルから呼び出しを行う際の一例として、上記の権限を付与した上で $ gcloud auth print-identity-token から JWT token が取得でき、これをリクエストの HTTP Header に Authorization: Bearer {your_token} の形で設定することでリクエストが行えるようになります。

Cloud Run サービスに割り当てられるID

Cloud Run ではサービス作成時にアクセス可能な URL が発行されますが、その形式は https://{service_name}-{id}-an.a.run.app となります。
この内 id の部分はサービスを作成するまで分からず、terraform のようなコードベースでインフラ構成管理を行なっている際に不都合が生じる場合があります。
(GAE の場合は https://{service_name}-dot-{project_name}.appspot.com の形式なので事前に推測することが可能です)

Belong inc では push type の PubSub との組み合わせなど、事前に URL を知りたいケースがあったため、事前にデモコンテナでサービスを作成してしまってから terraform に反映することで対応を行いました。

deploy image selection

※ デモコンテナは GCP 側で用意してくれているため、特に準備は必要ありません。
(画像の「サンプル コンテナでテスト」を押すとデモコンテナへの URL が挿入されます)

リビジョンで使用する Service Account の運用

Cloud Runで使用される Service Account は標準で Default Compute Engine Service Account を使用する設定になっています。
これは編集者(roles/editor)を持っており、大抵の場合で過剰な権限になってしまいます。

そのため Cloud Run のサービス単位で専用の Service Account を作成し、設定を変更するのがセキュリティ的に好ましいです。
この際、マイクロサービスなど複数のサービスが想定される場合は、各サービスごとで Service Account を分けるか、最低限プロジェクトレベルでの Run専用 Service Accountを使用するという方針で Belong inc は運用しています。

リビジョントラフィック移行時の注意点

デプロイされたリビジョンにはトラフィックを割り当てる必要があり、サービス内で合計が 100% になるように設定します。
トラフィックは特定のリビジョンに 100%、新しくデプロイされたリビジョンには 10% のみにして、残りは稼働中のリビジョンに割り当てるなど目的に応じて柔軟に設定することができます。

これらは Cloud Console -> Cloud Run -> サービス選択 -> 変更内容タブから設定を行うことができ、デプロイされているリビジョンの詳細も確認することができます。

revision and traffic (before)

※リビジョンが 3つ デプロイされており、その内「test-server-00004-zij」にトラフィックが 100% 割り当てられている状態

このトラフィックの設定を Cloud Console や gcloud コマンドで変更する場合、少し注意が必要になります。

traffic management

※上記画像の設定から1つ前の「test-server-00002-boj」にトラフィックを移行します。

revision and traffic (after)

※トラフィック移行後の表示

手動でのトラフィック移行後は画像の通り「(最新にルーティング)」の表示が無くなってしまいます。これは Cloud Run が直前で行なった設定・操作を保持するためです。
この状態で新しいデプロイを行なった場合、新しいリビジョンへトラフィックは割り当てられず、「test-server-00002-boj」が 100%のリクエストを受けるままになり、機能追加/修正をしたものをデプロイしたけれどトラフィックが当たらず、以前の挙動のままになってしまうなどのトラブルになってしまう場合があります。

最新リビジョンへの割り当てが自動で行われるように戻すためには下記の 2通りがあります。

  • $ gcloud run services update-traffic コマンドで --to-latest を指定
    (コマンドの詳細は公式ドキュメントを参照ください)
  • Cloud Console から新しいリビジョンをデプロイし、「このリビジョンをすぐに利用する」オプションにチェックを入れる

リリース不備でロールバックを行う際など、トラフィックを移行するケースも少なからずあると思いますが、これらを念頭に置いて作業をする必要があります。
Belong inc では本番デプロイ時に --no-traffic オプションを指定し、その後に $gcloud run services update-traffic でトラフィックを移行する運用になっています。
(また、これらは全て CI で行い、マニュアル作業はできるだけ行わない体制にしています)

Cold startと CPU freeze

Cold start

2019年11月に Cloud Run が GA になりましたが、GA になってからしばらくは min-instance の設定を行うことができませんでした。
冒頭で触れた弊社 CTO tty@ の記事でも触れられていますが、min-instance 導入前の Cloud Run でサービスを構築するにあたって、2~3s のコールドスタートは避けて通れない課題でした。

Belong inc では Health check 用のエンドポイントを作っておき、Gateway や Proxy などサービス到達前に通る箇所で バックエンドへのハートビートを行うことで事前にインスタンスを起動する運用をしていました。
(ハートビートを行うサービスのコールドスタート問題もあったため、そちらは GAE の min-instance を使用するなど、多少苦労していた印象です)

CPU freeze

コールドスタート自体は min-instance で防げるようになりましたが、Cloud Run にはリクエストが無い時間がしばらく続くと CPU をスタンバイ状態にする仕様があり、スタンバイからフル稼働までには大体 数百ms ~ 1s くらいのラグがあります。
また、この仕様により Cloud Run でのバックグラウンド処理は推奨されていませんでした。

2021年9月に CPU 割り当て機能が GA になり、従来通りのリクエスト中の CPU割り当ての他、インスタンス起動中は CPU をフル稼働にしておくことができるようになりました。
公式ドキュメントにも記載がありますが、この CPU 割り当て機能は下記のように使い分けることが推奨されています。

・受信トラフィックが散発的か、バーストまたは急増する場合は、リクエストの処理中にのみ CPU を割り当てることをおすすめします。

・受信トラフィックが安定して緩やかであれば、CPU を常に割り当てることをおすすめします。
https://cloud.google.com/run/docs/configuring/cpu-allocation

補足1: min-instance と CPU 割り当ての設定は恩恵も大きい分、コストもかかることに注意する必要があります。

補足2: 「Cloud Run の応答時間を最適化する3つの方法」に記載がありますが、コンテナイメージのサイズはコールドスタートに影響しません。
サイズは小さいほど良いですが、Cloud Run ではビルドやデプロイの時間を短縮するために行う最適化と捉えてください。

Cloud Runでの特定文字を含む環境変数の利用

Cloud Run では「Environment variables」に記載の通り、デフォルトでいくつかの環境変数が設定されています。

サービス個別の環境変数を設定する場合は gcloud run deployのオプションなどで指定する必要があります。
(詳細は「サービスの環境変数を設定する」を参照ください)

Cloud Run では環境変数を設定する際、標準で , を区切り文字として使用します。
そのままでは CSV 形式や JSON などのカンマを含む値を1つの環境変数に設定することができませんが、^@^などを変数設定時に使用することで一時的に区切り文字を変更することができます。
(e.g. --set-env-vars "^@^KEY1=value1,value2,value3@KEY2=...")

区切り文字についての挙動も公式ドキュメント内に記載がありますが、キャレット(^)で囲った値を区切り文字で使用することになるため、上記の例では@が環境変数の区切り文字となります。

Secret Manager との連携

2021年11月に Cloud Run から Secret Manager を使うための連携機能(公式ドキュメント)がリリースされました。
Secret Manager はとてもシンプルに機密情報を設定・取得できるため Belong inc でも重宝していますが、当機能のリリースまでは Secret Manager API /RPC を利用し、アプリケーションコードで明示的な取得をする必要がありました。

当機能を利用することで Secret からアプリケーション変数 へのバインドを自動で行ってくれるようになったため、コードでの記述を省略することができました。
Belong inc では環境変数へのバインドを主に利用していますが、挙動としては下記のようになります。

環境変数での利用

  • インスタンス起動時に Secret を取得する
  • 取得時に該当の Secret が存在しない場合はインスタンス起動に失敗
  • min_instance などで既に起動しているインスタンスがある場合は Secret を更新してもインスタンスが生きている間は古いバージョンを使用する

latest を使用するか、バージョンを固定するべきかは用途やプロジェクト方針によっても異なると思いますが、latest が扱いやすい印象です。

その他 Secret を扱う関係上、誤ってログなどで出力しないようにするなど依存しているライブラリの挙動なども含めた細心の注意は必要になりますが、これは Secret Manager や Cloud Run を使用するからというものではなく、常に意識をする必要があります。

補足1: Secret Manager の値を Cloud Run で取得する場合は、Secret Manager のシークレット アクセサー(roles/secretmanager.secretAccessor)の権限が必要になります。

--

--