📌

NginxをECS-fargateで簡単にデプロイ ~ terraformで実装 ~

2024/10/08に公開

前提

Terraformのコードで実装しています。Terraformの解説はしておりません。
NW・ロードバランサー・セキュリティグループについてある程度理解されている方が望ましいです。
簡単なNginxのコンテナをECSで起動する方法を解説します。
なるべく最小限の設定だけで、デプロイできるように意識しています。

AWS構成

ざっくり構成です。非常にシンプルです。
ロードバランサーもNginxサービスもパブリックサブネットに配置しました。(サービスをプライベートサブネットに配置する方法もあり)

                                    Internet
                                        |
                                        |
                                [Internet Gateway]
                                        |
                                        |
                        +---------------+---------------+
                        |               |               |
                    [Public Subnet 1a]  |        [Public Subnet 1c]
                    10.0.1.0/24         |         10.0.2.0/24
                        |               |               |
                        |   [Application Load Balancer] |    
                        |               |               |
                        |               |               |
                        +---------[ECS Cluster]---------+
                                        |
                                        |
                              [ECS Service (Nginx)]
                                        |
                                        |
                              [ECS Task Definition]
                            
                            All within VPC (10.0.0.0/16)

NWを作成

  • VPCを用意
  • パブリックサブネットを2つのAZに配置
  • インターネットゲートウェイをルートテーブルに設置、パブリックサブネットに設定 = インターネットとの通信を許可
// VPC
resource "aws_vpc" "ecs_navi_vpc" {
  cidr_block = "10.0.0.0/16"
  enable_dns_support = true
  enable_dns_hostnames = true

  tags = {
    Name = "${var.service_name}-vpc"
  }
}

// Subnet
resource "aws_subnet" "ecs_navi_subnet_public_1a" {
  vpc_id = aws_vpc.ecs_navi_vpc.id
  cidr_block = "10.0.1.0/24"
  availability_zone = "ap-northeast-1a"
  map_public_ip_on_launch = true

  tags = {
    Name = "${var.service_name}-subnet-public-1a"
  }
}

resource "aws_subnet" "ecs_navi_subnet_public_1c" {
  vpc_id = aws_vpc.ecs_navi_vpc.id
  cidr_block = "10.0.2.0/24"
  availability_zone = "ap-northeast-1c"
  map_public_ip_on_launch = true
  
  tags = {
    Name = "${var.service_name}-subnet-public-1c"
  }
}

// Internet Gateway
resource "aws_internet_gateway" "ecs_navi_igw" {
  vpc_id = aws_vpc.ecs_navi_vpc.id 
}

// Route Table
resource "aws_route_table" "ecs_navi_route_table" {
  vpc_id = aws_vpc.ecs_navi_vpc.id

  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.ecs_navi_igw.id
  }
}

// Route Table Association
resource "aws_route_table_association" "ecs_navi_subnet_public_1a_association" {
  subnet_id = aws_subnet.ecs_navi_subnet_public_1a.id
  route_table_id = aws_route_table.ecs_navi_route_table.id
}

resource "aws_route_table_association" "ecs_navi_subnet_public_1c_association" {
  subnet_id = aws_subnet.ecs_navi_subnet_public_1c.id
  route_table_id = aws_route_table.ecs_navi_route_table.id
}

セキュリティグループを作成

  • ロードバランサーに付与するセキュリティグループ(ecs_navi_sg)とタスクに付与するセキュリティグループ(ecs_navi_backend_sg)を作成
