3. 인스턴스 생성 (로드밸런서)

AWS 로드밸런서 생성

1. AWS 리전 지정

provider "aws" {
    region = "us-east-2"
}

2. 변수 지정

variable "server_port" {
    description = "HTTP 서비스"
    type = number
    default = 8080
}

variable "alb_http_port" {
    type = number
    default = 80
}

3. 보안 그룹 생성

## EC2 보안 그룹 생성
resource "aws_security_group" "instance" {
    name = "terraform-example-instance"
    # 인바운드 tcp 8080 트래픽 허용
    ingress {
        from_port = var.server_port
        to_port = var.server_port
        protocol = "tcp"
        cidr_blocks = [ "0.0.0.0/0" ]        
    }
}

## ALB 보안 그룹 생성
## 80 포트에서 들어오는 요청을 허용하여 HTTP를 통해 로드밸런서에 접속
## 바깥으로 나가는 요청은 포트와 상관없이 허용하여 로드밸런서가 Health check를 수행하도록 한다.
resource "aws_security_group" "alb" {
    name = "terraform-example-alb"
    # 인바운드 HTTP 트래픽 허용
    ingress {
        from_port = var.alb_http_port
        to_port = var.alv_http_port
        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" ]
    }
}

4. Autoscaling 설정

AWS 계정에서 VPC의 서브넷 목록 불러오기

## AWS의 VPC 불러오기
data "aws_vpc" "default" {
    default = true
}

## VPC 내 서브넷 조회
data "aws_subnet_ids" "default" {
    vpc_id = data.aws_vpc.default.ide
}

시작 구성 EC2 인스턴스 정의

## ASG로 생성할 인스턴스의 설정 정의
resource "aws_lanuch_configuration" "example" {
    image_id = "ami-0a695f0d95cefc163"    # 이미지 지정
    instance_type = "t2.micro"            # EC2 인스턴스 타입 지정
    security_groups = [aws_security_group.instance.id] # EC2 보안그룹 지정
    user_data = <<-EOF
        # httpd 8080포트로 서비스 시작 스크립트
        #!/bin/bash
        echo "Hello, World" > index.html
        nohup busybox httpd -f -p ${var.server_port} &
        EOF
    # ASG에서 시작 구성을 사용할 때 필요한 구문
    # 테라폼은 AWS의 오브젝트의 값을 교체할 때, 값을 변경하지 못하는 오브젝트에 대해서는
    # 기존 오브젝트를 삭제한 뒤 새로운 값으로 오브젝트르 생성한다.
    # 교체되는 오브젝트가 먼저 생성된 뒤 이전 오브젝트를 삭제하는 정책
    lifecycle {
        create_before_destroy = true
    }
}

Autoscaling Group 생성

## ASG 생성
resource "aws_autoscaling_group" "example" {
    lanuch_configuration = aws_lanuch_configuration.example.name
    vpc_zone_identifier = data.aws_subnet_ids.default.ids

    # EC2 인스턴스 목록을 ALB 대상 그룹으로 연결
    # ASG가 대상 그룹의 상태 확인을 하여 인스턴스가 정상인지 여부를 판별하고
    # 대상 그룹이 상태 불량으로 보고되면 인스턴스를 자동으로 교체하도록 지시한다.
    target_groups_arns = [aws_lb_target_group.asg.arn]
    health_check_type = "ELB"
    min_size = 2
    max_size = 10
    
    tag { 
        key = "Name"
        value = "Autoscaling-example"
        propagate_at_launch = true
    }
}

5. 로드밸런서 배포

## ALB 리스너 정의
resource "aws_lb_listener" "http" {
    load_balancer_arn = aws_lb.example.arn
    port = 80
    protocol = "HTTP"

    # 기본 값으로 단순한 404 페이지 오류 반환
    # 리스너 규칙과 일치하지 않는 요청에 대해 기본 응답으로 404 페이지를 보내도록 구성
    default_action {
        type = "fixed-response"
        fixed_response {
            content_type = "text/plain"
            message_body = "404: page not found"
            status_code = 404
        }        
    }
}

ALB 리스너 규칙 생성

리스너 규칙을 생성하여 위에서 생성한 부분을 연결

resource "aws_lb_listener_rule" "asg" {
    listener_arn = aws_lb_listener.http.arn
    priority = 100
    condition {
        path_pattern {
            values = ["*"]
        }
    }
    action {
        type = "forward"
        target_group_arn = aws_lb_target_group.asg.arn
    }
}

모든 경로와 일치하는 요청을 ASG가 포함된 대상 그룹으로 보내는 리스너 규칙

ALB DNS 이름 출력

output "alb_dns_name" {
    value = aws_lb.example.dns_name
    description = "로드밸런서 도메인 네임"
}

