Microsoft Azure Container AppsとGoogle Cloud Runと比べてみた

Google Cloudの **Cloud Run** は素晴らしいクラウドサービスだと思っています。

https://cloud.google.com/run?hl=ja

が、一方で代替はあるのか？ということも気になっています。そこでMicrosoft Azureの **Container Apps** を触ってみました。

https://azure.microsoft.com/ja-jp/products/container-apps/

## 前提
2022年10月頃時点の情報をもとに書かれています。

なおGoogle CloudのCloud Runには同名のサービスとしてCloud Run for Anthosがありますが、本稿ではこちらではなく **Cloud Run (fully managed)** の方を扱います。

### 共通事項
Cloud RunとAzure Container Appsは、ともに以下のような特徴をもつサービスです。

- **フルマネージド** のクラウドサービスである
    - 裏側のインフラ（クラスター、インスタンスなど）を意識する必要がない
    - Kubernetes（k8s）ベースの技術だが、k8s自体やそれに似た概念を意識しなくていい
- **コンテナ** を使って任意のアプリケーションを実行できる
    - インフラ特有のハンドラなどに依存せず、HTTP等の普通のリクエストを普通に受け取るアプリケーションを構成できる
- **サーバレス** である
    - HTTP等のリクエストに応じてサーバが動作して課金される
    - 自動でよしなにオートスケールし、リクエストが来ない時はゼロにスケールもできる

こういったような特徴をもつサービスです。要するに **コンテナをビルドして、デプロイしたら、すぐ動く** というサービスです。
コンテナさえ使っていればアプリケーションに要求される制約がゆるく、ローカルで`$ docker compose`などで開発していたアプリケーションをそのままクラウドにぶん投げて動かせます。コンテナとクラウドの両者の利点を活かした強力なサービスだと思っています。

○aaSのような総称があるのかは分かりません。コンテナを中心に動きますがCaaS（Container as a Service）と呼ばれるものとはちょっと違い[^caas]、どちらかというとFaaS（Function as a Service）に近い特性を持つものだと思っています。

[^caas]: https://cloud-ace.jp/column/detail323/ によると、CaaSとしてGoogle Kubernetes Engineが挙げられている

