ALBからNLBに乗り換える

エアクロゼットのエンジニアのアインです。最近、私たちがアプリケーションロードバランサーからネットワークロードバランサーに切り替えしたばかりなので本ブログでその経験についてシェアさせていただきます。

## ALBとNLBの概要

### ALBとは

ALB(Application Load Balancer - アプリケーションロードバランサー)は第7層(OSIモデルのアプリケーション層)で動作するロードバランサーです。

![Screen Shot 2022-12-18 at 19.00.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135669/fcce56b0-7347-271a-0c8e-cd122b44d283.png)

ALBは、`HTTP`および`HTTPS`プロトコルを使用したアプリケーションの負荷分散をサポートします。
ALBがサポートする主な機能は:
- Layer-7 Load balancing: EC2、マイクロサービスなどのターゲットへのHTTPおよびHTTPSリクエストを分散できます。
- HTTPSをサポートします。
- 転送: あるURLから別のURLへの転送ができます。

注意点としてはALB作る時に必ずALBに対応する`Security Group`がを設定しないといけません。

### NLBとは
NLB(Network Load Balancer - ネットワークロードバランサー)は第４層(OSIモデルのトランスポート層)で動作するロードバランサーです。

![Screen Shot 2022-12-18 at 18.59.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135669/9d83c891-707c-23c6-6f61-1e479613ffd0.png)

NLBがTCP, UDP, TCP_UDP, TLSをサポートしています。NLBがトラフィックの多いアプリを対応できるように設計されています。また非常に低いレイテンシで一秒あたり数百万リクエストが対策できます。

NLBがサポートする主な機能は:
- Layer-4 Load balancing: EC2、マイクロサービスなどのターゲットへのTCP及びUDPリクエストが分散できます。
- 低いレイテンシ。
- クライアントセッションターミネーションをサポートするし、ロードバランサーへのTLS終了タスクのオフロードができます。

NLBを作る時に`Security Group`設定が**不要となります**。

### ALBとNLBの違う点

`ALB`が**第7層**に動作するから`context`また`cookieデータ`を結構気にするんですが`NLB`が**第4層**に動作するから特にネットワーク周りの情報のみ気にしています。

他は:
- NLBがトランスポート層で動くから単なるリクエストをターゲットにフォワードするだけですけどALBがルーティングのためリクエストのヘッダの内容を見ています。
- NLBがトランスポート層で動作するのきっかけでアプリケーションの可用性をあまり担保できなくて、普通にはNLBがサーバーがICMP pingに応答できるかどうか、またはサーバーがTCPハンドシェイクを保証できるかどうかによって、この可用性を定義します。ALBが成功なHTTP GETリクエストを通じてアプリケーションの可用性が全然担保できます。
- 例として2つアプリケーションAとBが同じIPアドレスを使用すればNLBが２つのアプリが全く分別できないからNLBがオフラインアプリあるいはクラッシュ中アプリにリクエスト送る可能性があります。

## 現状のシステム
現状のエアクロゼットのアプリ、Webのインフラはこんな感じです。

![Screen Shot 2022-12-18 at 21.45.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135669/872aba3f-8f45-272f-16d6-37a7543949ef.png)

### システムの説明

エアクロはWebユーザとモバイルアプリのユーザ両方とも対応しているので2つホストがあり:
- www.air-closet.com: Web用ホスト
- api.air-closet.com: API用ホスト

2つホストが1つのみALBを向いています。ALBには次通りにルーティングしています:
- `HTTP`でアクセスする場合`HTTPS`に転送します。
- ホスト`api.air-closet.com`だったらAPI EC2インスタンスと紐づく`api-target-group`にルーティングします。
- ホスト`www.air-closet.com`の場合はURIをチェックして `/api`形式ならAPI EC2インスタンスと紐づく`api-target-group`にルーティングします。
- 残りとデフォルトはWeb EC2インスタンスと紐づく`web-target-group`にルーティングします。

具体的にはこれです。

![Screen Shot 2022-12-18 at 22.01.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135669/d9e7857c-5389-bfab-9284-0b3c55c4550b.png)

### 出会った問題

モバイルアプリとWebの両方で訪問者数が増加するピーク時に、次の2つのことをやらないといけないです。
- ALBのアクセスキャパシティを増えるためAWSにELB暖機申請が必要でやらなければアプリとWebがめっちゃくちゃ遅くなるはずです。
- API EC2インスタンスを増強します。

基本的にはAPI EC2インスタンス増強のことが全然避けられないですがELB暖機申請がさようならできると思ってます。そのためにALBからNLBに切り替えようと考えております。理由としてはNLBの概要通り

> NLBがトラフィックの多いアプリを対応できるように設計されています。また非常に低いレイテンシで一秒あたり数百万リクエストが対策できます。

ということなんです。

## 切り替え後のシステム
切り替えした後のシステムはこんな感じとなっております。

![Screen Shot 2022-12-18 at 22.09.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135669/37841889-767c-7a1f-869b-496575a22c7f.png)

とても複雑に見えますよね？ 順を追って説明しますのでご安心ください。

### 切り替え後の問題

