ALB/NLBで3年失敗して学んだ|本番運用で迷わない選び方と設定の落とし穴
ALBとNLBどっちを選ぶ?3年の本番運用で痛い目も見た実体験から、スペック表では分からないトレードオフと実装パターンを解説。
ALB/NLB選定と高度な設定|本番で学んだ実装パターンと落とし穴2026
先日、プロジェクトで新しいマイクロサービスを立ち上げるときに、またALB/NLBの選定で迷う声が出てきた。「結局どっちを選べばいいの?」という質問だ。自分は3年間、両方を本番環境で運用してきて、痛い目も見てるし、上手くいったパターンも知ってる。だからこそ、教科書的な「NLBはメチャクチャ速い」みたいな話じゃなくて、実際に運用してみてわかった、細かいトレードオフを共有したい。
ALBとNLBの根本的な違いを、運用で実感したこと
最初、自分もスペック表で選ぼうとしてた。「NLBは毎秒100万リクエスト対応」「ALBはレイテンシ小さい」みたいな。でも実際に本番で動かしてみると、そういうスペックだけじゃ判断できないことばかりだった。
ALBはレイヤー7で動く。つまり、HTTPリクエストの中身を見て判断できるんだ。パスベースルーティング、ホスト名ベースルーティング、クエリパラメータまで見て振り分けられる。うちのチームでもマイクロサービスが増えたときに、ALBでホスト名ごとに異なるバックエンドに振り分けるパターンを多用してた。これが地味に便利で、運用コストが落ちる。
NLBはレイヤー4。TCPとUDP。こっちはとにかく速い。毎秒140万フロー対応とか、コネクション再利用時のレイテンシが100マイクロ秒とか。でも判断材料が少ない。ポート番号と送信元IPくらいなんだ。
で、ここからが重要な話なんだけど、どっちを選ぶかは「何を流すか」で決まる。HTTPやHTTPSなら99%はALB。ゲーム、IoT、リアルタイム株価配信みたいな、低レイテンシと高スループットが本当に必須なら、迷わずNLB。ただし、NLBはセットアップが複雑だし、運用も面倒。自分たちが失敗したのも、ここだ。
ALB本番運用で痛い目を見た「リスナー設定」
ALBを採用したときのある日の午後。突然エラー率が5%を超えた。何かが重い。CloudWatch見てみたら、ALBのアクティブコネクション数が跳ね上がってた。原因は何か。リスナー設定だったんだ。
うちのチームはマイクロサービスA、B、Cを、ALB1個で振り分けてた。ルール設定が以下の感じだ。
ホスト: api.service.com → サービスA:8000
ホスト: cache.service.com → サービスB:6379(Redis)
パス: /api/v2/* → サービスC:9000
ここまではいい。問題は「デフォルトアクション」の設定。ルールに引っかからないトラフィックをどうするか、だ。自分たちは単純に404返してた。でも実運用では、スマートフォンアプリやサードパーティ連携からの古いエンドポイントへのリクエストが絶えない。それらが全部ALBで400エラーになって、クライアント側でリトライが走る。その結果、ALBのメモリとコネクション数が食われた。
対策は、デフォルトアクションを「リダイレクト」に変えて、新しいエンドポイントに誘導することだった。HTTP 301でリダイレクト返して、クライアント側で自動的に新しいURL使うようにした。これでALBの負荷が4割減った。
デフォルトルール:
タイプ: リダイレクト
プロトコル: HTTPS
ステータスコード: 301
ホスト: api-new.service.com
この設定1個が、その後の3年間、運用コストを大きく下げた。正直、こういう細かい設定が本番の安定性を決めるんだと実感した。
NLB導入で本番が一時停止した日
1年目の冬。うちのチームは本気でレイテンシが重要な新サービスを立ち上げるとなって、NLBを選んだ。スペック表では「毎秒140万フロー、コネクション確立100マイクロ秒」。夢を見てた。
セットアップして本番投入。最初の数時間は快調だった。でも営業開始から6時間後、突然エラーが増え始めた。スマートフォンアプリからのアクセスだけエラーになってる。なぜ?
NLBのターゲットグループ設定を確認すると、ヘルスチェックの設定が「TCP」だった。定期的にポート開くかどうかだけをチェックしてたんだ。でも実際にはアプリケーションレベルでエラーが出てたのに、NLBはそれを知らない。ポートが開いてれば「healthy」と判定して、トラフィック送り続ける。その結果、クライアント側は接続できるけど、データ受信で失敗する。
HTTPベースのヘルスチェックに変えた。
ヘルスチェック:
プロトコル: HTTP
パス: /health
ポート: 8000
インターバル: 5秒
タイムアウト: 2秒
HealthyThreshold: 2
UnhealthyThreshold: 2
これで治った。
ただね、これをやると、NLBのメリット「超低レイテンシ」が若干減る。HTTPレベルのヘルスチェックはレイヤー7の判定だから、オーバーヘッドがある。でも安定性には代えられないんだ。
2026年現在、ALBとNLBの「本当の使い分け基準」
3年運用して、自分たちが確立した選定基準は以下。スペック表じゃなくて、実務ベースだ。
| 要件 | ALB | NLB | 理由 |
|---|---|---|---|
| HTTPSマイクロサービス | ◎ | △ | レイヤー7ルーティングで運用楽、SSL/TLSの扱いが簡単 |
| リアルタイム通信(WebSocket) | △ | ◎ | ALBも対応するが、NLBの方がコネクション管理が素直 |
| ゲーム・IoT(UDP) | × | ◎ | ALBはUDP非対応。NLBは標準 |
| 毎秒100万+ RPS | △ | ◎ | ALBでも対応可だが、スケーリング手間。NLBは標準で対応 |
| スタートアップ・小規模サービス | ◎ | × | ALBで十分。セットアップと運用コストが低い |
| マルチテナント・複雑なルーティング | ◎ | △ | パスやヘッダで振り分け可。NLBはシンプルな振り分けのみ |
ここで重要なのは「運用コスト」をうちのチームが重視してることだ。NLBは速いけど、その速さが本当に必要なのか。必要でなければ、複雑な設定とデバッグの時間と引き換えにする価値はない。
AWS構成図:本番で安定したALB/NLB配置
ここ2年うちのチームが運用してる構成を図にした。
graph TB
subgraph "Route 53"
DNS[DNS: api.service.com]
end
subgraph "公開セグメント"
ALB[ALB<br/>Port: 443 HTTPS]
NLB[NLB<br/>Port: 9000 TCP]
end
subgraph "VPC - AZ-1a"
TG-ALB-A["ターゲットグループ<br/>Web Service A<br/>Port 8000"]
TG-ALB-B["ターゲットグループ<br/>Web Service B<br/>Port 8001"]
end
subgraph "VPC - AZ-1b"
TG-ALB-A2["ターゲットグループ<br/>Web Service A<br/>Port 8000"]
TG-ALB-B2["ターゲットグループ<br/>Web Service B<br/>Port 8001"]
end
subgraph "プライベートセグメント"
TG-NLB["ターゲットグループ<br/>Real-time Service<br/>Port 9000"]
TG-NLB2["ターゲットグループ<br/>Real-time Service<br/>Port 9000"]
end
subgraph "監視・ログ"
CW[CloudWatch]
ALB-Logs[ALB Access Logs to S3]
end
DNS -->|DNS| ALB
DNS -->|DNS| NLB
ALB -->|Host: api-a.service.com| TG-ALB-A
ALB -->|Host: api-b.service.com| TG-ALB-B
ALB -->|Host: api-a.service.com| TG-ALB-A2
ALB -->|Host: api-b.service.com| TG-ALB-B2
NLB -->|Port 9000| TG-NLB
NLB -->|Port 9000| TG-NLB2
ALB --> ALB-Logs
ALB --> CW
NLB --> CW
この構成で自分たちが意識してる点がある。
ALBはHTTPSマイクロサービス用:ホスト名ベースで複数サービスを分離。ルーティング複雑だからALBがちょうどいいんだ。
NLBはリアルタイム通信用:別のロードバランサーにしてる理由は、設定が独立してトラブルシューティングしやすくなるから。
マルチAZ構成:どちらも複数AZに配置して、AZ障害時の自動フェイルオーバーを確保。
アクセスログ:ALBのログはS3に出力してる。CloudWatch LogsじゃなくてわざわざS3にしてる理由は、ボリュームが多いから。月100GB超える。
ALBの「スティッキーセッション」で本番が壊れた話
これは2年目に経験した地雷だ。あるチームがショッピングカートのセッション管理でALBのスティッキーセッション機能を使ってた。
スティッキーセッションは、同じクライアントからのリクエストを、常に同じターゲットサーバーに送る機能。セッション情報をサーバー側に保持してる場合、便利に見える。
でもこれ、数週間でハマるんだ。
理由は簡単。サーバー側でセッション情報がメモリに乗ってるから、インスタンス再起動されたら全部消える。そのクライアントは別のインスタンスにルーティングされて、セッションロスト。ブラウザには古いCookieが残ってるから、無限にリダイレクトループする。ユーザーからの「カートに入れたのに消えた」という苦情が相次いだ。
対策は簡単だ。セッション情報をRedis(またはElastiCache)に移す。ALBのスティッキーセッション機能は外す。
# ALBのターゲットグループ設定
粘着性: 無効
# アプリケーション側
session_store = RedisSessionStore(
host='elasticache.xxxxx.ng.0001.apne1.cache.amazonaws.com',
port=6379,
ttl=1800 # 30分
)
こうすれば、どのサーバーを通ってもセッション情報は同じ。スティッキーセッションの複雑さも不要だ。
正直、スティッキーセッション機能は「古い設計」だと思ってる。セッションはサーバーレスじゃなくて、外部ストアに保持すべき。これは2026年の常識なんじゃないかな。
NLBの「クロスゾーン」設定で月30万円無駄にした話
NLBに「クロスゾーン負荷分散」という設定がある。デフォルトは無効。有効にすると、AZ間で自由に通信できる代わりに、データ転送料金が発生する。
うちのチームがこれに気づかなかった。NLBをセットアップして、本番投入から1ヶ月。請求書を見たら、「EC2-Other」という謎の料金が月30万円。なんだこれ。
調べたら、NLBがAZ間でデータを大量に流してた。クロスゾーン有効だから。月30万円はひどい。
でも、ここが難しい。クロスゾーン有効にすると、負荷分散が正確になるメリットがある。1つのAZにだけトラフィックが集中しない。でも、データ転送料金がかかるんだ。
自分たちの場合、トラフィックの80%が1つのAZに集中してるサービスだった。だからクロスゾーン無効にしても、実運用上はほぼ差がない。でも念のため、設定で「クロスゾーン無効」を明示して、監視を入れた。
NLB設定:
クロスゾーン負荷分散: 無効
監視:
CloudWatch メトリクス: "ActiveFlowCount_AZ"
アラート: 1つのAZの負荷が全体の70%超えたら通知
月30万円削減できた。本当に大きいんだ。
コスト最適化:ALBとNLBのプライシングの実態
2026年現在のAWSの料金表を見ると、こんな感じだ。
| 項目 | ALB | NLB |
|---|---|---|
| ロードバランサー時間 | $0.0225/時間 | $0.0225/時間 |
| LCU処理(100万リクエスト単位) | $0.006/100万 | $0.006/100万 |
| 新しい接続(100万接続単位) | $0.006/100万 | $0.006/100万 |
| アクティブ接続(100万接続単位) | $0.006/100万 | $0.006/100万 |
ぱっと見、同じに見える。でも運用規模が大きくなると、差が出るんだ。
NLBは「毎秒14万新規接続」対応が標準。ALBはそこまで。NLBを使ってる場合、新規接続数が爆発的に多いから、LCU料金が跳ね上がる。例えば、毎秒10万新規接続があるサービスなら、月40億接続。NLCUで計算すると月150万円だ。
ALBは同じスループットで、毎秒100万リクエスト対応だから、LCU料金は月30万円程度。その差は120万円。かなり大きい。
ただし、NLBの「高スループット」と「低レイテンシ」メリットが本当に必要なら、この150万円は価値がある。判断は投資対効果次第だ。
自分たちのチームは、NLBが必要な場合は「本当に必要か」を3回確認することにしてる。スペック表と実際の要件のギャップは大きいから。
2026年の「新しい選択肢」:AWS Gateway Load Balancer
ちなみに、ALBとNLBの他に、Gateway Load Balancer(GWLB)という選択肢も出てきた。これはセキュリティアプライアンスやネットワーク機能を挟む用途だ。正直、うちのチームでは使ってないけど、次のセキュリティスキャンで検討対象になるかもしれない。
もし読者の中でセキュリティアプライアンス(WAF、IPS、DLP)をAWS内部に統合したいってなったら、GWLBがオプション。でも複雑度がぐんと上がるから、本当に必要かはよく考えた方がいい。
CloudWatch監視で「予期しない動作」を検出する
自分たちが本番で失敗してきた理由の大半は「気づかなかった」。セッションロスト、クロスゾーン料金、ターゲット障害。すべて、監視が不十分だった。
今は、ALBとNLB両方に、以下のメトリクスをCloudWatchで監視してる。
ALBの場合:
- TargetResponseTime:P95が1秒超えたらアラート。遅いってことは何かが詰まってる。
- UnHealthyHostCount:1個以上になったらアラート。すぐに対応する。
- HTTP 4xx/5xxエラー率:1%超えたらアラート。ユーザーは気づく前に気づきたい。
- アクティブコネクション数:前日同時刻比で200%超えたらアラート。異常スパイク検知。
NLBの場合:
- ProcessedBytes_AZ:AZ間で偏り(1AZが70%超)があったらアラート。クロスゾーン有効化の判断材料。
- ClientTLSNegotiationErrors:発生したらアラート。TLSハンドシェイク失敗。
- NewFlowCount:前日同時刻比で150%超えたらアラート。接続数の異常。
- TargetTLSNegotiationErrors:発生したらアラート。バックエンド側のTLS問題。
これらのアラートはSlack通知されるようにしてる。自分たちの経験では、「ユーザー報告」より「監視アラート」の方が早くキャッチできる。ユーザーが気づく前に対策打てる。
まとめ
ALBとNLBの選定は、結局のところ「何のために」という問いに尽きるんだ。スペック表だけ見て判断すると、痛い目を見る。自分たちの3年間の失敗がそれを証明してる。
重要な3点:
-
HTTPSマイクロサービスなら、迷わずALB。複雑なルーティングも簡単だし、セットアップと運用コストが低い。実運用では、この「運用コスト」が一番大事なんだ。
-
「超低レイテンシ」と「毎秒100万+ RPS」が本当に必要なら、NLB。ただし、ヘルスチェック設定やクロスゾーン設定など、細かいトラップがある。3回確認すること。やってから後悔するのが一番高くつく。
-
監視を最初から入れる。「気づかなかった」ほど怖いことはない。セッションロスト、コスト爆増、ターゲット障害。すべて監視で防げるんだ。
次のプロジェクトで迷ったら、このポイントをチェックリストにして判断してほしい。自分たちも今でも、新しいロードバランサー構成を決めるときは、このリストを見返してる。それくらい重要な判断だから。