個人的に大切だと思っているのがゼロスケールで、AWS AppRunnerはこの点を満たしていなかった[^apprunner-stop]ので比較対象から外しました。[^whats-apprunner]
[Scale to zero · Issue \#9 · aws/apprunner\-roadmap](https://github.com/aws/apprunner-roadmap/issues/9)

[^apprunner-stop]: AWSのWebコンソールやCLIを使って、手動での一時停止・再開はできるらしい

[^whats-apprunner]: 他にも[公式のIssue](https://github.com/aws/apprunner-roadmap/issues?q=is%3Aissue+is%3Aopen+sort%3Areactions-%2B1-desc)にはWebSocketやHTTP/2など未対応な機能や、不具合のような挙動が残っているなど問題点が多く挙げられている。プライベートエンドポイントも設定不能だったが[本稿執筆中に対応されたらしい](https://github.com/aws/apprunner-roadmap/issues/2)。

### 共通する注意点
もちろん良いところばかりではなくて、Cloud RunとContainer Appsが共通して持っている課題も複数あります。

- スケールアウト時のコールドスタート問題
- ハイスペックマシンを使いたい場合などインフラ構成の自由度の低さ
- フルマネージドであるがゆえの問題発生時の調査・対応のしづらさ

このあたりが大きな問題になるケースにおいては、たとえばコールドスタート時のパフォーマンス低下対策としてCDNを活用するなど、違うサービスでの解決を検討すべきだと思います。

また、インフラやk8sが十分に抽象化されていて意識しなくて良い点を長所として挙げましたが、知識があったほうがより運用しやすいという事も、他のクラウドサービスと同様にあります。

## 実際に使ってみた
ただコンテナをデプロイして動かすだけなら、同じことができます。次の記事では全く同じソースコード・全く同じDockerイメージで、ふたつのサービスにデプロイしています。

- [13億パラメータの GPT モデルを GCP Cloud Run で動かす \- Qiita](https://qiita.com/suzuki_sh/items/0f0ecfce8f5984afeeab)
- [3700万パラメータの GPT モデルを Azure Container Apps で動かす \- Qiita](https://qiita.com/suzuki_sh/items/b5b5fe8daad1695c1891)

（パラメータ数が違う所からオチが見える）

基本的にはまったく同じアプリケーションをCloud RunとContainer Appsでそのまま動かせます。

## 比較
### 歴史

日付|できごと
--|--
2018年8月15日|[Cloud Run アルファ版リリース](https://cloud.google.com/run/docs/release-notes#August_15_2018)
2019年11月14日|[Cloud Run GA](https://cloud.google.com/run/docs/release-notes#November_14_2019)
2021年11月2日|[Azure Container Apps パブリックプレビュー](https://azure.microsoft.com/ja-jp/updates/public-preview-azure-container-apps/)
2022年5月25日|[Azure Container Apps GA](https://twitter.com/AzContainerApp/status/1529141320811368453)


Azure Container Appsのほうが新しく、Cloud Runのほうが3年ほど歴史の長いサービスです。

### コンセプト
Cloud Runの概念は以下の通り。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50391/17600f61-8a3f-16b7-976c-02462c50ce10.png)
▲[リソースモデル  \|  Cloud Run のドキュメント  \|  Google Cloud](https://cloud.google.com/run/docs/resource-model?hl=ja)より引用

サービスの中に複数リビジョンがあり、リビジョンの中に複数コンテナインスタンスがあります。比較的シンプルです。
「リビジョン」は、デプロイされたコンテナと環境変数などの設定のセットです。複数あるのは履歴を持っているからです。基本的には最新のリビジョンが使われますが、トラフィックを任意の割合で振り分けてBlue/GreenデプロイやA/Bテストに利用できます。
「コンテナインスタンス」が複数あるのは、オートスケールを表しています。

Azure Container Appsの概念は以下の図の通りです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50391/dd3414aa-d76b-c2c2-b4a3-51f9d1514e27.png)
▲[Azure Container Apps 環境 \| Microsoft Learn](https://learn.microsoft.com/ja-jp/azure/container-apps/environment)より引用

環境の中にアプリがあり、アプリの中に複数リビジョンがあり、リビジョンの中にレプリカがあり、レプリカの中にコンテナがあります。
「環境」が仮想ネットワークを構成し、その中に複数のアプリを配置できるようになっています。
「リビジョン」はCloud RunとAzure Container Appsとでほぼ同じ意味と言っていいと思います。
一方で、オートスケールを表しているのは「レプリカ」となっています。そのレプリカの中にさらに複数のコンテナが含まれています。

以下の点が、Cloud Runの図とは少し異なっています。

- 仮想ネットワークを構成する「環境」がある
- 「レプリカ」の中に複数コンテナがある

### コンテナの種類
いずれもLinuxコンテナ（`linux/amd64`）にのみ対応しています。

- [Azure Container Apps のコンテナー \| Microsoft Learn](https://learn.microsoft.com/ja-jp/azure/container-apps/containers)
- [サービスを開発する  \|  Cloud Run のドキュメント  \|  Google Cloud](https://cloud.google.com/run/docs/developing?hl=ja#containerizing)

Azure Container AppsはMicrosoftだからWindowsに対応しているとかそういう事はないようです。

### 裏側の技術
Cloud RunとAzure Container Appsは、いずれもk8sベースになっています。（公言されていたかどうかは怪しいですがおそらく）それぞれのk8sサービスである[Google Kubernetes Engine（GKE）](https://cloud.google.com/kubernetes-engine?hl=ja)と[Azure Kubernetes Service （AKS）](https://azure.microsoft.com/ja-jp/products/kubernetes-service/)がバックグラウンドにありますが、先程も述べたとおりそれらを意識しなくても良い設計になっています。

Azure Container Appsは、[KEDA](https://keda.sh/)、[Dapr](https://dapr.io/)、[Envoy](https://www.envoyproxy.io/)など、周辺機能もCNCFのOSSの技術の上に作られています。ちょっとMicrosoftらしくない感じですが、嬉しいですね。

### 最小/最大スケーリングの設定

-|Azure Container Apps|Cloud Run
--|--|--
最小インスタンス数|0〜|0〜
最大インスタンス数|〜30|〜100

いずれも最小インスタンス数・最大インスタンス数を設定して、スケーリングを制御できます。
細かい違いはありますが、インスタンス数が0になると課金が発生しない点、最小インスタンス数を1以上にしてリクエストを処理していない場合「アイドル状態」となり割引が適用されたりする点なども、共通しています。

Cloud Runでは当初は最小インスタンス数を指定できず、放っておくとゼロにスケールしてしまうためコールドスタートの影響を受けやすいという懸念がありましたが、アップデートで解消されています。

- Cloud Run
    - [最小インスタンス数（サービス）  \|  Cloud Run のドキュメント  \|  Google Cloud](https://cloud.google.com/run/docs/configuring/min-instances)
    - [コンテナ インスタンスの最大数（サービス）  \|  Cloud Run のドキュメント  \|  Google Cloud](https://cloud.google.com/run/docs/configuring/max-instances)
- Azure Container Apps
    - [Azure Container Apps でのスケーリング \| Microsoft Learn](https://learn.microsoft.com/ja-jp/azure/container-apps/scale-app)

### オートスケールのトリガー
Cloud Runはシンプルです。最小インスタンス数・最大インスタンス数・最大同時実行数の設定によって、来たリクエストに応じて自動的に増えたり減ったりします。

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

一方Azure Container Appsは、オートスケールの設定としていくつか選択できます。
HTTPトラフィック・TCPトラフィックで設定すると、Cloud Runとだいたい似たことができます。
また、「CPU・メモリの使用量が一定%以上になったらスケールする」という設定もできます。（この設定の場合、ゼロスケールはできない）
さらに[KEDA（Kubernetes Event-driven Autoscaling）](https://keda.sh/docs/2.8/scalers/)でのイベントドリブンスケーリングというのにも対応しており、実際にメッセージキューの溜まり具合によってスケーリングを制御する例が公開されています。
このように、色々と設定できるようになっています。

https://learn.microsoft.com/ja-jp/azure/container-apps/scale-app


### CPUとメモリのスペック
-|Azure Container Apps|Cloud Run
--|--|--
メモリ最小|512MiB|第1世代は128MiB、第2世代は512MiB
メモリ最大|4GiB|32GiB
vCPUコア最小|0.25|1
vCPUコア最大|2|8

こちらはCloud Runの方が高いスペックのマシンを動かせる印象です。特にメモリが32GiBも使えるので、要求メモリの大きい言語モデルも動かせます。
またAzure Container AppsもCloud Runも、メモリ・vCPUを自由に設定できるのではなく組み合わせに制約があります。Cloud Runのほうが柔軟に設定できる印象です。

一方で、GPUなどハイスペックな環境にはいずれも対応していません。

- Azure Container Apps
    - [Azure Container Apps のコンテナー \| Microsoft Learn](https://learn.microsoft.com/ja-jp/azure/container-apps/containers)
- Cloud Run
    - [CPU 制限  \|  Cloud Run のドキュメント  \|  Google Cloud](https://cloud.google.com/run/docs/configuring/cpu)
    - [メモリ上限を構成する  \|  Cloud Run のドキュメント  \|  Google Cloud](https://cloud.google.com/run/docs/configuring/memory-limits)

### トリガーの対応
Cloud RunとAzure Container Appsは、いずれも普通にHTTPで叩く以外に、gRPCやWebSocketをサポートしています。

また、HTTP等のリクエストを伴わずバックグラウンドジョブとしてコンテナを実行することもできます。この場合Cloud Runは最大60分でタイムアウトするのですが、Azure Container Appsは時間制限は無く動かしっぱなしにできると言われていた記憶があります（ドキュメント失念）

- Cloud Run
    - [gRPC の使用  \|  Cloud Run のドキュメント  \|  Google Cloud](https://cloud.google.com/run/docs/triggering/grpc)
    - [WebSocket の使用  \|  Cloud Run のドキュメント  \|  Google Cloud](https://cloud.google.com/run/docs/triggering/websockets)
    - [ジョブの作成  \|  Cloud Run のドキュメント  \|  Google Cloud](https://cloud.google.com/run/docs/create-jobs)
- Azure Container Apps
    - [Azure Container Apps での HTTPS または TCP イングレスの設定 \| Microsoft Learn](https://learn.microsoft.com/ja-jp/azure/container-apps/ingress?tabs=bash)
    - [チュートリアル: Azure Container Apps を使用してバックグラウンド処理アプリケーションをデプロイする \| Microsoft Learn](https://learn.microsoft.com/ja-jp/azure/container-apps/background-processing?tabs=bash)

### VPC
Cloud Runでは、「サーバレスVPCアクセス」というのを使ってVPCに外部から接続しに行く形になります。

https://cloud.google.com/run/docs/configuring/connecting-vpc?hl=ja

一方Azure Container Appsですが、コンセプトの項にて「環境」というものを作ってその中にアプリを配置すると説明しました。この「環境」が仮想ネットワークを持っていて、デフォルトだと適当に新規作成されますが、既存の仮想ネットワークに環境を作ってそこにアプリをデプロイもできるようです。

https://techcommunity.microsoft.com/t5/apps-on-azure-blog/azure-container-apps-virtual-network-integration/ba-p/3096932

https://learn.microsoft.com/ja-jp/azure/container-apps/networking

#### データベースへのアクセス
具体的な例として、コンテナアプリからデータベースへアクセスしたい場合について考えてみます。

Cloud Runでは、「Cloud SQL Auth Proxy」か「サーバレスVPCアクセス」を用いることになります。

https://cloud.google.com/sql/docs/postgres/connect-instance-cloud-run?hl=ja

Azure Container Appsでは、先述のVNET統合を使ってプライベートなDBにアクセスを確立できると思います。（良い例が見つからず）
[サービスコネクタ](https://learn.microsoft.com/ja-jp/azure/container-apps/service-connector)を使ってDBに「マネージドID」で接続情報を秘匿しながらPostgreSQLデータベースにアクセスする例がありました。

https://learn.microsoft.com/ja-jp/azure/container-apps/tutorial-java-quarkus-connect-managed-identity-postgresql-database

### サイドカー
Cloud Runでは1サービスあたり1コンテナであり、サイドカーコンテナに非対応です。
これがCloud Runを使わない理由として挙げられる事もあるようです。

https://techblog.zozo.com/entry/faans-replacement-to-gke-autopilot#%E3%81%AA%E3%81%9CGKE%E3%81%AB%E7%A7%BB%E8%A1%8C%E3%81%97%E3%81%9F%E3%81%84%E3%81%AE%E3%81%8B

https://cloud.google.com/blog/ja/products/containers-kubernetes/when-to-use-google-kubernetes-engine-vs-cloud-run-for-containers

一方Azure Container Appsは1アプリに対して複数のコンテナをデプロイするサイドカーパターンに対応しています。Daprを使った実装例がいくつか公開されています。

https://learn.microsoft.com/ja-jp/azure/container-apps/containers#multiple-containers

https://learn.microsoft.com/ja-jp/azure/container-apps/dapr-overview?tabs=bicep1%2Cyaml

### 永続化可能なファイルシステム
サーバレスな仕組みなので原則としてファイルシステムは一時的なものであり、保存したデータなどは通常スケールインした時などに揮発します。そのためアプリケーションの動作コンテナはステートレスに設計するほうがCloud RunやAzure Container Appsで使いやすくなります。

ですが、なんらかの利用でファイルシステムを永続化したい場合。Cloud Runでは第2世代環境であればネットワークファイルシステムを、Azure Container AppsであればAzure Filesを、それぞれマウントすることで永続化されたファイルシステムをコンテナアプリから利用できるようになっています。

- [ネットワーク ファイル システムを使用する  \|  Cloud Run のドキュメント  \|  Google Cloud](https://cloud.google.com/run/docs/using-network-file-systems?hl=ja)
- [Azure Container Apps でストレージ マウントを使用する \| Microsoft Learn](https://learn.microsoft.com/ja-jp/azure/container-apps/storage-mounts?source=recommendations&pivots=aca-cli)


### 料金
東日本リージョン・ドル建ての場合で比較します。

-|Azure Container Apps|Cloud Run
--|--:|--:
vCPU（$ / vCPU秒）|$0.000024	|$0.00002400
メモリ（$ / Gib秒）|$0.000003	|$0.00000250
リクエスト（$ / 100 万リクエスト）|$0.40|$0.40 

秒単位だとよくわからないので、30日間動作しっぱなしだった場合に換算すると下記です。

-|Azure Container Apps|Cloud Run
--|--:|--:
vCPU（$ / vCPU 30日間）|$62.208|$62.208
メモリ（$ / Gib 30日間）|$7.776|$6.48

Cloud Runのほうが若干安いですがほとんど同じです。
いずれも無料利用枠が結構用意されているので、少し試す程度であれば無料で利用できると思います。

- Cloud Run
    - [料金  \|  Cloud Run  \|  Google Cloud](https://cloud.google.com/run/pricing?hl=ja)
- Azure Container Apps
    - [Azure Container Apps \- 価格 \| Microsoft Azure](https://azure.microsoft.com/ja-jp/pricing/details/container-apps/)

### コンテナレジストリ
Cloud Runは、Google Cloudのレジストリ **（Container RegistryまたはArtifact Registry）** のイメージをデプロイできます。ローカルでDockerを使ったり、Cloud Buildを使ったりして、ビルドしたイメージを上げてあげる必要があります。

https://cloud.google.com/run/docs/deploying?hl=ja

Azure Container Appsには **Azure Container Registry** も当然ありますが、その他の **Docker Hub** などのレジストリもサポートされています。

https://learn.microsoft.com/ja-jp/azure/container-apps/get-started-existing-container-image-portal?pivots=container-apps-private-registry

Cloud Runに対する**Artifact Registry**と、Azure Container Appsに対する**Azure Container Registry**が出てきました。これらの価格に着目してみます。

Artifact Registryは1GBあたり月額 \\$0.1です。
それに対してAzure Container Registryは10GBからで日額\\$0.167、つまり30日換算で約\\$5となっています。容量の単位が違うので単純比較はできませんが、Azureのほうがかなり高額になっている印象です。

- Google Cloud Artifact Registry
    - [料金  \|  Artifact Registry  \|  Google Cloud](https://cloud.google.com/artifact-registry/pricing?hl=ja#storage)
- Azure Container Registry
    - [価格 \- Container Registry \| Microsoft Azure](https://azure.microsoft.com/ja-jp/pricing/details/container-registry/)

サーバ自体の費用に比べると少額になることが多いと思うので、そこまで気にする必要は無いかもしれません。
しかしアプリケーションを頻繁にゼロにスケールインさせる場合、サービス自体の料金はほとんど掛かりませんが、アプリケーションが起動していなくてもコンテナレジストリの料金は継続的に発生するため注意が必要です。

## まとめ
Google Cloud RunとAzure Container Appsは、それぞれ単体ではほとんど似たように使うことができます。
Cloud Runの方が歴史があるぶんサービスとしても整っていますが、Azure Container Appsも拡張性に有利があります。
コンセプトとして、Cloud Runは単一コンテナによるコンパクトな構成のサービスを想定しています。一方Azure Container Appsは複数コンテナによるマイクロサービスアーキテクチャを意識しているようです。このあたりは好みが分かれそうです。

いくつか異なる点はありましたが、実際に利用するものをどちらにするか比較する際はこの二者の優劣で決めると言うよりは、Google CloudとMicrosoft Azureの周辺サービスの要件等によって決まる事の方が多いのではないか、と感じました。

こういうクラウドサービスは競合に類似サービスが立つと互いに成長が促進されるイメージですので、両者発展して欲しいと感じました。
特に、よりハイスペックなインスタンスが使えるようになったり、GPUに対応したりしてくれれば、できることが増えると思いました。

## 参考
- [Azure Container Apps の特徴と Azure Web Apps / Azure Functions との違い \- しばやん雑記](https://blog.shibayan.jp/entry/20211104/1636012258)