Azure Functions の Flex コンシュームプランに切り替え、「Always‑On を有効にしたのに、たまにだけレスポンスが遅い」「シーケンシャルに叩いているだけなのにコールドスタートが発生する」と悩むケースが増えています。本記事では、その原因となりやすい HTTP 同時実行数の設定とスケール動作の仕組みを整理し、具体的なチューニング手順と検証パターンを詳しく解説します。
Azure Functions FlexコンシュームプランとAlways‑Onの前提
まずは、Flex コンシュームプランと従来コンシュームプランの違い、そして「Always‑On」の意味を整理します。ここを誤解したまま運用を始めると、応答遅延やコスト増加につながりやすくなります。
Flexコンシュームプランとは
Flex コンシュームプランは、従来コンシュームプランの「イベントドリブンで自動スケールする」という特徴を維持しつつ、以下のような柔軟性を追加した新しいプランです。
- インスタンスごとのメモリサイズを選択できる(例: 512MB, 1024MB, 2048MB など)。
- 常時起動(Always‑On)インスタンス数を指定できる。
- HTTP 同時実行数(HTTP Concurrency)を制御できる。
- 従来コンシュームよりも「ウォーム状態」を長く維持しやすい。
特に HTTP 同時実行数と Always‑On は、レスポンス遅延やコールドスタートを大きく左右する重要なパラメーターです。本記事の主題もこの 2 つになります。
従来コンシュームプランとの違い
従来コンシュームプランでも、直近にリクエストがあったインスタンスはしばらくウォーム状態を保ちますが、その維持時間は比較的短く、一般的には数分程度と考えられています。そのため、アクセスがまばらなワークロードでは、
- しばらくリクエストがない → インスタンス破棄
- 次のリクエスト到着時に新しいインスタンス起動 → コールドスタート
というサイクルが頻繁に発生しがちです。
Flex コンシュームプランでは Always‑On を有効化することで、この「常に誰かが起きている状態」を長時間維持しやすくなります。ただし、ここで重要なのは「Always‑On にしたからと言って、インスタンスが絶対に落ちないわけではない」という点です。
Always‑On の本当の意味
Flex コンシュームプランにおける Always‑On は、あくまで「常に一定数のインスタンスが待機できるように制御しようとする」ための設定です。実際の挙動としては、以下のような要因でインスタンスがリサイクルされることがあります。
- 一定時間アイドル状態が続いたとき。
- メモリや CPU の過負荷が検知されたとき。
- 基盤側のメンテナンスやアップデートが行われたとき。
つまり「Always‑On = インスタンスが永遠に同じまま動き続ける」ではなく、「Always‑On = 必要に応じて新しいインスタンスを起こし、常に所定数のインスタンスが待機するよう努める」というイメージに近いです。この点を頭に入れておくと、後述する挙動が理解しやすくなります。
なぜAlways‑Onなのに単発リクエストで遅くなるのか
ここからは、実際に多くの人が直面している典型的な症状を整理し、原因を紐解いていきます。
よくある症状のパターン
以下のようなケースは、Flex コンシュームプランを使い始めた直後によく観測されます。
- Always‑On を 1 台に設定しているのに、数回に一度は 2 台目が起動し、そのリクエストだけ応答が遅い。
- Always‑On を 2 台に増やしても、約 10 回に 1 回は新しいインスタンスが起動して遅延が発生してしまう。
- 10 秒間隔でシーケンシャルにリクエストしているだけなのに、新しいインスタンスに割り当てられコールドスタートが発生する。
「Always‑On なのに、なぜ既存インスタンスが使われないのか?」という疑問が生まれますが、その鍵になっているのが HTTP 同時実行数(HTTP Concurrency) の設定です。
HTTP 同時実行数が小さいとどうなるか
HTTP 同時実行数は、「1 つのインスタンスが同時に処理できる HTTP リクエスト数の上限」を意味します。デフォルト値はメモリサイズやランタイムによって異なり、特に Python など一部ランタイムでは、安全側に倒した非常に低い値(例: メモリ 2GB で 1)になっていることがあります。
この値が小さすぎると、次のような流れで不必要なスケールアウトが発生しやすくなります。
- 既存インスタンスが 1 件のリクエストを処理中。
- HTTP 同時実行数の上限をすでに満たしているため、追加リクエストは新規インスタンスに振り分けられる。
- 新規インスタンスが起動され、コールドスタートが発生 → そのリクエストだけ遅く見える。
実際には、ユーザー側の観点では「いつも 1 リクエストしか飛ばしていないのに…」というケースでも、接続のライフサイクルやランタイム側の処理が影響して、プラットフォームから見ると「まだ処理中」とみなされる時間が長く残り、同時実行数の上限に早々に到達してしまうことがあります。
HTTP同時実行数を手動設定に変更する
こうした「Always‑On なのに新規インスタンスが起動して遅くなる」問題を抑える最も有効な手段が、HTTP 同時実行数を手動設定に切り替え、適切な値まで引き上げることです。
Azureポータルでの設定手順
HTTP 同時実行数を手動設定に変更する手順は次のとおりです。
- Azure ポータルで対象の 関数アプリ(Function App) を開く。
- 左側メニューから 「スケールと同時実行」(Scale & Concurrency)を選択。
- 「HTTP 同時実行」の設定を 「自動」→「手動」 に切り替える。
- 同時実行数の値を入力し、保存する。
設定変更後は、数分程度様子を見た上で、負荷試験ツールなどを用いて応答時間の変化を必ず確認してください。以下で、メモリサイズごとの目安値を紹介しますが、あくまでスタート地点と捉え、自身のワークロードで再計測することが重要です。
メモリサイズ別のHTTP同時実行数の目安
Flex コンシュームプランはインスタンスあたりのメモリサイズを選べるため、メモリと同時実行数のバランスを取ることが重要です。以下は、実運用でよく見かける構成の一例と、それに対する HTTP 同時実行数の調整例です。
| メモリサイズ | ランタイム例 | デフォルト同時実行数の例 | 試してみたい目安値 | 想定シナリオ |
|---|---|---|---|---|
| 512MB | .NET / Node.js など軽量処理 | 4 | 10 前後に引き上げ | 軽量な API、簡単な I/O 中心処理 |
| 1024MB | .NET / Node.js / Python | 2 ~ 4 程度 | 8 ~ 12 | 中程度の CPU/I/O 負荷の API |
| 2048MB | Python 関数(CPU やメモリを多用) | 1(非常に低く設定されていることが多い) | 5 ~ 10 | 重めの処理だが、多少の並列を許容したい場合 |
特に Python 関数では、デフォルト同時実行数が 1 に設定されているケースがあり、そのままだと 少しアクセスが重なるだけで即スケールアウト → 新規インスタンス起動 → コールドスタート という挙動になりやすくなります。メモリにまだ余裕があるようであれば、5 ~ 10 程度まで段階的に引き上げて様子を見てください。
ただし、同時実行数を大きくしすぎると、そのインスタンス 1 台にリクエストが詰め込まれすぎてメモリ不足や GC(ガーベジコレクション)の頻発を招き、逆にレスポンスが悪化するリスクもあります。メモリ使用量と CPU 使用率を観測しながら、少しずつ上げていくのが安全です。
Always‑Onインスタンスは“常駐”ではない
Always‑On の理解でよくある誤解が「Always‑On にしているインスタンスは、決して落ちない」というものです。実際には、Flex コンシュームプランの Always‑On は次のような性質を持ちます。
- プラットフォーム側は、設定した台数のインスタンスが常時「待機できる状態」で存在するように努力する。
- しかし、個々のインスタンスは負荷やアイドル時間に応じてリサイクルされることがある。
- リサイクル後は新しいインスタンスが起こされるため、そのタイミングでコールドスタート相当の遅延が発生する可能性がある。
そのため、Always‑On を 1 ~ 2 台程度にしていても、長時間放置した後の最初のリクエストや、たまたまインスタンスの入れ替えが起こったタイミングでは、どうしても数秒レベルの遅延が発生し得ます。
重要なのは、「常にまったく遅延が出ない状態」ではなく、「通常のトラフィック中は既存インスタンスに割り当てられやすい状態」 に持っていくことです。そのために HTTP 同時実行数の調整が非常に効いてきます。
具体的なテスト構成と改善例
ここでは、実際の検証で確認された構成例とその挙動を整理してみます。条件によって結果は変わりますが、傾向を掴むうえで参考になります。
検証シナリオの要件
- 単発テストでは 1 秒以内に応答してほしい。
- ときどき最大 1,000 件の並列リクエストが来るため、スケールアウト自体は許容する。
- Flex コンシュームプラン + Always‑On を利用している。
この要件に対して、次のような構成で負荷試験を行ったとします。
| ケース | Always‑On台数 | HTTP同時実行数 | 想定リクエストパターン | 観測された傾向 |
|---|---|---|---|---|
| A | 1 | デフォルト値(Python 2GB で 1) | 10 秒おきに単発リクエスト | 数回に一度、新規インスタンスが起動し、2 ~ 数秒の遅延を観測 |
| B | 1 | 5 まで引き上げ | 10 秒おきに単発リクエスト | ほとんどのリクエストが既存インスタンスに割り当てられ、1 秒未満の安定した応答 |
| C | 2 | 10 まで引き上げ | パルス的に 100 ~ 200 リクエストを投入 | スケールアウトは発生するが、多くのリクエストがウォームなインスタンスで処理され、体感の遅延が大きく改善 |
このように、Always‑On 台数を増やすだけでなく、HTTP 同時実行数を適切に引き上げることで、「既存インスタンスに割り当てられるリクエストの割合」を増やし、結果としてユーザーから見たレスポンス遅延を抑えることができます。
1インスタンスでどこまで捌くかの考え方
コスト最適化の観点からは、「少ない Always‑On 台数 × ほどよく高い同時実行数」という構成が、現実的な落としどころになりやすいです。
- Always‑On インスタンス数を増やす → その分、時間あたりの課金がストレートに増える。
- HTTP 同時実行数を上げる → 1 台あたりで捌けるリクエストが増え、同じトラフィックなら必要な台数を抑えられる。
もちろん、同時実行数を上げすぎると 1 台あたりの負荷が高くなりすぎるため、メモリの使用状況や処理時間を見ながらバランスを取る必要がありますが、最初から Always‑On を増やすよりも「まず同時実行数でどこまで行けるか」を確認する方が、コストパフォーマンスは良くなりやすいです。
シナリオ別・設定チューニングの指針
ここからは、よくある課題ごとに「どの設定をどういじるべきか」を整理します。
| 課題 | 対処ポイント | 補足 |
|---|---|---|
| 単発リクエストでコールドスタートが発生する | HTTP 同時実行数を手動設定にし、少し高めに設定して既存インスタンスに余裕を持たせる | 大きくし過ぎると、突発的なバースト時にメモリ不足や応答劣化を招くため、段階的に調整 |
| 不必要なスケールアウトが頻発する | 同時実行数を上げて 1 台あたりの処理能力を上げ、Always‑On 台数もワークロードに合わせて見直す | それでも解消しない場合は、Always‑On 台数を +1 してスパイク時のクッションを増やす |
| コストが想定以上に高い | 「常時起動台数 × メモリ量 × 時間」が課金に直結するため、同時実行数の増加で台数を抑えられないか検討する | ベース負荷とピーク負荷を分けて考え、ピークはスケールアウトに任せる設計を意識する |
| たまにだけ極端に遅いリクエストがある | インスタンスのリサイクルや新規起動のタイミングが重なっていないかを疑い、同時実行数・Always‑On 台数を微調整する | 完全にゼロにはできないため、SLO を「P95/P99 の遅延」を含めて定義しておくと運用が楽になる |
Python関数での注意ポイント
質問で挙がることが多いのが Python 関数です。Python ランタイムはシングルスレッド中心で動作し、重い処理を抱えやすいため、プラットフォーム側が保守的な同時実行数を初期値として採用していることがあります。
メモリ 2GB の Python 関数で、HTTP 同時実行数のデフォルトが 1 に設定されているケースでは、次のような挙動になりがちです。
- 1 リクエストが少し長め(数百ミリ秒~1 秒)にかかる。
- その間、プラットフォームから見ると「このインスタンスはいっぱい」と判断される。
- 少しタイミングがずれてリクエストが来ただけで、「処理中だから新しいインスタンスを起動しよう」と判断される。
結果として、実質的にはほぼシングルリクエストしか飛んでいないのに、新しいインスタンスがどんどん起動してしまうという勿体ない状態になります。この場合、
- HTTP 同時実行数を 5 ~ 10 に引き上げる。
- 処理が CPU バウンドであれば、コード側で処理の分割・キャッシュ・I/O 待ちの非同期化を検討する。
- メモリに余裕があるかをメトリクスで確認し、足りないようならメモリサイズを上げる。
といった対策を組み合わせることで、コールドスタート頻度と不必要なスケールアウトを大きく抑えられます。
負荷試験とモニタリングの具体的な進め方
設定を変えたら、必ず負荷試験とメトリクス確認をセットで行う必要があります。ここでは、実践的な確認ポイントを整理します。
ステップ1: SLO(目標応答時間)を決める
まず、「どのレベルのレスポンス速度を目指すのか」を数値で決めます。例えば:
- 単発リクエストの 95% が 1 秒未満(P95 < 1s)
- ピーク時 1,000 並列でも P95 < 3s 以内
といった形で具体的にしておきます。これがないと、「十分速いのか、まだ遅いのか」が判断できません。
ステップ2: 負荷パターンを決める
次に、実際のユースケースに合わせて負荷パターンを決めます。代表的なパターンは以下の通りです。
- シーケンシャル(10秒おきなど): 単発テストやバッチ処理の確認に適する。
- 急激なバースト(数百~数千並列): キャンペーン開始時やバッチ集中処理などのピークを再現する。
- ゆるやかなランプアップ: トラフィックが徐々に増えていく想定でスケール挙動を見る。
HTTP 同時実行数や Always‑On 台数の組み合わせを変えながら、これらを繰り返しテストし、応答時間とスケールアウト挙動を比較していきます。
ステップ3: 見るべきメトリクス
Azure Monitor 等で、少なくとも次のメトリクスはチェックしておくと良いでしょう。
- 平均応答時間、P95、P99 応答時間
- インスタンス数(スケールアウトのタイミング)
- 1 インスタンスあたりの CPU 使用率とメモリ使用量
- エラー率(429 / 5xx など)
これらを見ながら、「同時実行数を上げた結果、インスタンス数は減ったが 1 台あたりの CPU 使用率が 90% を超えている」など、ボトルネックを特定していきます。
従来コンシュームプランとの使い分け
最後に、Flex コンシュームプランと従来コンシュームプランの使い分けの考え方を簡単に整理しておきます。
| 項目 | 従来コンシュームプラン | Flexコンシュームプラン |
|---|---|---|
| ウォーム維持の仕組み | Ping 直後 ~ 数分のみウォームを維持しやすい | Always‑On 設定で長期間ウォームを狙える |
| HTTP 同時実行の制御 | 基本的にはプラットフォーム任せ | HTTP 同時実行数を手動でチューニング可能 |
| 細かなリソース制御 | あまり柔軟ではない | メモリサイズや常時起動台数を柔軟に設定可能 |
| 適するワークロード | イベント頻度が一定以上あり、多少のコールドスタートを許容できるケース | 「単発リクエストもなるべく速く」「ピーク時には大きくスケールする」など、応答時間に厳しさがあるケース |
Flex コンシュームプランは、従来よりも細かくチューニングできる反面、「HTTP 同時実行数を適切に調整しないと本来の性能を発揮できない」という側面があります。本記事で紹介したような観点で、必ず自分のアプリケーションに合った値を探してみてください。
よくある誤解とアンチパターン
最後に、Flex コンシュームプラン + Always‑On の運用で陥りやすい誤解をまとめます。
誤解1: Always‑On にすればコールドスタートは完全になくなる
前述の通り、Always‑On は「インスタンス数を保証するためのメカニズム」であり、「インスタンスが一切リサイクルされない」という意味ではありません。基盤側のメンテナンスや過負荷による再起動はどうしても発生します。
そのため、「たまに数秒遅くなるリクエストが存在する」こと自体は、完全にはゼロにできないと考えるのが現実的です。重要なのは、その頻度をどこまで減らし、SLO の範囲内に収めるか、という視点です。
誤解2: Always‑On の台数を増やせばすべて解決する
Always‑On の台数をやみくもに増やせば、確かにコールドスタートの発生確率は下がります。しかし同時に、課金額もほぼ比例して増えるため、コスト面でのインパクトが大きくなります。
また、HTTP 同時実行数が低いままだと、インスタンス数を増やしても各インスタンスの処理能力が低いままなので、「インスタンスは多いのに、実際には並列処理があまり進んでいない」という状態になりがちです。まずは HTTP 同時実行数を適切に上げ、その上で必要な分だけ Always‑On を増やす、という順番を意識しましょう。
誤解3: HTTP 同時実行数は大きければ大きいほど良い
HTTP 同時実行数を大きくすると、1 台で処理できるリクエストが増え、スケールアウトを抑制できます。しかし、極端に大きくしてしまうと、
- 1 台に負荷が集中しすぎて CPU が張り付き、レスポンスが全体的に遅くなる。
- メモリ不足やスワップが発生し、かえってコールドスタートに似た遅延が頻発する。
といった副作用も生まれます。特に、画像処理や PDF 生成、機械学習推論など、メモリと CPU を多く消費する処理を Functions 上で行う場合は、同時実行数を控えめにした方が安定するケースも少なくありません。
結局のところ、HTTP 同時実行数は「高ければ良い」のではなく、「ワークロードに対してちょうど良い値」を探すチューニング項目です。
まとめ: HTTP同時実行数を制する者がFlexを制す
本記事では、Azure Functions Flex コンシュームプランで「Always‑On なのに応答が遅くなる」問題に対して、HTTP 同時実行数の調整を中心に解説しました。最後にポイントを整理します。
- Flex コンシュームプランの Always‑On は、インスタンスの「常駐」ではなく「常時待機」を狙う仕組みであり、インスタンスのリサイクルは発生し得る。
- HTTP 同時実行数が低すぎると、少数のリクエストでも早々にしきい値を超えてしまい、新規インスタンス起動 → コールドスタートの頻度が上がる。
- Azure ポータルの「スケールと同時実行」から HTTP 同時実行数を「手動」に切り替え、メモリサイズやランタイムに応じた現実的な値に引き上げることで、既存インスタンスを最大限活用できる。
- Always‑On 台数 × メモリ量 × 稼働時間がコストに直結するため、同時実行数の調整でインスタンス数を抑える方がコスト効率が良くなりやすい。
- 最適値はワークロードごとに異なるため、負荷試験とメトリクス監視をセットで行いながら、段階的にチューニングすることが不可欠。
Azure Functions Flex コンシュームプランの真価は、「設定を自分でコントロールできる」点にあります。Always‑On の台数だけに頼るのではなく、HTTP 同時実行数という重要なダイヤルを積極的に回しながら、応答速度とコストのベストバランスを見つけていきましょう。

コメント