🦩

ECS+Fargate の Public IP を固定 IP にしたい

2021/09/24に公開

やりたいこと

Amazon ECS+Fargate を構築し、Public IP アドレスを割り当てています
Internet Gateway へのルートを持つ Public Subnet にこれを配置してます

遊びでやってるので、Public にしてます
(Private だと AWS 料金が増えるからです)

この Public IP アドレスがデプロイ時に ECS タスクを作り変えるたびに変わってしまうので、これを固定させたいと考えています

調べると 2 通りのやり方がありそう

  • Network Load Balancer(NLB)を使う
    1. NLB に Elastic IP を割り当てる
      • ちなみに Fargate に直接 Elastic IP を割り当てることは現状できない(ECS の起動タイプが EC2 ならいける)
    2. Fargate と同じ Subnet 内に NLB を配置する
  • NAT Gateway を使う
    1. Fargate を Private Subnet に配置する
    2. Public Subnet に NAT Gateway を作成
    3. NAT Gateway に Elastic IP を割り当てる
    4. Public Subnet に Internet Gateway を作成
    5. Fargate は NAT Gateway を介して、インターネットに接続する

今回は NLB を使うやり方にします

理由は料金的な話で

  • NAT Gateway の料金が高い、処理データ量(GB)単位で課金される
  • ECS タスクを起動する際に Docker イメージを pull するが、Private Subnet に Fargate を配置すると、イメージの pull を NAT 経由で行ってしまうので、そのサイズ分課金されるらしい(確認してないけど)
    • NAT を経由しない別ルーティングを用意する必要が出てくる

NLB を作成

VPC と Public Subnet は Fargate と同じやつを使う

AWS コンソールで作成

  1. EC2 -> Elastic IPから Elastic IP アドレスを作成

  2. EC2 -> ターゲットグループからターゲットグループを作成

    Basic configuration

    設定項目 設定値 備考
    Choose a target type IP addresses Fargate なので IP(インスタンスだとたぶん駄目)
    Target group name ターゲットグループの名前
    Protocol TCP NLB は L4(TCP や UDP)のプロトコルをサポートしている
    Port 80
    VPC Fargate と同じやつ

    Health checks

    設定項目 設定値 備考
    Health check protocol TCP

    Register targets

    設定なし

  3. EC2 -> ロードバランサーから Network Load Balancer を作成

    Basic configuration

    設定項目 設定値 備考
    Load balancer name ロードバランサーの名前
    Scheme Internet-facing Public Subnet は Internet-facing(インターネット向け)、Private Subnet は Internal(内部向け)
    IP address type IPv4

    Network mapping

    設定項目 設定値 備考
    VPC Fargate と同じやつ
    Subnet Fargate と同じやつ
    IPv4 settings 作成した Elastic IP

    Listeners and routing

    設定項目 設定値 備考
    Protocol TCP
    Port 80 今回は HTTP プロトコルで待ち受ける
    Default action(type) Forward to 指定したターゲットグループにリクエストを転送する
    Default action 作成したターゲットグループ

Terraformで作成

Elastic IP

resource "aws_eip" "this" {
  tags = {
    Name = "sample"
  }
}

ターゲットグループ

resource "aws_lb_target_group" "this" {
  target_type = "ip"
  name        = "sample"
  protocol    = "TCP"
  port        = 80
  vpc_id      = aws_vpc.this.id

  health_check {
    protocol = "TCP"
  }
}

NLB

resource "aws_lb" "this" {
  load_balancer_type = "network"
  name               = "sample"
  internal           = false
  ip_address_type    = "ipv4"

  subnet_mapping {
    subnet_id     = aws_subnet.pub_a.id
    allocation_id = aws_eip.this.id
  }
}

NLB リスナー

resource "aws_lb_listener" "this" {
  load_balancer_arn = aws_lb.this.id
  protocol          = "TCP"
  port              = "80"
  default_action {
    type             = "forward"
    target_group_arn = aws_lb_target_group.this.arn
  }
}

ECS のサービスを作り直す

AWS コンソールで作成

ロードバランサーは後から追加できないので、サービスを作り直す

設定項目 設定値 備考
ロードバランサーの種類 Network Load Balancer
ロードバランサー名 作成したロードバランサー
ターゲットグループ名 作成したターゲットグループ

ECS サービスのセキュリティグループでロードバランサーからのアクセスを許可するため、インバウンドルールに VPC の IPv4 CIDR ブロック(例:10.0.0.0/16)を追加
Port range は Fargate 上で動かしているアプリケーションに合わせて適宜設定する

EC2 -> ターゲットグループ -> Targetsタブで Health status が healthy になればオッケー
(固定の)Elastic IP アドレスで疎通できるようになる

Terraformで作成

セキュリティグループ

インバウンドルールに VPC の IPv4 CIDR ブロックを追加
ポートは適宜設定

resource "aws_security_group" "this" {
  name   = "sample"
  vpc_id = aws_vpc.this.id
}

resource "aws_security_group_rule" "nlb" {
  type              = "ingress"
  description       = "NLB"
  from_port         = 8080
  to_port           = 8080
  protocol          = "tcp"
  cidr_blocks       = [aws_vpc.this.cidr_block]
  security_group_id = aws_security_group.this.id
}

ECS サービス

resource "aws_ecs_service" "this" {
  ...
  load_balancer {
    target_group_arn = aws_lb_target_group.this.arn
    container_name   = "sample"
    container_port   = 8080
  }
}

Discussion