terraform apply 시 생성한 로드밸런서의 DNS 이름이 출력된다.

6. 확인

terraform apply

PS C:\terraform_code> terraform plan
data.aws_vpc.default: Reading...
data.aws_vpc.default: Read complete after 2s [id=vpc-070ddb03df90391be]
data.aws_subnet_ids.default: Reading...
data.aws_subnet_ids.default: Read complete after 0s [id=vpc-070ddb03df90391be]

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following 
symbols:
  + create

Terraform will perform the following actions:

  # aws_autoscaling_group.example will be created
  + resource "aws_autoscaling_group" "example" {
      + arn                       = (known after apply)
      + availability_zones        = (known after apply)
      + default_cooldown          = (known after apply)
      + desired_capacity          = (known after apply)
      + force_delete              = false
      + force_delete_warm_pool    = false
      + health_check_grace_period = 300
      + health_check_type         = "ELB"
      + id                        = (known after apply)
      + launch_configuration      = (known after apply)
      + max_size                  = 10
      + metrics_granularity       = "1Minute"
      + min_size                  = 2
      + name                      = (known after apply)
      + name_prefix               = (known after apply)
      + protect_from_scale_in     = false
      + service_linked_role_arn   = (known after apply)
      + target_group_arns         = (known after apply)
      + vpc_zone_identifier       = [
          + "subnet-0753b66315e0af6e3",
          + "subnet-077820c8912f1f5c2",
          + "subnet-0df722400e6b54e2a",
        ]
      + wait_for_capacity_timeout = "10m"

      + tag {
          + key                 = "Name"
          + propagate_at_launch = true
          + value               = "Autoscaling-example"
        }
    }

  # aws_launch_configuration.example will be created
  + resource "aws_launch_configuration" "example" {
      + arn                         = (known after apply)
      + associate_public_ip_address = (known after apply)
      + ebs_optimized               = (known after apply)
      + enable_monitoring           = true
      + id                          = (known after apply)
      + image_id                    = "ami-0a695f0d95cefc163"
      + instance_type               = "t2.micro"
      + key_name                    = (known after apply)
      + name                        = (known after apply)
      + name_prefix                 = (known after apply)
      + security_groups             = (known after apply)
      + user_data                   = "a1323b251462faf59ee4c09a927f5250b15db09c"
    }

  # aws_lb.example will be created
  + resource "aws_lb" "example" {
      + arn                                         = (known after apply)
      + arn_suffix                                  = (known after apply)
      + desync_mitigation_mode                      = "defensive"
      + dns_name                                    = (known after apply)
      + drop_invalid_header_fields                  = false
      + enable_deletion_protection                  = false
      + enable_http2                                = true
      + enable_tls_version_and_cipher_suite_headers = false
      + enable_waf_fail_open                        = false
      + enable_xff_client_port                      = false
      + id                                          = (known after apply)
      + idle_timeout                                = 60
      + internal                                    = (known after apply)
      + ip_address_type                             = (known after apply)
      + load_balancer_type                          = "application"
      + name                                        = "terraform-asg-example"
      + preserve_host_header                        = false
      + security_groups                             = (known after apply)
      + subnets                                     = [
          + "subnet-0753b66315e0af6e3",
          + "subnet-077820c8912f1f5c2",
          + "subnet-0df722400e6b54e2a",
        ]
      + tags_all                                    = (known after apply)
      + vpc_id                                      = (known after apply)
      + xff_header_processing_mode                  = "append"
      + zone_id                                     = (known after apply)
    }

  # aws_lb_listener.http will be created
  + resource "aws_lb_listener" "http" {
      + arn               = (known after apply)
      + id                = (known after apply)
      + load_balancer_arn = (known after apply)
      + port              = 80
      + protocol          = "HTTP"
      + ssl_policy        = (known after apply)
      + tags_all          = (known after apply)

      + default_action {
          + order = (known after apply)
          + type  = "fixed-response"

          + fixed_response {
              + content_type = "text/plain"
              + message_body = "404: page not found"
              + status_code  = "404"
            }
        }
    }

  # aws_lb_listener_rule.asg will be created
  + resource "aws_lb_listener_rule" "asg" {
      + arn          = (known after apply)
      + id           = (known after apply)
      + listener_arn = (known after apply)
      + priority     = 100
      + tags_all     = (known after apply)

      + action {
          + order            = (known after apply)
          + target_group_arn = (known after apply)
          + type             = "forward"
        }

      + condition {
          + path_pattern {
              + values = [
                  + "*",
                ]
            }
        }
    }

  # aws_lb_target_group.asg will be created
  + resource "aws_lb_target_group" "asg" {
      + arn                                = (known after apply)
      + arn_suffix                         = (known after apply)
      + connection_termination             = false
      + deregistration_delay               = "300"
      + id                                 = (known after apply)
      + ip_address_type                    = (known after apply)
      + lambda_multi_value_headers_enabled = false
      + load_balancing_algorithm_type      = (known after apply)
      + load_balancing_cross_zone_enabled  = (known after apply)
      + name                               = "terraform-asg-example"
      + port                               = 8080
      + preserve_client_ip                 = (known after apply)
      + protocol                           = "HTTP"
      + protocol_version                   = (known after apply)
      + proxy_protocol_v2                  = false
      + slow_start                         = 0
      + tags_all                           = (known after apply)
      + target_type                        = "instance"
      + vpc_id                             = "vpc-070ddb03df90391be"

      + health_check {
          + enabled             = true
          + healthy_threshold   = 2
          + interval            = 15
          + matcher             = "200"
          + path                = "/"
          + port                = "traffic-port"
          + protocol            = "HTTP"
          + timeout             = 3
          + unhealthy_threshold = 2
        }
    }

  # aws_security_group.alb will be created
  + resource "aws_security_group" "alb" {
      + arn                    = (known after apply)
      + description            = "Managed by Terraform"
      + egress                 = [
          + {
              + cidr_blocks      = [
                  + "0.0.0.0/0",
                ]
              + description      = ""
              + from_port        = 0
              + ipv6_cidr_blocks = []
              + prefix_list_ids  = []
              + protocol         = "-1"
              + security_groups  = []
              + self             = false
              + to_port          = 0
            },
        ]
      + id                     = (known after apply)
      + ingress                = [
          + {
              + cidr_blocks      = [
                  + "0.0.0.0/0",
                ]
              + description      = ""
              + from_port        = 80
              + ipv6_cidr_blocks = []
              + prefix_list_ids  = []
              + protocol         = "tcp"
              + security_groups  = []
              + self             = false
              + to_port          = 80
            },
        ]
      + name                   = "terraform-example-alb"
      + name_prefix            = (known after apply)
      + owner_id               = (known after apply)
      + revoke_rules_on_delete = false
      + tags_all               = (known after apply)
      + vpc_id                 = (known after apply)
    }

  # aws_security_group.instance will be created
  + resource "aws_security_group" "instance" {
      + arn                    = (known after apply)
      + description            = "Managed by Terraform"
      + egress                 = (known after apply)
      + id                     = (known after apply)
      + ingress                = [
          + {
              + cidr_blocks      = [
                  + "0.0.0.0/0",
                ]
              + description      = ""
              + from_port        = 8080
              + ipv6_cidr_blocks = []
              + prefix_list_ids  = []
              + protocol         = "tcp"
              + security_groups  = []
              + self             = false
              + to_port          = 8080
            },
        ]
      + name                   = "terraform-example-instance"
      + name_prefix            = (known after apply)
      + owner_id               = (known after apply)
      + revoke_rules_on_delete = false
      + tags_all               = (known after apply)
      + vpc_id                 = (known after apply)
    }

