L4/L7 ロードバランサーを理解する

# はじめに

L4とL7のロードバランサーの違いを説明できるようになるために学習したことをまとめます。

クラウドにも以下のようなサービスが展開されていることはご存じの人も多いでしょう。

これらのサービスの違いを説明できるようになるためのベースとなる知識を付けていきます。

- Azure
    - [Azure Load Balancer](https://learn.microsoft.com/ja-jp/azure/load-balancer/load-balancer-overview)
    - [Azure Application Gatewy](https://learn.microsoft.com/ja-jp/azure/application-gateway/overview)

- AWS
    - [Network Load Balancer](https://docs.aws.amazon.com/ja_jp/elasticloadbalancing/latest/network/introduction.html)
    - [Application Load Balancer](https://docs.aws.amazon.com/ja_jp/elasticloadbalancing/latest/application/introduction.html)


今回は一般的なL4とL7のロードバランサーを理解したの後、Nginxで実際に実装してみます。

# ロードバランサーとは？

- 負荷分散装置
- 受診したトラフィックを複数のサーバーに振り分ける（サーバーダウンを防ぐ）
- ダウンしたサーバーには振り分けない（死活監視的な機能）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2518403/85f89769-bbda-1707-a98f-a67b8cd900a9.png)

参考図：https://www.ntt.com/bizon/glossary/j-r/load-balancer.html

より詳細を知りたい方は以下を参照。

https://www.nginx.co.jp/resources/glossary/load-balancing/

# OSI基本参照モデル

そもそもL4とL7とは何なのか考えましょう。

>OSI参照モデルとは、コンピュータ間通信を行う際に必要とされる機能を整理したものです。 1977年3月にISO(国際標準化機構)の委員会が設置され、策定が始まりました。 当時コンピュータ間通信はメーカー独自仕様が乱立していて、 互いに通信することが難しかったため、 標準的なネットワークとしてOSI(Open Systems Interconnection)を作成する気運が高まりました。 そこで開発に先立って必要な機能を整理するため、OSI参照モデルが策定されたというわけです。 最終的にISO 7498(*1)として規格化されています。
引用：https://www.nic.ad.jp/ja/basics/terms/osi.html

特徴としては、以下のように機能を７階層に整理したことです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2518403/3fb71498-6ca9-391f-0baa-ea1ce773f6d5.png)

参考図：https://www.itmanage.co.jp/column/osi-reference-model/

１～４層を下位層、５～７層を上位層と呼び、下位層はシステム側に近い機能、上位層は人間に近い機能を規定しています。

L4とL7というのは、OSI基本参照モデルの４層（トランスポート層）、７層（アプリケーション層）を指します。

L4（トランスポート層）の役割は、
>ノード間の通信制御に関して規定しています。
送信元から送られたデータは様々な機器を経由して宛先に届き、経由している中でデータが欠けてしまうと宛先でデータを正しく処理をできなくなってしまいます。
そのようなことにならないように、データが欠けることなく届けるといった通信の信頼性に関する機能が提供されています。
引用：https://qiita.com/nakamura_slj/items/0c827de1dab6ebc722d2

L7（アプリケーション層）の役割は、
>ユーザーが利用するアプリケーション間での通信に関して規定をしています。
WebブラウザであればWeb用のプロトコルとなり、ユーザーが利用するアプリケーションで使用するプロトコルが規定されます。
引用：https://qiita.com/nakamura_slj/items/0c827de1dab6ebc722d2

# L4とL7のロードバランサーの違い

OSI基本参照モデルを踏まえて、結局どのように振分けを行うのかというと、

- <b>L4ロードバランサーの特徴</b>
    - IPアドレスやポートを利用して振り分ける（レイヤー４の負荷分散）
- <b>L7ロードバランサーの特徴</b>
    - URLパスやHTTPヘッダーを利用して振り分ける（レイヤー７の負荷分散）

また、Nginxの記事も引用しておく。

>レイヤー4ロードバランシングは、中間のトランスポートレイヤーで動作します。このレイヤーは、メッセージの内容に関係なくメッセージのデリバリーを処理します。TCP（Transmission Control Protocol）は、インターネット上のHTTP（Hypertext Transfer Protocol）トラフィック向けのレイヤー4プロトコルです。レイヤー4のロードバランサーは、パケットの内容は検査せず、上流のサーバとの間で単純にネットワークパケットの転送を実行します。また、TCPストリームの最初の数パケットを検査し、限定的にルーティングを決定できます。

>レイヤー7ロードバランシングは、さらに上位のアプリケーションレイヤーで動作します。このレイヤーでは、各メッセージの実際の内容が処理されます。HTTPは、インターネット上のWebサイトのトラフィックに使用される主要なレイヤー7プロトコルです。レイヤー7のロードバランサーは、レイヤー4のロードバランサーに比べて、はるかに高度な方法でネットワークトラフィックをルーティングします。特に、HTTPなどのTCPベースのトラフィックを対象とします。レイヤー7のロードバランサーは、ネットワークトラフィックを終端し、中のメッセージを読み取ります。メッセージの内容（URLやCookieなど）に基づいて、ロードバランシングの判断を行うことができます。続いて、選択した上流サーバに新しいTCP接続を実行（または、HTTPキープアライブによって既存の接続を再利用）して、サーバにリクエストを書き込みます。
引用：https://www.nginx.co.jp/resources/glossary/layer-7-load-balancing/

# 要するに。。

トランスポート層はTCP/UDPプロトコルが使われていて、これらはクライアントとサーバー間での通信チャネル（ポート）の提供、通信管理を行う。したがって、L4ロードバランサーはポートで振分けができるようになっている。

また、IPアドレスによる振り分けは３層のネットワーク層のIPプロトコルから取得して実現しているとのこと。

アプリケーション層の特徴は、HTTPプロトコルが使われており、URL、Cookie、HTTPヘッダーなどが取得できる。したがって、L7のロードバランサーではメッセージの内容（URLやCookie）に基づいて、負荷分散することができる。

# クラウドサービスを振り返る

最初に述べたクラウドのロードバランサーを振り返ると、
- Azure
    - [Azure Load Balancer](https://learn.microsoft.com/ja-jp/azure/load-balancer/load-balancer-overview)　⇒　L4
    - [Azure Application Gatewy](https://learn.microsoft.com/ja-jp/azure/application-gateway/overview)　⇒　L7

- AWS
    - [Network Load Balancer](https://docs.aws.amazon.com/ja_jp/elasticloadbalancing/latest/network/introduction.html)　⇒　L4
    - [Application Load Balancer](https://docs.aws.amazon.com/ja_jp/elasticloadbalancing/latest/application/introduction.html)　⇒　L7

に対応します。

# ロードバランサーを体験する

## NginxによるL4ロードバランサーの実装

https://facsiaginsa.com/nginx/configure-nginx-as-layer-4-load-balancer

```yaml:compose.yaml
services:
  lb:
    container_name: lb
    hostname: lb
    image: nginx:latest
    ports:
      - '80:80'
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf
  web1:
    container_name: web1
    hostname: web1
    image: nginx:latest
    volumes:
      - ./html/web1.html:/usr/share/nginx/html/index.html
  web2:
    container_name: web2
    hostname: web2
    image: nginx:latest
    volumes:
      - ./html/web2.html:/usr/share/nginx/html/index.html
  web3:
    container_name: web3
    hostname: web3
    image: nginx:latest
    volumes:
      - ./html/web3.html:/usr/share/nginx/html/index.html
```

```nginx.conf

user  nginx;
worker_processes  auto;

error_log  /var/log/nginx/error.log notice;
pid        /var/run/nginx.pid;


events {
    worker_connections  1024;
}

stream {
    upstream backend {
        # コンテナサービス名を指定
        server web1:80;
        server web2:80;
        server web3:80;
    }
    server {
        # リッスンポート
        listen   80;
        proxy_pass backend;
        # allow {許可IP};
        # deny all;
    }
}

```
```html/web1.html
web1
```
```html/web2.html
web2
```
```html/web3.html
web3
```
コンテナ構築後、以下を実行する。
```sh
for i in `seq 1 20` ; do
 curl http://localhost/
done
```

振り分けられていることが分かる。

```sh
web3
web1
web2
web3
web1
web2
web3
web1
web2
web3
web1
web2
web3
web1
web2
web2
web3
web1
web2
web3
```

## NginxによるL7ロードバランサーの実装

https://facsiaginsa.com/nginx/configure-nginx-as-layer-7-load-balancer

`compose.yaml`は上記と同様。`nginx.conf`を以下に書き換える。

```nginx.conf
user  nginx;
worker_processes  auto;

error_log  /var/log/nginx/error.log notice;
pid        /var/run/nginx.pid;


events {
    worker_connections  1024;
}

http {
    upstream backend {
        server web1:80;
        server web2:80;
        server web3:80;
    }

    server {
        listen       80;
        location / {
            proxy_pass http://backend;
        }
    }
}
```

コンテナ実行後、アクセス確認を行う。

```sh
for i in `seq 1 20` ; do
 curl http://localhost/
done
```

メッセージが均等に表示されたらOK。



# 参考

https://zenn.dev/axpensive/articles/d80e4d102eefb4