[GCP]Cloud Run services

[公式ドキュメント](https://cloud.google.com/run/docs/overview/what-is-cloud-run?hl=ja)をベースに自分の言葉でまとめた記事。
初稿は 1,2 年前に限定共有で作成し、継ぎ足し継ぎ足しで書いてきたので、ちょっとおかしい点あるかもしれませんがご了承ください。

注意
[Cloud Run jobsはまた別のGCPサービスです](https://qiita.com/WisteriaWave/items/d7507510840eea141ece)。

# 1. 概要
**フルマネージドでサーバレス**なコンテナサービス。Kubernetesの機能を気軽に利用できる。
主な用途は API, Web アプリケーション、ストリーミング。
Webサーバの役割( HTTP / gRPC 対応)は Cloud Run が吸収してくれる。
デプロイは数秒で完了、スケーリングもいい感じに自動でやってくれる

## リソースモデル

|![x](https://cloud.google.com/static/run/docs/images/resource-model.svg?hl=ja)|
|---|
|出典: [リソースモデル](https://cloud.google.com/run/docs/resource-model?hl=ja)|

リビジョンとそれらを束ねるサービスから成り立つ。外部からはサービスに対してアクセスする形になる。


### サービス
複数のリビジョンを束ねる単位である。
外部から通常利用するためのエンドポイント URL は、このサービスの単位で発行される。
サービスにリクエストが届くと、配下のリビジョンにトラフィックが振り分けられる。

### リビジョン
コンテナイメージをサービスへデプロイする際に作成されるもの。
「コンテナ化するイメージ・コンピューティングリソース・スケーリング等の設定が適用されたインスタンス」であり、内部には1つ以上のコンテナが含まれる。
Terraform でリソースの設定を反映したり、`gcloud run deploy` 実行するごとにリビジョンが作成される。
作られたリビジョンは世代管理され、過去のものも残る。
Cloud Run サービスでは、リビジョン単位でスケーリングされる。

#### トラフィックの制御
外部からのサービスへのリクエストに対して、そのトラフィックをどのリビジョンに何％流すかという設定ができる。
これにより、トラフィックをリビジョン間で分割しながら段階的にロールアウトするといったことができる。

##### タグ付き URL
リビジョンにはタグを付与することができる。
タグを付与するとタグ含みのエンドポイント URL が発行される(タグ付きURL)。
タグ付きURLは、サービスへのトラフィックを新しいリビジョンにルーティングしなくても利用できる。
ルーティング前に新しいタグ付きリビジョンでテストして問題ない → そのリビジョンを正式にトラフィック100%でルーティングしてリリースするということができる(ブルーグリーンデプロイ)。

#### オートスケーリングのロジック
スケーリングのロジックの詳細はわからないが、そのトリガーとなる要素は公開されている。

https://cloud.google.com/run/docs/about-instance-autoscaling?hl=ja

## IAM によるアクセス制御
サービスを実行するためには、その サービスリソースに対する Cloud Run 起動元(`roles/run.invoker`)というロールが付与された[プリンシパル](https://qiita.com/WisteriaWave/items/a52661732abbd56a889d#%E3%83%97%E3%83%AA%E3%83%B3%E3%82%B7%E3%83%91%E3%83%AB:~:text=%E7%A2%BA%E8%AA%8D%E3%81%99%E3%82%8B%E3%81%A8%E3%82%88%E3%81%84%E3%80%82-,%E3%83%97%E3%83%AA%E3%83%B3%E3%82%B7%E3%83%91%E3%83%AB,-%E4%BB%A5%E4%B8%8B%E3%81%AE%E7%B7%8F%E7%A7%B0)の ID トークンを リクエストヘッダ `Authorization: Bearer` にセットしてリクエストする必要がある。
ただし、`roles/run.invoker` を `allUsers` に付与しておく場合は ID トークンが不要になる。

これらにより、**サービスの外部への公開 / 非公開 を制御できる**。

以下の公式ドキュメントに書かれていることををまとめると、上記の結論が見えてくる。
明示的に述べられてはいないため、非常にわかりづらい・・・

https://cloud.google.com/run/docs/authenticating/overview?hl=ja
https://cloud.google.com/run/docs/authenticating/public?hl=ja
https://cloud.google.com/run/docs/authenticating/developers?hl=ja
https://cloud.google.com/run/docs/authenticating/service-to-service?hl=ja

### 外部非公開状態のサービスの実行に必要なこと
1. カスタムオーディエンスの設定 (サービスの URL をカスタムドメインにしている場合は、こちらをカスタムドメインの URL にしておくと混乱がなくてよい)
2. サービスへリクエストを送信するプリンシパルに `roles/run.invoker` を付与しておく
3. サービスへリクエストを送信する直前に、そのプリンシパルの ID トークンを発行する
    - ID トークンの発行については、[こちら](https://qiita.com/WisteriaWave/items/140f388b329d18f9a599)を参照
    - Cloud Run で使う ID トークンの発行では、オーディエンスに「サービスのデフォルトの URL」 をセットするか、あらかじめサービスに設定した「カスタムオーディエンス」をセットして発行する。
4. 以下のリクエストヘッダのどちらかにセットして、サービスへリクエストを送信する。
    - `Authorization: Bearer`
    - `X-Serverless-Authorization: Bearer`

参考: 
- [サービス間認証](https://cloud.google.com/run/docs/authenticating/service-to-service?hl=ja#run-service-to-service-example-go)
- [アプリケーションのデフォルト認証情報の仕組み](https://cloud.google.com/docs/authentication/application-default-credentials?hl=ja)


# 2. 制限事項
コンテナサービスとして Cloud Run を利用する上での主な制限事項。
公開するサービスの要件にこれらが邪魔になってしまうのであれば、k8sの知識が必要になる分ハードルは高くなるがGKE を使うことを検討した方がよい。

**コンテナイメージ内の実行ファイルのフォーマット**
- Linux x86_64(amd64)
 
**コンテナ内アプリケーションのリッスンポート**
- 外部から渡されたリクエストを処理するために Cloud Run の環境変数``PORT``に設定している値をリッスンする必要がある。デフォルト値は 8080。

**Cloud Run からのレスポンスタイムアウト**
- デフォルト 5 分、Max 60 分で設定可能。タイムアウト時は504エラーになる

**Cloud Run から外部へのリクエストタイムアウト**
- VPC へのリクエスト: 10 分
- インターネットへのリクエスト: 20 分

**コンテナ内のファイル保存**
- ファイルシステムはインメモリであり、コンテナが停止すると書き込まれたデータは破棄される

~~**1コンテナインスタンスあたり1コンテナしかデプロイできない**~~
~~- Cloud Run は GKE のようなコンテナオーケストレーションツールではない~~
(マルチコンテナ構成が 2023 年 11 月より GA になった)

**1コンテナインスタンスあたりのスペック**
(第2世代の場合)

CPU(コア数)|メモリ
---|---
1|512 MB〜4 GB
2, 3|1 GB〜8 GB
4, 5|2 GB〜16 GB
6, 7|4 GB〜32 GB
8|4 GB〜32 GB


https://cloud.google.com/run/docs/configuring/services/cpu?hl=ja

https://cloud.google.com/run/docs/configuring/services/memory-limits?hl=ja

**1コンテナインスタンスあたりの同時リクエスト受付数**
- デフォルト値 80、最大 1000 まで設定可能

https://cloud.google.com/run/docs/about-concurrency?hl=ja


**1 リビジョンのコンテナインスタンス数のオートスケーリング設定可能幅**
- 最小: 0
- 最大: リージョン単位でベースの値が定められている。Googleにお願いして上限突破可能

https://cloud.google.com/run/docs/about-instance-autoscaling?hl=ja

https://cloud.google.com/run/docs/configuring/max-instances?hl=ja#limits


**リビジョンインスタンスのオートスケーリング設定で細かく条件を設定できない**
先述の通り、詳細なロジックは不明である。


# 3. 設定項目
Cloud Run サービスにサービスを追加する際に、開発者が設定することになる項目。

- サービス名
- サービスを作成するリージョン
- サービスの説明
- サービスへのアクセスに関する制限(外部から完全にOK/LBを介する/内部リソースからのみ、IAM認証ユーザー/全ユーザー)
- リビジョンへのトラフィック制御方法(100%最新のリビジョンへ流す or 指定した複数のリビジョンへ流す)
- リビジョンのテンプレート設定(以下)
    - リビジョン名
    - リビジョンのインスタンス数の範囲(オートスケール)
    - リビジョンから別のGCPリソースへのトラフィックの転送方法
    - レスポンスタイムアウト値
    - 他のGCPリソースを操作する際のサービスアカウント
    - 1リビジョンが管理するコンテナの設定(複数コンテナを設定可能ではある)
    - コンテナ化するイメージのURL
    - エントリポイントの実行コマンド
    - エントリポイントに渡す引数
    - 環境変数のリスト(Secretの値も環境変数として読み込ませることが可能)
    - CPU数(メモリ設定と相互に制約あり)
    - メモリ容量(CPU数設定と相互に制約あり)
    - 常時CPUを割り当てさせるか(コールドスタート拒否するか)
    - コールドスタートに備えたCPUブースト設定するか
    - コンテナへ転送するポートのプロトコル(HTTP or HTTP2)とポート番号
    - コンテナの死活監視の設定
    - マウントする他リソースのストレージ


# 4. 使ってみたその1
「[Cloud Run に Go サービスをデプロイする](https://cloud.google.com/run/docs/quickstarts/build-and-deploy/deploy-go-service?hl=ja)」をベースに、「Goサービスの[ソースコードからのデプロイ](https://cloud.google.com/run/docs/deploying-source-code?hl=ja)」をしてみる

**前提**
- Macでの作業である
- GitHubアカウントがある
- 「Google Cloudの無料トライアル」に申し込みが完了しており、「Google Cloudコンソール」へログイン済みである

## 4.1. 動作確認用のGoogle Cloudプロジェクトを作成する
①グローバルナビゲーションのプルダウン(My First Projectと書かれているところ)を押下
②「プロジェクトの選択」ダイアログで「新しいプロジェクト」を押下
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59542/b29fd176-8d0d-6538-6307-dcbd7884961c.png)
③任意のプロジェクト名を入力して「作成」を押下
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59542/d61fda6e-07dd-9f8b-e648-a89ffa878b74.png)
→通知で緑のチェックがつけば作成完了
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59542/45204700-782d-7624-2110-4f3b8e86ae38.png)

## 4.2. Google Cloud CLIをインストールする
以下に従ってインストールする

https://cloud.google.com/sdk/docs/install?hl=ja

**メモ**
```
Pick cloud project to use:
```
では、4.1.で作ったプロジェクトの番号を入力する。
プロジェクトIDはグローバルナビゲーションのプルダウン押下で表示される「プロジェクトの選択」ダイアログで確認可能

## 4.3. 動作確認用のGoモジュールを作成

以下を参考に、動作確認用のGoモジュールを作成する

https://qiita.com/WisteriaWave/items/60a1052981131f95fbf6#3-%E3%83%A2%E3%82%B8%E3%83%A5%E3%83%BC%E3%83%AB%E3%81%AE%E4%BD%9C%E6%88%90%E6%89%8B%E9%A0%86

①GitHubへ「cloud_run_test」という名前のリポジトリを作成
②``git clone``してローカルリポジトリを作成
③ローカルリポジトリの作業用ディレクトリでmain.go作成(中身は[これ](https://github.com/GoogleCloudPlatform/golang-samples/blob/HEAD/run/helloworld/main.go)をコピペ)
④``go mod init github.com/<オーナー名>/cloud_run_test``してgo.modファイルを作成

## 4.4. ソースコードからのデプロイをする

①``cd <作業用ディレクトリのパス>``で4.3.で作成したGoモジュールの作業用ディレクトリへ移動する
②``gcloud run deploy``でソースコードからのデプロイを実行する(色々問われるので回答して続けていく)

- Service name: Cloud Runへ作成するサービス名をどうするか → 任意。デフォルトのままでいい
- Please specify a region: Cloud Runサービスのリージョンをどうするか → 任意。とりあえず3番の東京リージョンに
- API [artifactregistry.googleapis.com] not enabled on project [<Google Cloudプロジェクト番号>]. Would you like to enable and retry (this will take a few minutes)?: Artifact RegistryのAPIが、デプロイ対象のGoogle Cloudプロジェクトで有効化されていない。有効化して続行するか → Y
- Allow unauthenticated invocations to [<Cloud Runサービス名>]: 未認証の呼び出しを許可するか → y
- API [cloudbuild.googleapis.com] not enabled on project [<Google Cloudプロジェクト番号>]. Would you like to enable and retry (this willtake a few minutes)?: Cloud BuildのAPIがデプロイ対象のGoogle Cloudプロジェクトで有効化されていない。有効化して続行するか → y

このコマンドの実行により、実際には以下のような感じで処理されていくっぽい(ちょっと違う部分はあるかも)

![0_IMG_8213.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59542/cf045030-c249-6faf-f02d-ae37c24cf1b9.jpeg)






→完了すると以下のようにターミナル表示される
```
Building using Buildpacks and deploying container to Cloud Run service [<②で設定したCloud Runサービス名>] 
in project [<Google Cloud プロジェクトID>] region [<②で設定したリージョン>]
✓ Building and deploying new service... Done.
  ✓ Uploading sources...
  ✓ Building Container... Logs are available at [https://console.cloud.google.com/cloud-build/builds/xxxxxxxxxxxx
  xxxxxxxxxxxx?project=<Google Cloud プロジェクト番号>].
  ✓ Creating Revision... Revision deployment finished. Checking container health.
  ✓ Routing traffic...
  ✓ Setting IAM Policy...
Done.
Service [<②で設定したCloud Runサービス名>] revision [<②で設定したCloud Runサービス名>-xxxxx-xxxxx] has been deployed and is serving 100 percent of traffic.
Service URL: https://<②で設定したCloud Runサービス名-xxxxxxxxx-an.a.run.app
```
※Building Containerのときに、Cloud Buildに行くとビルド進捗が確認できる

表示されているURLをブラウザで開くと以下のように表示される
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59542/6c70b2f9-3746-1f91-15a0-2b1219f0a584.png)


**!!!注意!!!**

- Cloud Storageに保存されるソースコード
- Artifact Registryに保存されるコンテナイメージ

それぞれで料金がかかるので、動作確認後はそれぞれ削除しておいた方がよい

# 5. 使ってみたその2
既存コンテナイメージを使ったCloud Runサービスの作成をしてみた。
以下の手順は、[クイックスタート: Cloud Run にデプロイする](https://cloud.google.com/run/docs/quickstarts/deploy-container?hl=ja)に基づいて、Artifact Registryに存在するコンテナイメージからサービスを作成する例.

①グローバルナビゲーションのプルダウンを押下して、4.1.で作成したプロジェクトを選択する
②グローバルナビゲーションの検索ボックスから「Cloud Run」のコンソールへ移動する
③「サービスの作成」を押下する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59542/6d446a3d-41f7-190b-6e8b-08f2f9f3137f.png)
④以下それぞれ埋めて「作成」を押下する

項目|設定値|補足
---|---|---
デプロイ方式|既存のコンテナイメージから1つのリビジョンをデプロイする + サンプルコンテナでテスト押下|
サービス名|cloud-run-test|今回のモジュール名と一致させてるだけ
リージョン|asia-northeast1(東京)|任意。
CPUの割り当てと料金|リクエストの処理中にのみCPUを割り当てる|
自動スケーリング|最小:0 最大:100|
Ingress|すべてのトラフィックを許可する|
認証|未認証の呼び出しを許可|

→「サービスの詳細」画面へ遷移するので、全項目に緑のチェックがつくまで待つ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59542/8eab3f0a-4596-0053-b146-2bd56049e893.png)

# 6. Cloud Runサービスの設定
TODO: 以下についてそれぞれどういうことになるのかまとめたい

```
■CPU割当と料金
- リクエストの処理中にのみ CPU を割り当てる(課金はリクエスト単位であり、またコンテナ インスタンスがリクエストを処理した場合にのみ発生します。
- CPU を常に割り当てる(コンテナ インスタンスのライフサイクル全体に対して課金されます。

■自動スケーリング
- 1に設定すると「コールドスタートが減少します」って何？

■Ingress
- すべてのトラフィックを許可する
- 内部トラフィックと Cloud Load Balancing からのトラフィックを許可する
- 内部トラフィックのみを許可する

■認証
- 未認証の呼び出しを許可(公開する API またはウェブサイトを作成する場合は、このチェックボックスをオンにします。
- 認証が必要(Cloud IAM を使用して承認済みユーザーを管理します。
```

# 7. gcloud run deployコマンドについての所感
「gcloud run deploy」コマンドでフルに引数を指定すると、以下のようなことが一気にできる

ソースコードの圧縮 + Cloud Storageへのアップロード
→ Cloud Build を使ったコンテナイメージのビルド & Artifact RegistryへイメージをPUSH
→ イメージをCloud Run servicesへデプロイ(リビジョンのテンプレート設定変更・作成 & インスタンス化)

リビジョンのテンプレート設定には、コンテナのCPUやメモリといったインブラリソースの設定も含まれている。
これを変更できてしまう。
Terraformを利用するなどして、インフラを一元的管理しているような場合は、バッティングしてしまうため注意が必要。

# 8. デフォルトのサービスアカウント
リビジョンにおいて、明示的にサービスアカウントを設定しなくてもデフォルトのサービスアカウントが設定される。
このデフォルトのサービスアカウントはかなり多くの操作ができる権限を持っている。
セキュリティ的には、[公式からの案内](https://cloud.google.com/run/docs/configuring/service-accounts?hl=ja)にもある通り、権限をちゃんとしぼったサービスアカウントを別途用意してやる方がよさそう。




# 9. 参考
**Cloud Runの概要の理解に**

https://blog.g-gen.co.jp/entry/cloud-run-explained#:~:text=Cloud%20Run%20%E3%81%A8%20GKE%20%E3%81%AF,%E3%81%A7%E3%81%8D%E3%82%8B%E3%82%B5%E3%83%BC%E3%83%93%E3%82%B9%E3%80%8D%E3%81%A8%E3%81%84%E3%81%86%E4%BD%8D%E7%BD%AE%E3%81%A5%E3%81%91%E3%81%A7%E3%81%99%E3%80%82

**Cloud Run + αで構成するシステムの図がとてもわかりやすい**

https://zenn.dev/kazumax4395/articles/66ec5c259c950c

**Cloud Run + Cloud Spannerのおすすめ構成がのってる**

https://cloud-ace.jp/column/detail356/

※CEDECでの公演とあわせると、補足説明があってもっとわかりやすい

https://www.youtube.com/watch?v=sHo_IBwJWYE

---

https://cloud.google.com/run/docs/container-contract?hl=ja

https://zenn.dev/tnk4on/articles/6067d7e827c471

https://shinyorke.hatenablog.com/entry/cloud-arch-serverless

https://dev.classmethod.jp/articles/google-cloud-run-grpc-stream/