Plan: 8 to add, 0 to change, 0 to destroy.

Changes to Outputs:
  + alb_dns_name = (known after apply)

│ Warning: Deprecated Resource

│   with data.aws_subnet_ids.default,
│   on main.tf line 80, in data "aws_subnet_ids" "default":
│   80: data "aws_subnet_ids" "default"{

│ The aws_subnet_ids data source has been deprecated and will be removed in a future version. Use the aws_subnets data source     
│ instead: https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/subnets

│ (and one more similar warning elsewhere)


───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── 

Note: You didn't use the -out option to save this plan, so Terraform can't guarantee to take exactly these actions if you run     
"terraform apply" now.

작성한 코드 점검

terraform apply

PS C:\terraform_code> terraform apply
[...]
Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes
[...]
Apply complete! Resources: 8 added, 0 changed, 0 destroyed.

Outputs:

alb_dns_name = "terraform-asg-example-190757077.us-east-2.elb.amazonaws.com"

웹 서버 접속

이 상태에서 인스턴스 중 하나를 종료시키면 ALB는 인스턴스가 다운되었음을 자동으로 감지하고 라우팅을 중지시킨다.

인스턴스가 종료된 후 얼마 지나지 않아 ASG는 2개 미만의 인스턴스가 실행 중임을 감지하고 자동으로 새 인스턴스를 시작하여 인스턴스를 복구한다.

7. 리소스 삭제

terraform destroy

테스트 완료 후 생성한 리소스 삭제

PS C:\terraform_code> terraform destroy
[...]
Do you really want to destroy all resources?
  Terraform will destroy all your managed infrastructure, as shown above.
  There is no undo. Only 'yes' will be accepted to confirm.

  Enter a value: yes
[...]
Destroy complete! Resources: 8 destroyed.

Last updated