で、Cloud Functions と Cloud Run って何が違うの？

:::note info
2024/8/22 に Cloud Functions は Cloud Run functions としてリブランディングされ Cloud Run の各機能を利用できるように変更されることが[発表](https://cloud.google.com/blog/products/serverless/google-cloud-functions-is-now-cloud-run-functions)されました。
:::


## はじめに
こんにちは、Datadog Japan で Sales Engineer をしている [**AoTo**](https://qiita.com/AoTo0330) です。

普段から Google Cloud を利用されている方や、イベント駆動やマイクロサービスの設計を検討する方が直面する、「**で、Cloud Functions と Cloud Run って何が違うの？**」という疑問を解消するべくこのブログを執筆しています。

今回の比較対象は [**Cloud Functions(第2世代)**](https://cloud.google.com/functions/docs/concepts/version-comparison?hl=ja#new-in-2nd-gen)[^1] と [**Cloud Run services**](https://cloud.google.com/run/docs/overview/what-is-cloud-run?hl=ja#services)・[**Cloud Run jobs**](https://cloud.google.com/run/docs/overview/what-is-cloud-run?hl=ja#jobs) の3つです。特に、Cloud Functions との比較では Cloud Run services にのみ言及されることが多いため、ここでは Cloud Run jobs のユースケースについても考察します。
[^1]: Cloud Functions(第1世代) は第2世代と比較してサポートされていない機能があり、実行環境も異なります。ちなみに、Cloud Run も jobs の登場とともに現在は第2世代となっています。 

App Engine と Cloud Run の比較は『[で、Cloud Run と App Engine って何が違うの？](https://qiita.com/AoTo0330/items/6b31bb6c042e2f285168)』をご参照ください🐶

https://qiita.com/AoTo0330/items/6b31bb6c042e2f285168

## まとめ
 - **Cloud Functions(第2世代)** は **Cloud Run services** 上で稼働するサーバレス関数サービスである[^2]
 - **Cloud Functions(第2世代)** と **Cloud Run services** は HTTP と Eventarc を中心に豊富なトリガーが利用できる
 - **Cloud Functions(第2世代)** は Pub/Sub や Cloud Storage と統合され、直接イベントをトリガーできる
 - **Cloud Run jobs** は配列ジョブを実行できるが、HTTP で直接トリガーできない[^3]
 - [**Binary Authorization** を必須にする](https://cloud.google.com/binary-authorization/docs/run/requiring-binauthz-cloud-run?hl=ja)場合には **Cloud Functions** は利用できない
 - Cloud Functions や Cloud Run jobs が利用できない場合は、**Cloud Run services** を利用する 
[^2]: 実際に Cloud Functions(第2世代) を実行すると Cloud Run services が稼働する様子を Google Cloud Console からも確認できます。この様子は『[Cloud Functions（2nd gen）と Cloud Run の関係性を知る](https://zenn.dev/cloud_ace/articles/b30971199b392c)』でまとめられています。
[^3]: HTTP トリガーを行うことはできませんが、[Cloud Run Admin API コネクタ](https://cloud.google.com/workflows/docs/reference/googleapis/run/Overview)を用いて API を経由したトリガーは可能です。

|  | Cloud Functions | Cloud Run jobs | Cloud Run services |
| -- | -- | -- | -- |
| トリガー | HTTP トリガー <br>イベントトリガー<br>Pub/Sub, Cloud Storage etc | Cloud Scheduler<br>Cloud Run Admin API(HTTP) | HTTP トリガー <br>イベントトリガー |
| デプロイ単位 | アプリケーションコード | [コンテナイメージ](https://cloud.google.com/run/docs/overview/what-is-cloud-run?hl=ja#packaged) | [コンテナイメージ](https://cloud.google.com/run/docs/overview/what-is-cloud-run?hl=ja#packaged) |
| 開発環境 | 関数フレームワーク | コンテナ | コンテナ |
| 実行環境 | Cloud Run services | Knative Serving 互換[^4] | Knative Serving 互換 |
| 実行時間 | HTTP トリガー：60分<br>イベントトリガー：9分 | デフォルト：10分<br>最大：24時間| 制限なし |

[^4]: Cloud Run jobs の実行環境を確認することはできませんが、[Cloud Run のドキュメント](https://cloud.google.com/run/docs/overview/what-is-cloud-run?hl=ja#services-and-jobs)には「サービスとジョブはどちらも同じ環境で動作し」とあるため、ここでは Knative Serving 互換で実行されていると判断します。

## 選定方法
今回に限らず、クラウドサービスの利用を検討する際は複数の類似サービスを比較・検討することが重要です。利用者はより運用・管理コストの低いマネージドサービスから検討していくことで、クラウドのメリットを最大限に享受できます。

Cloud Functions, Cloud Run はどちらもマネージドサービスですが、Cloud Functions(第2世代) が Cloud Run services 上で稼働していることからも、Cloud Functions がより抽象化されていると言えます。

以上を踏まえると、抽象化の順は以下のようになります。

```mermaid
flowchart LR
%%{init:{'theme':'neutral'}}%%
 1["Kubernetes(Borg)"] --> 2[Knative Serving API]
 2 --"③"--> 3[Cloud Run service]
 2 --"②"---> 4[Cloud Run jobs]
 3 --"①"---> 5[Cloud Run functions]

 a([フレキシブル]) <-----> b([マネージド])
```

さらに、それぞれのサービスを選定する基準や方針は以下のように言い表せます。

1. HTTP や Cloud イベントトリガーを利用し、コンテナイメージを管理したくない。特に、Pub/Sub や Cloud Storage のイベントをトリガーしたり、短い時間で実行が完了する場合。
1. 決められたジョブを定義し、コンテナイメージを利用できる。特に、Cloud Scheduler で定期的にトリガーするジョブを並列化し高速化したい場合。
1. HTTP や Cloud イベントトリガーを利用し、コンテナイメージを利用できる。特に、長い実行時間が必要な処理や、Web コンテンツのホスティングを行う場合。

Cloud Run service を選択することで、Cloud Functions や Cloud Run jobs で実現したいことも十分に実現できます。しかし、それぞれのサービスを選択すれば、利用者が管理する範囲を狭めることができます。①-③ のように、よりマネージドなサービスから順に検討することで、適切なサービスを選択できます。

## サンプルアーキテクチャ
最後に、それぞれのサービスの特徴を活かしたアーキテクチャを考えてみます。


ちなみに、Cloud Run のアーキテクチャは Google Cloud の Customer Engineer の Suwa さんが執筆されたブログがあります。23個のアーキテクチャが紹介されていますが、一部のものは Cloud Functions でも実現できます。

https://zenn.dev/google_cloud_jp/articles/cloudrun-architectures

### Cloud Functions で Cloud Storage イベントによるトリガー
![CF_sample.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2363567/f1c82fa9-c145-bee3-e681-90e86bd81ed0.png)

Cloud Functions を用いた Cloud Storage イベントをトリガーするアーキテクチャです。Cloud Storage オブジェクトへの変更(作成・削除・変更・アーカイブ・メタデータの変更)をトリガーに簡易的な処理を実行できます。

Cloud Functions は Pub/Sub と Cloud Storage のイベントを直接トリガーできるため、**Cloud Run と比較して Eventarc の存在を意識せず**イベント処理を構成できます。

### Cloud Run jobs による Cloud SQL のレコード処理
![CRj_sample.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2363567/bac7295d-9e43-eb8e-dea0-fbad33700cba.png)

Cloud Run jobs を用いた Cloud SQL 内のレコードを処理するアーキテクチャです。Cloud Scheduler で実行される定期ジョブとして実行され、膨大なレコードを並列に処理する配列ジョブを定義し効率的に処理できます。

Cloud Run jobs は配列ジョブを実行できるため、**処理データ量に応じてスケールアウトする前提でジョブを構成**できます。この時、ジョブは失敗する前提で[ジョブの再試行とチェックポイントのベストプラクティス](https://cloud.google.com/run/docs/jobs-retries?hl=ja#checkpoint-sql)のように**冪等性**を考慮することが重要です。

### Cloud Run services による Web サイトホスティング
![CRs_sample.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2363567/b76f6ca4-43fc-3728-8dbb-c237404bd1d0.png)

Cloud Run services を用いた、マルチコンテナでの Web サイトホスティングを行うアーキテクチャです。Application Load Balancer でリクエストを受け取り、Cloud SQL にデータを保管することで Cloud Run services コンテナ自体はステートレスで効率的に運用可能です。

Cloud Run services はイベント処理やジョブ実行も可能ですが、Web サイトのホスティングのような**常時リソースを必要とするサービスを実行**できます。この時、**[マルチコンテナ](https://cloud.google.com/run/docs/deploying?hl=ja#sidecars)によってコンテナ毎に役割を分離する**ことができるのも大きな特徴です。

## おわりに
Cloud Functions と Cloud Run はどちらも便利なサービスです。どちらもサービスの開始時から世代が代わり、様々な機能が追加されることで、実現できる幅が広がっています。特に、Cloud Run services は幅広いユースケースに適用することができるため、Cloud Functions や Cloud Run jobs をどのような際に利用すれば良いかという疑問から本記事を執筆しました。

すべてのケースに Cloud Run services を利用すれば良いのではなく、Cloud Functions や Cloud Run jobs のアーキテクチャや特徴を理解することで、ユースケースに沿ったサービスを選定ができるのではないでしょうか。

この考え方は、Cloud Run services と Google Kubernetes Engine(GKE) の選定や Cloud Run jobs と Cloud Batch の選定の際も同様で、サービスに対する理解を深めることが重要です🐶