Terraformを使用して、AWSのApplication Load Balancer(ALB)を構築した

# 前書き
`Terraform`を使用して`Application Load Balancer (ALB)`を構築しました。今回の構築に取り組む中で、負荷分散の基本的な概念についても理解が不足していた為、その学びも踏まえて、`LD`(ロードバランサー)の概要と`ALB`の構築手順を以下に記載します。

# ロードバランサーの概要(Load Balancer)
- 複数のサーバーにトラフィックを均等に分配し、システム全体のパフォーマンスを向上させる為の仕組み

| **機能** | **説明** |
| :-: | - |
| **トラフィック(通信)の分散** | クライアントからのリクエストを複数のサーバー(バックエンド)に均等に割り振り、負荷集中を防止する |
| **可用性の向上** | サーバーの1つがダウンした場合、他の正常なサーバーに自動でトラフィックを切り替え、サービスを継続する |
| **スケーラビリティ <br> (負荷に応じて拡張・縮小できる柔軟性)** | 負荷増加時にサーバーを追加し、ロードバランサーが自動的に新しいサーバーにトラフィックを分散する |
| **障害検知 <br> (ヘルスチェック)** | 定期的に各サーバーを監視し、正常なサーバーだけにリクエストを割り振る |
| **SSL/TLSオフロード** | `SSL/TLS`の暗号化・復号化をロードバランサーで処理し、バックエンドの負荷を軽減する |
| **種類別ロードバランサー** | `L4`: ネットワーク層（`IP`やポート）で分散 <br> `L7`: アプリケーション層（URLやHTTPヘッダ）で分散する |

**用語説明**
- オフロード : 重い作業を別の場所に分散して効率化

# AWSのロードバランサーの種類
## Application Load Balancer (ALB)

- **L7(アプリケーション層)** で動作
- `HTTP/HTTPS`プロトコルに対応し、`URL`や`HTTP`ヘッダーに基づくルーティングが可能
- `Web`アプリケーションや`API`ゲートウェイに適している

## Network Load Balancer (NLB)

- **L4(ネットワーク層)** で動作
- `TCP/UDP`トラフィックを高速に処理し、低レイテンシが必要なアプリケーションに最適
- ゲームサーバーや金融アプリケーションに適している

## Gateway Load Balancer (GWLB)

- ネットワークアーキテクチャの一部として、サードパーティのセキュリティアプライアンスを統合する
- **ファイアウォールや侵入防止システム(IPS)** を構築する場合に使用

## Classic Load Balancer (CLB)

- AWS初期から提供されているロードバランサーで、アプリケーション層とネットワーク層の両方をサポート
- 現在は、`ALB`や`NLB`への移行が推奨されている

## 特徴と機能
- 下記にAWSのロードバランサー機能・説明を記載する

| 機能 | 説明 |
| :-: | - |
| 自動スケーリング対応 | インスタンスを自動で追加・削除し、負荷に応じてスケールアウト・スケールインを実現する |
| ヘルスチェック | 定期的にバックエンドのサーバーの状態を確認し、正常なサーバーだけにトラフィック(通信)を転送する |
| `SSL/TLS`オフロード | 暗号化・復号化をロードバランサーで処理し、バックエンドの負荷を軽減する |
| セキュリティグループ対応 | セキュリティ設定を制御し、特定の`IP`からのアクセスのみを許可するなどのポリシーを設定する |