// セキュリティグループ
resource "aws_security_group" "ecs_navi_sg" {
  vpc_id = aws_vpc.ecs_navi_vpc.id

  ingress {
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  ingress {
    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 = "${var.service_name}-sg"
  }
}

// セキュリティグループ
resource "aws_security_group" "ecs_navi_backend_sg" {
  vpc_id = aws_vpc.ecs_navi_vpc.id

  ingress {
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    security_groups = [aws_security_group.ecs_navi_sg.id]
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

ロードバランサーを作成

  • ロードバランサー、リスナールール、ターゲットグループを作成
  • ターゲットグループの target typeはIPで指定します

https://docs.aws.amazon.com/ja_jp/elasticloadbalancing/latest/application/load-balancer-target-groups.html#target-type

// ロードバランサー
resource "aws_lb" "ecs_navi_lb" {
  name               = "${var.service_name}-lb"
  internal           = false
  load_balancer_type = "application"
  security_groups    = [aws_security_group.ecs_navi_sg.id]
  subnets            = [aws_subnet.ecs_navi_subnet_public_1a.id, aws_subnet.ecs_navi_subnet_public_1c.id]

  enable_deletion_protection = false
}

resource "aws_lb_target_group" "nginx_tg" {
  name     = "${var.service_name}-nginx-tg"
  port     = 80
  protocol = "HTTP"
  vpc_id   = aws_vpc.ecs_navi_vpc.id
  target_type = "ip" // IPで設定

  health_check {
    path                = "/"
    interval            = 30
    timeout             = 5
    healthy_threshold   = 5
    unhealthy_threshold = 2
    matcher             = "200"
  }
}

resource "aws_lb_listener" "http" {
  load_balancer_arn = aws_lb.ecs_navi_lb.arn
  port              = 80
  protocol          = "HTTP"

  default_action {
    type             = "forward"
    target_group_arn = aws_lb_target_group.nginx_tg.arn
  }
}

// https のリスナー
resource "aws_lb_listener" "https" {
  load_balancer_arn = aws_lb.ecs_navi_lb.arn
  port              = 443
  protocol          = "HTTPS"
  ssl_policy        = "ELBSecurityPolicy-2016-08"
  certificate_arn   = aws_acm_certificate.ecs_navi_cert.arn

  default_action {
    type             = "forward"
    target_group_arn = aws_lb_target_group.nginx_tg.arn
  }
}

ECSを作成

ECSのリソースを作成していきます。
ECS Serviceでは今まで作成してきたNWとロードバランサー・ターゲットグループなどを反映していきます。
そうすることで、コンテナとして起動するタスク(Nginxコンテナ)へのインターネットからの処理フローを制御します。

// IAM
resource "aws_iam_role" "ecs_navi_ecs_task_execution_role" {
  name = "${var.service_name}-ecs-task-execution-role"

  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Action = "sts:AssumeRole"
        Effect = "Allow"
        Principal = {
          Service = "ecs-tasks.amazonaws.com"
        }
      }
    ]
  })

  tags = {
    Name = "${var.service_name}-ecs-task-execution-role"
  }
}


// ECS Cluster
resource "aws_ecs_cluster" "ecs_navi_cluster" {
  name = "${var.service_name}-cluster"
}

// ECS Service
resource "aws_ecs_service" "nginx_service" {
  name            = "${var.service_name}-nginx-service"
  cluster         = aws_ecs_cluster.ecs_navi_cluster.id
  task_definition = aws_ecs_task_definition.ecs_navi_nginx_task_definition.arn
  desired_count   = 1
  launch_type     = "FARGATE"

  network_configuration {
    subnets         = [aws_subnet.ecs_navi_subnet_public_1a.id, aws_subnet.ecs_navi_subnet_public_1c.id]
    security_groups = [aws_security_group.ecs_navi_backend_sg.id]
    assign_public_ip = true
  }

  load_balancer {
    target_group_arn = aws_lb_target_group.nginx_tg.arn
    container_name   = "nginx"
    container_port   = 80
  }
}

// nginxのタスク定義
resource "aws_ecs_task_definition" "ecs_navi_nginx_task_definition" {
  family = "${var.service_name}-nginx-task"
  requires_compatibilities = ["FARGATE"]
  network_mode = "awsvpc"
  cpu                      = 256
  memory                   = 512

// nginxのデフォルトイメージをECRから取得
  container_definitions = jsonencode([
    {
      name = "nginx"
      image     = "nginx:latest"
      portMappings = [
        {
          containerPort = 80
          hostPort = 80
        }
      ]
    }
  ])

  execution_role_arn = aws_iam_role.ecs_navi_ecs_task_execution_role.arn
}

確認

コンソールからDNS名を確認してみます。

Amazon Elastic Container Service >> クラスター >> ecs-navi-cluster >> サービス

サービスの「設定とネットワーク」にネットワーク情報が記載されています。
ここからDNS名を取得してブラウザでリクエストしてみてください。

Nginxのデフォルト画面が表示されればOKです。

終わりに

新しいサービスを利用するときには、簡単な状態をまずは作ってそこから色々といじっていくと理解が早まると思います。
不備などあれば、教えて下さい。

Discussion