# Azure Container Apps オートスケール完全ガイド：KEDAによるゼロスケールとイベント駆動（HTTP・キュー・CPU）

> Azure Container AppsのKEDAオートスケールを実コードで徹底解説。HTTP/TCP/カスタム（CPU・メモリ・Service Bus・Event Hubs・Kafka・Redis）の各スケールルール、ゼロスケールの設計と自爆の罠、スケールアルゴリズムによる容量計画、マネージドID認証、ジョブのScaledJobまでを、Microsoft Learn公式に忠実にBicep/az CLIで示します。

- 公開日: 2026-06-26
- 著者: 友田 陽大
- タグ: Azure, Container Apps, KEDA, サーバーレス, オートスケール, コスト最適化, 信頼性, アーキテクチャ設計
- URL: https://tomodahinata.com/blog/azure-container-apps-keda-autoscaling-scale-to-zero-event-driven-guide

## 要点

- Azure Container AppsはKEDA（Kubernetes Event-driven Autoscaling）で水平スケールし、HTTP・TCP・CPU/メモリ・Service Bus/Event Hubs/Kafka/Redisなどのイベントソースでレプリカを増減する
- ゼロスケールは最大の魅力だが2つの罠：①CPU/メモリ負荷でスケールするアプリはゼロにできない ②Ingress無効かつ最小レプリカ0・ルール無しのワーカーは0に落ちると二度と起き上がれない
- 容量計画はアルゴリズム `desiredReplicas = ceil(currentMetricValue / targetMetricValue)` で逆算する。キュー駆動なら『1レプリカが捌く目標メッセージ数』が設計の主役
- KEDAスケーラーの認証はシークレットよりマネージドIDを優先し、接続文字列をアプリから消す。非HTTPイベントルールでは activeRevisionsMode を single にする
- ポーリング30秒・クールダウン300秒・スケールアップ刻み1→4→8→16→32・スケールダウンは安定化300秒後。これを理解すると急増・急減の挙動が読める

---

オートスケールは「設定したつもり」で事故が起きやすい領域です。**ゼロスケールしたワーカーが二度と起き上がらない**、**急増トラフィックに台数が追いつかない**、**接続文字列をアプリに直書きしてしまう**——どれも、KEDAの挙動を正確に理解していれば防げます。