### 参考資料
- [[AWS公式] Application Load Balancer とは?](https://docs.aws.amazon.com/ja_jp/elasticloadbalancing/latest/application/introduction.html)
- [[AWS公式] Network Load Balancer とは?](https://docs.aws.amazon.com/ja_jp/elasticloadbalancing/latest/network/introduction.html)
- [[AWS公式] Gateway Load Balancer とは？](https://docs.aws.amazon.com/ja_jp/elasticloadbalancing/latest/gateway/introduction.html)
- [[AWS公式] Classic Load Balancer の設定](https://docs.aws.amazon.com/ja_jp/elasticbeanstalk/latest/dg/environments-cfg-clb.html)


## ターゲットグループの概要
- Elastic Load Balancing(ELB)の一部で、ロードバランサーからのリクエストを受け取るサーバーやサービスのグループを管理している
- リクエストをどのように分散するかのルールを定義し、ロードバランサーの背後にある複数のターゲットに負荷を均等に分散する役割を果たす

## 特徴と機能
- 下記にAWSのターゲットグループの機能・説明を記載する

| 項目 | 説明 |
| :-: | :- |
| ターゲットの種類 | `EC2`、`ECS`、`Lambda`、`IP`アドレス等をターゲットに指定可能 |
| プロトコルとポート | 各ターゲットごとに プロトコル(`HTTP`, `HTTPS`, `TCP`, `UDP`等)とポートを設定可能 |
| ヘルスチェック | 各ターゲットの 正常性を監視 し、障害が発生したターゲットへのトラフィック(通信)を停止させる |
| ターゲットの登録方法 | `IP`アドレスでの直接登録、`EC2`の登録、または`Auto Scaling`で管理可能 |
| ターゲットグループの種類 | `Application Load Balancer(ALB)`、`Network Load Balancer(NLB)`、`Gateway Load Balancer(GLB)`などに対応したターゲットグループが存在する |
| スティッキネス（Stickiness） | ユーザーのリクエストを同じターゲットに維持する設定。セッションを一貫させる用途に使用する |
| 重み付きルーティング | 各ターゲットに **異なる重み(ウェイト)** を設定し、特定のターゲットに多くのリクエストを送ることが可能 |
| ターゲットの状態管理 | `initial`, `healthy`, `unhealthy`, `draining`等のステータスで各ターゲットの状態を管理 |


### 参考資料
- [[AWS公式] Application Load Balancer のターゲットグループ](https://docs.aws.amazon.com/ja_jp/elasticloadbalancing/latest/application/load-balancer-target-groups.html)
- [[AWS公式] Application Load Balancer ターゲットグループのヘルスチェック](https://docs.aws.amazon.com/ja_jp/elasticloadbalancing/latest/application/target-group-health-checks.html)
- [[AWS公式] Application Load Balancer のセキュリティポリシー](https://docs.aws.amazon.com/ja_jp/elasticloadbalancing/latest/application/describe-ssl-policies.html)


# Terraformの構築
## Application Load Balancer(ALB)

```alb.tf
resource "aws_lb" "test_alb" {
  name                             = "test-alb"
  internal                         = false
  idle_timeout                     = 60
  enable_deletion_protection       = true
  enable_cross_zone_load_balancing = true

  subnets = [ 
    # サブネット ID
  ]   

  security_groups = [
    # セキュリティーグループ ID
  ]

  tags = {
    Name = "test-alb"
  }
}
```
### aws_lb内で使用しているメソッドと説明
| メソッド | 説明 |
| :-: | :- |
| internal | 	ロードバランサーを 内部用(`VPC`内限定) にするか、パブリック(インターネットからアクセス可能)にするかを制御する。`false`の場合、外部からのアクセスが可能なパブリックロードバランサーになる |
| idle_timeout | クライアントとの無通信状態が続く最大秒数を設定する。指定時間(今回の場合は60秒)が経過すると接続が切断される |
| enable_deletion_protection | 誤削除を防止する為の削除保護を有効化する。有効にすると、手動でこの保護を解除するまでリソースを削除出来なくなる |
| enable_cross_zone_load_balancing | 複数の可用ゾーン(`AZ`)間でトラフィックを均等に分散 する様にする。これにより、負荷分散の効率が向上し、各ゾーンのトラフィックがバランスよく処理される |



:::note warn
警告
`HTTPS`で接続する場合、`SSL/TLS`証明書が必要になる為、DNSの構築が必要となる。その為、`certificate_arn`の部分を別途`Terraform`で構築する必要がある
:::

```alb_listener.tf
# 疎通テスト用のリスナールール
resource "aws_lb_listener" "test_http_alb_listener" {
  # HTTPでのアクセスを受け付ける
  port              = "80"
  protocol          = "HTTP"
  load_balancer_arn = aws_lb.test_alb.arn

  # "ok" という固定レスポンスを設定する
  default_action {
    type = "fixed-response"
    fixed_response {
      content_type = "text/plain"
      status_code  = "200"
      message_body = "ok"
    }
  }
}

# EC2やECSに接続する際のリスナールール
resource "aws_lb_listener" "test_https_alb_listener" {
  load_balancer_arn = aws_lb.test_alb.arn
  port              = "443"
  protocol          = "HTTPS"
  ssl_policy        = "ELBSecurityPolicy-TLS13-1-3-2021-06"
  default_action {
    type             = "forward"
    target_group_arn = aws_lb_target_group.test_tg.arn
    order            = 1
  }
  
  # 使用するSSL/TLS証明書
  certificate_arn = # aws_acm_certificateのarn
}
```
### aws_lb_listener内で使用しているメソッドと説明

#### 疎通テスト用のリスナールール
| メソッド | 説明 |
| :-: | :- |
| port | ロードバランサーが待ち受ける ポート番号を指定する。今回の場合は、`HTTP`トラフィック用の 80 番ポートを設定している |
| protocol | 通信プロトコルを指定する。今回の場合は、`HTTP`プロトコルを使用している |
| load_balancer_arn | 対象のロードバランサー(`ALB`)の`ARN`を指定する |
| default_action | デフォルトのアクションを定義する。指定した条件に一致するリクエストがない場合、このアクションが自動で実行される |
| fixed_response | クライアントに返す **固定レスポンス** を設定する |
| content_type | レスポンスのコンテンツタイプを指定する。今回の場合は、`text/plain`を指定する |
| status_code | レスポンスに含まれる **HTTP ステータスコード** を指定する |
| message_body | レスポンスボディとしてクライアントに`ok`という固定メッセージを返す |

#### EC2やECSに接続する際のリスナールール

| メソッド | 説明 |
| :-: | - |
| port | ロードバランサーが待ち受ける`443`番ポートを指定する。これは`HTTPS`トラフィックに使用される |
| protocol | `HTTPS`プロトコル を使用して暗号化された通信を行う |
| ssl_policy | `SSL/TLS`のポリシー を指定する。この場合、`ELBSecurityPolicy-TLS13-1-3-2021-06`ポリシーを適用して、`TLS 1.3`を強制する |
| default_action | デフォルトのアクションとして、リクエストを ターゲットグループに転送 (`forward`)する |
| target_group_arn | 転送先のターゲットグループ のARNを指定する |
| order | このアクションの実行順序を指定する。1を指定すると最優先で実行される |
| certificate_arn | `HTTPS`通信で使用する`SSL/TLS`証明書 の`ARN`を指定する。証明書のTerraformが構築されている場合は、`aws_acm_certificate`から取得した`ARN`を使用する |


## Target Group(TG)
- EC2やECSに通信を送る際のターゲットグループ

:::note warn
疎通テストを行うのみでターゲットグループの構築は不要
:::

```target_group.tf
resource "aws_lb_target_group" "test_tg" {
  name = "test-tg"

  vpc_id               = # VPC ID
  target_type          = "ip"
  port                 = 80
  protocol             = "HTTP"
  deregistration_delay = 300
  health_check {
    path                = "/test.txt"
    healthy_threshold   = 5
    unhealthy_threshold = 2
    timeout             = 5
    interval            = 30
    matcher             = 200
    port                = "traffic-port"
    protocol            = "HTTP"
  }
  
  depends_on = [aws_lb.test_alb]
  lifecycle {
    create_before_destroy = true
  }
}
```

### aws_lb_target_group内で使用しているメソッドと説明

| メソッド | 説明 |
| - | - |
| `name`  | ターゲットグループの名前を指定する |
| `vpc_id` | ターゲットグループが関連付けられるVPCのIDを指定する |
| `target_type` | ターゲットの種類を指定する (`instance`, `ip`, `lambda`) |
| `port`  | ターゲットグループがリッスンするポート番号を指定する |
| `protocol` | 通信プロトコルを`HTTP` または `HTTPS`のどちらかを指定する。他には、`TCP`、`UDP`、`TLS`、`GENEVE`がある |
| `deregistration_delay` | ターゲットの登録解除後に待機する秒数を指定する |
| `depends_on` | リソースの依存関係を設定する |
| `lifecycle` | リソースの作成・破棄時の挙動を制御する |
| `create_before_destroy` | リソースを新規作成してから既存リソースを削除する挙動を指定する |

### health_check内で使用しているメソッドと説明

| メソッド | 説明 |
| - | - |
| `path` | ヘルスチェックでリクエストを送信するパスを指定する |
| `healthy_threshold` | ターゲットを「ヘルシー」と判定するために必要な成功チェック数を指定 |
| `unhealthy_threshold` | ターゲットを「アンヘルシー」と判定するために必要な失敗チェック数を指定 |
| `timeout` | タイムアウトまでの秒数を指定する |
| `interval` | ヘルスチェックを実行する間隔（秒単位）を指定する |
| `matcher` | ヘルスチェックの成功を判定するステータスコードを指定する |
| `port` | ヘルスチェックで使用するポートを指定する |
| `protocol` | ヘルスチェックの通信プロトコルを指定する |

## Security Group(SG)

:::note warn
- 今回の構築では、`HTTPS`のインバウンドは不要
- セキュリティーグループに関しては、メソッドの説明を割愛する
- セキュリティーグループの基本的な仕組みは過去に記載しましたので、下記を参照して下さい
[セキュリティーグループのインバウンドで設定するソースとアウトバウンドで設定する送信先の役割について理解した事をまとめた](https://qiita.com/kengo-sk8/items/f453d10f3e7c53db01f9)
:::
```security_group.tf
resource "aws_security_group" "test_alb_sg" {
  name        = "test-alb"
  description = "test alb"
  vpc_id      = # VPC ID

  # インバウンド
  # HTTP
  ingress {
    description = "HTTP from VPC"
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
  # HTTPS
  ingress {
    description = "HTTPS from VPC"
    from_port   = 443
    to_port     = 443
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  # アウトバンド
  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }

  tags = {
    Name = "test-alb-sg"
  }
}
```


# Terrformの参考資料
- [[HashiCorp] aws_lb](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lb)
- [[HashiCorp] aws_lb_listener](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lb_listener)
- [[HashiCorp] aws_lb_target_group](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lb_listener)
- [[HashiCorp] aws_security_group](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lb_listener)
- [[Terraformで構築するAWS] ALB](https://y-ohgi.com/introduction-terraform/handson/alb/)

# まとめ
今回は、`ALB`の構築を行いました。ALBに関して改め理解出来たので、今回学んだ事を活し、引き続きインフラの構築を頑張りたいと思いました。