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年運用して、自分たちが確立した選定基準は以下。スペック表じゃなくて、実務ベースだ。

要件ALBNLB理由
HTTPSマイクロサービスレイヤー7ルーティングで運用楽、SSL/TLSの扱いが簡単
リアルタイム通信(WebSocket)ALBも対応するが、NLBの方がコネクション管理が素直
ゲーム・IoT(UDP)×ALBはUDP非対応。NLBは標準
毎秒100万+ RPSALBでも対応可だが、スケーリング手間。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の料金表を見ると、こんな感じだ。

項目ALBNLB
ロードバランサー時間$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点:

  1. HTTPSマイクロサービスなら、迷わずALB。複雑なルーティングも簡単だし、セットアップと運用コストが低い。実運用では、この「運用コスト」が一番大事なんだ。

  2. 「超低レイテンシ」と「毎秒100万+ RPS」が本当に必要なら、NLB。ただし、ヘルスチェック設定やクロスゾーン設定など、細かいトラップがある。3回確認すること。やってから後悔するのが一番高くつく。

  3. 監視を最初から入れる。「気づかなかった」ほど怖いことはない。セッションロスト、コスト爆増、ターゲット障害。すべて監視で防げるんだ。

次のプロジェクトで迷ったら、このポイントをチェックリストにして判断してほしい。自分たちも今でも、新しいロードバランサー構成を決めるときは、このリストを見返してる。それくらい重要な判断だから。

U

Untanbaby

ソフトウェアエンジニア|AWS / クラウドアーキテクチャ / DevOps

10年以上のIT実務経験をもとに、現場で使える技術情報を発信しています。 記事の誤りや改善点があればお問い合わせからお気軽にご連絡ください。

関連記事