この記事は、[Microsoft Learnの公式スケーリングドキュメント](https://learn.microsoft.com/en-us/azure/container-apps/scale-app)に忠実に、Azure Container Apps（ACA）のオートスケールを**容量計画の数式まで踏み込んで**解説します。私はAWSで[SQS駆動の冪等ワーカーをFargateで本番運用](/blog/aws-ecs-fargate-auto-scaling-target-tracking-sqs-worker-guide)し、[本番二重課金0件の決済基盤](/case-studies/payment-platform-reliability)を作ってきました。スケールはプラットフォームが、正しさはコードの構造（冪等性）が保証する——この分担はAzureでも同じです。ACA全体の本番運用は [Azure Container Apps 本番運用ガイド](/blog/azure-container-apps-production-guide) を参照してください。

---

## KEDAとは：ACAのスケールの頭脳

> To support this scaling behavior, Azure Container Apps uses KEDA (Kubernetes Event-driven Autoscaling). KEDA supports scaling against a variety of metrics like HTTP requests, queue messages, CPU and memory load, and event sources like Azure Service Bus, Azure Event Hubs, Apache Kafka, and Redis.（— [Scaling in Azure Container Apps](https://learn.microsoft.com/en-us/azure/container-apps/scale-app)）

ACAのスケールは、レプリカ（リビジョンの実行インスタンス）を**水平に**増減させること。その判断を **KEDA** が担います。スケールは3要素の組み合わせです。

- **Limits（上下限）**：最小・最大レプリカ数（既定0〜10、最大1,000まで設定可）
- **Rules（条件）**：いつ増減するか（HTTP / TCP / カスタム）
- **Behavior（挙動）**：ポーリング・クールダウン・刻みなどの時間特性

> 注意：`Vertical scaling isn't supported.` 1台を大きくする垂直スケールは不可。負荷対応は**台数（水平）だけ**です。

---

## スケールルール3種：使い分け

| カテゴリ | スケール基準 | 代表的な用途 | ゼロスケール |
|---------|------------|------------|:-----------:|
| **HTTP** | 同時HTTPリクエスト数（`concurrentRequests`、既定10） | Web API・Webアプリ | ◎ |
| **TCP** | 同時TCP接続数（`concurrentConnections`） | Redis等のTCPプロトコル | ◎ |
| **Custom（CPU/メモリ）** | CPU・メモリ負荷 | 計算主体のサービス | **✕** |
| **Custom（イベント）** | KEDAスケーラー（Service Bus等） | キュー駆動ワーカー | ◎ |

> 複数ルールを定義すると、`the container app begins to scale once the first condition of any rule is met.`——**どれか1つの条件が満たされた時点でスケール開始**します。

### HTTPスケール（最も一般的）

15秒ごとに「過去15秒のリクエスト数÷15」で同時リクエスト数を算出します。az CLIなら1行：

```bash
az containerapp create \
  --name web-api --resource-group my-rg --environment my-env \
  --image myregistry.azurecr.io/web-api:2026-06-26-a1b2c3d \
  --min-replicas 1 --max-replicas 20 \
  --scale-rule-name http-rule --scale-rule-type http \
  --scale-rule-http-concurrency 100   # 1レプリカあたり同時100リクエストを目標
```

「1レプリカが快適に捌ける同時リクエスト数」を負荷試験で測り、それを`http-concurrency`にします。憶測で大きくすると詰まり、小さくすると過剰にスケールしてコストが膨らみます。

---

## ゼロスケール：最大の魅力と2つの罠

ACAの目玉は**ゼロスケール**——アイドル時にレプリカを0にし、その間は課金されません。ただし**2つの罠**を知らないと事故ります。

### 罠①：CPU/メモリスケールはゼロにできない

> Applications that scale on CPU or memory load can't scale to zero.（— [overview](https://learn.microsoft.com/en-us/azure/container-apps/overview)）

CPU/メモリ負荷を計測するには、そもそもレプリカが動いている必要があるため。**ゼロに落としたいなら、HTTPまたはイベント駆動ルールを使う**。CPU/メモリルールは「常時最低1台」を前提にします。

### 罠②：Ingress無効ワーカーの自爆

これが最も多い事故です。

> Make sure you create a scale rule or set minReplicas to 1 or more if you don't enable ingress. If ingress is disabled and you don't define a minReplicas or a custom scale rule, your container app scales to zero and has no way of starting back up.（— [scale-app](https://learn.microsoft.com/en-us/azure/container-apps/scale-app)）

**Ingressを無効にしたバックグラウンドワーカー**で、最小レプリカ0かつスケールルール無しにすると、**0に落ちた後で二度と起き上がれません**（リクエストという起こすトリガーが無いから）。対策は2択：

1. **イベント駆動ルールを付ける**（キューにメッセージが来たら起きる）← 推奨
2. **`minReplicas`を1以上**にする（常駐させる）

```bash
# ✅ 正しい：イベント駆動ワーカーはスケールルールで0から起こす
az containerapp create \
  --name worker --resource-group my-rg --environment my-env \
  --image myregistry.azurecr.io/worker:2026-06-26-a1b2c3d \
  --min-replicas 0 --max-replicas 30 \
  --scale-rule-name sb-rule --scale-rule-type azure-servicebus \
  --scale-rule-metadata "queueName=orders" "namespace=my-sb" "messageCount=5" \
  --scale-rule-identity system   # マネージドIDで認証
```

---

## 容量計画：アルゴリズムを使って逆算する

KEDAのスケールアルゴリズムは公開されており、**容量計画に直結**します。

| 挙動 | 値 |
|------|-----|
| ポーリング間隔 | 30秒（HTTP/TCPには非適用） |
| クールダウン期間 | 300秒（最後のイベント後、最小レプリカへ落ちるまで） |
| スケールアップ安定化ウィンドウ | 0秒 |
| スケールダウン安定化ウィンドウ | 300秒 |
| スケールアップ刻み | 1, 4, 8, 16, 32, ...（最大まで） |
| スケールダウン刻み | 落とすべきレプリカの100% |
| アルゴリズム | `desiredReplicas = ceil(currentMetricValue / targetMetricValue)` |

### worked example：キュー駆動ワーカーの台数

Service Busキューで`messageCount: 5`（1レプリカが捌く目標＝5メッセージ）、キュー長が50なら：

```text
desiredReplicas = ceil(50 / 5) = 10 レプリカ
```

スループット目標から逆算するなら——「1メッセージの処理に2秒」「ピークで毎秒100メッセージ来る」なら、必要スループット=100 msg/s。1レプリカは0.5 msg/s（2秒/件）しか捌けないので、理論上**200レプリカ**必要。`messageCount`を小さくすると速く台数が増え（応答性↑・コスト↑）、大きくすると緩やかに増えます（応答性↓・コスト↓）。**SLAとコストのトレードオフを`messageCount`で調整**します。

### 急増・急減の読み方

- **急増**：`1 → 4 → 8 → 16 → 32 ...` と倍々で増える（安定化0秒なので即応）。
- **急減**：**スケールダウンは300秒の安定化ウィンドウ**を満たして初めて起こる。瞬間的な谷では台数を維持し、フラッピング（増減の振動）を防ぐ。
- **ゼロへ**：最後のイベントから**クールダウン300秒**待ってから0に落ちる。

この「上げは即・下げは慎重」という非対称が、**可用性を守りつつコストを抑える**KEDAの設計です。AWS Application Auto Scalingのスケールイン保護と同じ思想です。

---

## イベント駆動スケール：KEDAスケーラーの移植

任意の[KEDAスケーラー](https://keda.sh/docs/latest/scalers/)を、ACAのスケールルールに移植できます。KEDAの`ScaledObject`仕様の`type`と`metadata`を、ACAの`custom.type`と`custom.metadata`に写すだけ。

```bicep
scale: {
  minReplicas: 0
  maxReplicas: 50
  rules: [
    {
      name: 'eventhub-rule'
      custom: {
        type: 'azure-eventhub'         // KEDAスケーラーのtype
        metadata: {
          eventHubName: 'telemetry'
          consumerGroup: '$Default'
          unprocessedEventThreshold: '64'   // 1レプリカあたりの目標未処理イベント数
        }
        identity: 'system'             // マネージドIDで認証
      }
    }
  ]
}
```

対応イベントソースの例：**Azure Service Bus / Event Hubs / Storage Queue / Apache Kafka / Redis** ほか多数。

> 重要：`Set the properties.configuration.activeRevisionsMode property of the container app to single when using non-HTTP event scale rules.`——**非HTTPのイベントルールでは`activeRevisionsMode`を`single`**にします。

---

## 認証：シークレットよりマネージドID

KEDAスケーラーは認証情報を必要とします。ACAは2方式をサポートしますが、**マネージドIDを優先**するのが公式の推奨です。

> Where possible, use managed identity authentication to avoid storing secrets within the app.（— [scale-app](https://learn.microsoft.com/en-us/azure/container-apps/scale-app)）

### マネージドID（推奨）

スケールルールに`identity`プロパティを指定するだけ。接続文字列をどこにも保存しません。

```bicep
rules: [
  {
    name: 'azure-queue'
    custom: {
      type: 'azure-queue'
      metadata: {
        accountName: 'mystorage'
        queueName: 'jobs'
        queueLength: '1'
      }
      identity: 'system'   // または ユーザー割当IDのリソースID
    }
  }
]
```

あとは、そのマネージドIDに対象リソースのデータ権限ロール（例：`Azure Service Bus Data Receiver`、`Storage Queue Data Reader`）を付与すれば完了。**接続文字列の管理・ローテーションが不要**になり、漏洩面が消えます。

### シークレット（やむを得ない場合）

接続文字列をアプリの`secrets`に置き、スケールルールの`auth`配列で参照します。Key Vault参照と組み合わせれば、値の直書きは避けられます（詳細は [シークレット管理](/blog/azure-container-apps-production-guide)）。

---

## ジョブのスケール：ScaledJob

[イベント駆動ジョブ](/blog/azure-container-apps-jobs-batch-scheduled-event-driven-guide)も同じKEDAスケーラーを使いますが、**使い方が違います**。

> In an app, each replica continuously processes events, and a scaling rule determines the number of replicas to run to meet demand. In event-driven jobs, each job execution typically processes a single event, and a scaling rule determines the number of job executions to run.（— [Jobs](https://learn.microsoft.com/en-us/azure/container-apps/jobs)）

- **アプリ**：1レプリカが**継続的に**イベントを処理。スケールルールが**レプリカ数**を決める。
- **ジョブ**：1実行が**1イベント**を処理して終わる。スケールルールが**実行数**を決める。

「1イベントごとに専用リソースの新インスタンスが要る」「処理が長時間に及ぶ」ならジョブ、「常駐して流し続ける」ならアプリ、という使い分けです。

---

## スケールの落とし穴と既知の制限

公式が挙げる既知の制限（[scale-app](https://learn.microsoft.com/en-us/azure/container-apps/scale-app)）：

- **垂直スケール非対応**（台数だけ）。
- **レプリカ数は目標値であって保証ではない**（`Replica quantities are a target amount, not a guarantee.`）。
- **Daprアクターで状態管理する場合、ゼロスケール非対応**（インメモリ表現がIDと結びつくため）。
- **複数リビジョンモードでは、新しいスケールトリガーの追加は新リビジョンを作る**。旧リビジョンは旧ルールのまま残る。

### スケールが効かないときの診断

「スケールしない」と感じたら、システムログで `Error fetching scaler metrics` を確認します。これは**スケールの信号源（DB・Event Hub・別アプリ）に到達できていない**サイン。VNet・DNS・ファイアウォール・権限を確認します（[トラブルシューティングガイド](/blog/azure-container-apps-troubleshooting-revision-failed-exit-code-137-probes-guide)）。

---

## 本番設計：スケール×冪等性

オートスケールを本番品質にする鍵は、**スケールと正しさを分けて設計**することです。

- **スケールはプラットフォーム**：KEDAが台数を決める。あなたは`messageCount`やHTTP同時実行数という「1台あたりの目標」を測って設定するだけ。
- **正しさはコードの構造**：スケールイン（[SIGTERMで30秒以内に終了](/blog/azure-container-apps-production-guide)）やリトライでメッセージが再配信されても壊れないよう、ワーカーを**冪等**に作る。同じメッセージを2回処理しても二重課金しない——[冪等性キーで重複を吸収](/blog/aws-sqs-lambda-eventbridge-idempotent-async-processing-guide)します。

この分担を徹底すれば、**ゼロスケールしても取りこぼしも二重実行も起きない**。決済基盤で本番二重課金0件を達成した設計の核がこれです。

---

## まとめ

ACAのオートスケールは**KEDA**が担い、HTTP・TCP・CPU/メモリ・多数のイベントソースで水平スケールします。本番で効かせる勘所は4つ——

1. **ゼロスケールの2つの罠**（CPU/メモリは0不可、Ingress無効ワーカーの自爆）を回避する。
2. **アルゴリズムで容量を逆算**する（`messageCount`がSLAとコストの調整ノブ）。
3. **認証はマネージドID優先**で接続文字列をアプリから消す。
4. **スケール×冪等性**——スケールはKEDA、正しさはコードの構造で。

オートスケールの設計・チューニングから、キュー駆動ワーカーの冪等化まで、ご相談は[お問い合わせ](/contact)へ。本番運用全体は [Azure Container Apps 本番運用ガイド](/blog/azure-container-apps-production-guide) をどうぞ。
