ALB配下のEC2でnginxって要る?フレームワーク別に見てみた

  • AWS
  • ALB
  • EC2
  • nginx
  • Infrastructure

この記事は、構成と文章の整理にAIを使っています。
あくまで読みやすくするための補助で、内容そのものをAIにすべて任せて書いているわけではありませんが、ところどころ相談しながら整えています。

きっかけ

AWSでALB配下にEC2を置いてWebアプリを動かすとき、たまに迷うことがある。

アプリを3000番ポートとかで単体起動した場合、ALB → nginx → app にすべき?それとも ALB → app で直結して問題ない?

このあたりの判断が本当に妥当なのか、あらためて整理してみた。

結論:単一アプリならALB直結でOK

先に結論を書くと、単一アプリのシンプルな構成なら ALB → app の直結で問題ない。

ALBはL7ロードバランサーとして、TLS終端・ヘルスチェック・パスベース/ホストベースのルーティングなどをすでにこなしてくれる。単一アプリでこの範囲の機能が足りているなら、わざわざnginxを挟む理由がない。

nginxを挟んだほうがいいケース

とはいえ、以下に当てはまるならnginxを検討する価値はある。

  • 静的ファイル配信を分離したい(アプリ本体にやらせるより効率的)
  • gzip圧縮・キャッシュ制御を細かく調整したい
  • バッファリング/keep-aliveをアプリの手前で吸収したい
  • 同一EC2内で複数アプリをルーティングしたい(ALB側のリスナー/ターゲットグループを増やしたくない場合)
  • ALBのヘルスチェックの限界を補いたいとき。ALBはHTTPステータスコードだけで健全性を判定するので、アプリが200番台のエラーページを返す設計だと不健全な状態を見逃すことがある。nginx側でpassiveなヘルスチェックを組めば補完できる

直結する場合の実務上の注意点

nginxを挟まずALBから直結する場合、以下は意識しておいたほうがいい。

  • アプリ側の keep-alive timeoutをALBのidle timeout(デフォルト60秒)より長く設定する(逆だと接続が中途半端に切れる)
  • graceful shutdownをアプリ側で実装しておく(デプロイ時にALBのderegistration delayと噛み合わないと502が出る)
  • セキュリティグループで ALBからのみアプリのポートを許可する

「組み込みHTTPサーバ=開発用」は思い込みだった

今回整理していて気づいたのが、「アプリの組み込みHTTPサーバは開発用途」という思い込みが、実は言語・フレームワークによって全然違うということ。

これはDjangoのrunserverのような「明確に開発専用」なケースに引きずられていた感覚で、Node.jsなどは組み込みサーバがそもそも本番運用前提で設計されている。

フレームワーク別・ALB直結可否リスト

ALBがTLS終端とヘルスチェックを担っている前提で整理するとこうなる。

言語/フレームワーク 組み込みサーバ ALB直結 理由
Node.js(Express, Fastify, AdonisJS) 標準 ○ 本番運用前提の設計。長時間プロセス常駐が標準モデル
Go(net/http, Gin, Echo) 標準 ○ 標準ライブラリ自体が本番グレード
Rust(actix-web, axum) 標準 ○ 非同期ランタイム前提で高性能、そのまま公開できる設計
Laravel Octane(Swoole/RoadRunner/FrankenPHP) Octane導入時 ○ 常駐ワーカー型に変わるのでNode.jsと同じ扱いになる
Elixir Phoenix(Cowboy/Bandit) 標準 ○ BEAM/OTP前提で堅牢、そのまま本番運用される文化
ASP.NET Core(Kestrel) 標準 △ Microsoft公式は「必須ではないが推奨」というスタンス。ALBがTLS終端とヘルスチェックをやっているなら直結でも実質問題ない
Java(Spring Boot、組み込みTomcat/Netty) 標準 ○ 組み込みTomcatも本番運用前提
Python(FastAPI + Uvicorn/Gunicorn) 標準+ASGI ○ Uvicorn/Gunicorn自体が本番運用向け
Ruby(Rails + Puma) 標準(Puma) ○ Pumaはマルチスレッド対応の本番グレードサーバ(旧WEBrickは非推奨)
PHP(素のLaravel, Symfony等) php artisan serve等 × リクエストごとにフレームワーク全体を起動し直す仕組みで、組み込みサーバー自体が開発専用。nginx/Apache + PHP-FPMが必須
Python(Django) runserver × 完全に開発専用、公式でも本番禁止。Gunicorn/uWSGI等の別サーバが必要(ただしそのサーバ自体は直結でも可)

ざっくり見分けるなら、

  • 常駐プロセス型(Node, Go, Rust, Phoenix, Java, Octane, Puma, Uvicorn/Gunicorn)→ 基本直結OK
  • リクエストごとに起動し直す型(素のPHP, Django runserver)→ 別途本番サーバが必須。ただしそのサーバ自体は大抵直結できる

という軸で整理すると分かりやすい。

nginx併用がよく見られる理由(ALB構成では該当しにくい)

世の中の記事でnginxを併用している例が多いのは、以下のような理由がほとんどで、ALB構成では該当しない。

  • TLS終端を自前でやりたい(自宅サーバーでcertbotを使いたいなど)
  • サブパス配信をしたい(example.com/code のような形にしたい場合)
  • 同一ホストに他のサービスも同居させたい

まとめ/チェックリスト

ALB配下でアプリを直結するかどうかを検討するときは、以下を順に確認するとよさそう。

  1. アプリのフレームワークは常駐プロセス型か?(上の表を参照)
  2. ALBの標準機能(TLS終端/ヘルスチェック/パスルーティング)だけで足りるか?
  3. 静的配信・細かいバッファ制御・複数アプリの内部ルーティングなど、ALB以上の制御が必要か?
  4. keep-alive timeout、graceful shutdown、セキュリティグループの設定は済んでいるか?

nginxを外して直結にする(あるいはその逆をやる)場合は、切り替え後に以下も忘れずに。

  • ALBのヘルスチェックパスがアプリ側で正しく応答するか確認する
  • 旧構成のnginx設定・関連セキュリティグループルールなど、不要になった設定は片付けておく

というわけで、ALB配下のリバースプロキシ要否はこんな感じ。単純な構成なら直結、フレームワーク特有の事情がある場合だけ挟む、くらいの温度感で考えておけば大きく外さないと思う。

なお、どうしてもnginx+Node.jsの2コンテナ構成をECS Fargateで続ける場合のCPU/メモリ配分は、ECS FargateでnginxとNode.js動かすなら、まずtask cpu 512 / memory 1024でよさそう にまとめた。

また、ゼロトラスト寄りにALB↔バックエンドもHTTPSにする場合、ALBはターゲット証明書のチェーンを検証しない(自己署名でも通る)。当時nginxでTLS終端していた検証メモは ALBはバックエンド証明書を検証しない、が公式仕様だった に書いた。Node.js直結でも、アプリ側でTLSを受ければ同じ話になる。