インフラを変えると問題が発生する可能性が結構高いですよね。下記は我々が出会った問題です。
- どうやってWebからAPIに呼び出せる？？？ 元々WebからAPIにルーティングするはALBのボールですけどNLBがリクエストもらう際にすぐターゲットにフォワードするからルーティングを全然やらないです。
- どうやって`HTTP`から`HTTPS`に転送できる？？？ALBには転送ルールが設定できるんですがNLBにとっては全然無理です。

### 問題を解決する
実際にはWeb EC2インスタンスの中に`nginx`を設定しており、目的はnginxが`Reverse Proxy`としてWebに守ってあげます。
ですのでルーティングを全部`nginx`に移動しようと思ってます。

`どうやってWebからAPIに呼び出せる？？？`問題に対してはこんな感じでnginxを設定する

```nginx
server {
  listen 80 default_server;

  location /api* {
    proxy_pass https://api.air-closet.com;
  }
}
```

そうすればAPIルーティングはこんな感じになっています。

![Screen Shot 2022-12-18 at 22.24.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135669/f3051ec4-6b56-00b3-c9bb-fdb09428bccd.png)

WebからのAPI呼び出しのURLは`https://www.air-closet.com/api/*`という形式になり、これらの呼び出しはNLBによって処理されず、EC2インスタンスに直接送信されて、ここで、nginxはURIが`/api*`に類似していることを認識しているため、nginxはこの呼び出しをAPIホスト`https://api.air-closet.com`にフォワードします。

ではAPIルーティングはこれで対応済みです。これから`HTTP`から`HTTPS`に転送するのを見てみます。

Web用のNLBに2つリスナーを設定します。
- `TCP:80` - `HTTP`プロトコル専用。
- `TLS:443` - `HTTPS`プロトコル専用。

![Screen Shot 2022-12-20 at 12.57.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135669/857f9508-765e-0820-d72c-73fb23893cc5.png)

![Screen Shot 2022-12-18 at 17.33.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135669/39d2d55b-7e06-b31e-4a30-766d5fe013af.png)

これらの各リスナーに対応するのは、次のターゲットグループです。
- TCP:81 - `http-web-target-group`: 81ポートにリッスンする
- TCP:80 - `https-web-target-group`: 80ポートにリッスンする。

![Screen Shot 2022-12-18 at 17.30.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135669/7af72024-0c5b-72d6-4b97-f5b30412af16.png)
![Screen Shot 2022-12-18 at 17.30.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135669/53439e7e-abdc-237f-c39e-adecc976a00e.png)

まとめするとこんな感じになっております。

![Screen Shot 2022-12-19 at 22.23.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135669/475daa6b-53ad-b7e9-3fae-39290846f1ea.png)

NLBがトランスポート層に動作するからNLBと繋がってるターゲットグループのプロトコルは必ず**TCP**です。

上に書いている通り

> 実際にはWeb EC2インスタンスの中にnginxを設定しており、目的はnginxがReverse ProxyとしてWebに守ってあげます。

デフォルトに`nginx`が80ポートにリッスンするんですが今回で言うと**80ポート**だけではなくて**81ポート**にもハンドリング必要なんで`nginx`に**81ポート**にリッスンする設定を追加しようと思っております。こんな感じですね。

```nginx
server {
  listen 81 default_server;

  return 301 https://web.com$request_uri;
}
```

説明するとリクエストが**81ポート**に来るならいつも`HTTPS`に転送されています。
だから今のNLB、ターゲットグループとWeb EC2インスタンスはこんな感じになります。

![Screen Shot 2022-12-19 at 22.32.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135669/8ab7082b-9370-e3dd-71d1-0e51ccd3c9f0.png)

簡単に説明するとユーザーが`http://www.air-closet.com`にアクセスすると、リクエストはNLBによって`http-web-target-group`に転送されて、このターゲットグループはまたリクエストをWeb EC2インスタンスにフォワードします。インスタンスの81ポートにリッスンしているnginxがリクエストを受け取り`https://www.air-closet.com`にリダイレクトします。

ルーティングとリダイレクトは一旦大丈夫ですけど`Security Group`はどうなるかなって思っております。今回はNLBを使ってるから全てのリクエストがEC2インスタンスに転送されるのでインスタンスが**全てのIPアドレス**からHTTPのリクエストをもらえるように設定が要ると考えていますので一応下の感じで設定しています。

![Screen Shot 2022-12-18 at 17.27.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135669/dfed3cb3-c7d7-60a4-f3d3-c485e9dd9715.png)

簡単に言うと`全てのIPアドレス(0.0.0.0/0)`から`HTTP`と`TCP`リクエストを`80ポート`と`81ポート`で受信しています。

## システムの最終の形

システムの最終の形はこんな感じになっております。

![Screen Shot 2022-12-20 at 13.04.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135669/6e341a03-becb-070a-8d18-2ad77763b6df.png)


旧システムと比べるとかなり複雑に見えますが、今のNLBがリクエストを転送するだけでルーティングとリダイレクトを担当してないからALBより暇となっております。ですからAWSに暖機申請が不要になります。

これからまた別の問題が発生するかもしれないと思いますが解決できればまた新しい知識が勉強になりますので引き続きよろしくおねがいします。

では今回のALBとNLBのブログが以上となります、また別のブログにお会いましょう。