테라폼 실전 강좌 #4 ALB와 Auto Scaling: launch template, 대상 그룹, 스케일링 정책

4 분 소요

지난 편의 인스턴스는 한 대뿐이고, 죽으면 끝이고, 트래픽이 몰려도 늘어나지 않습니다. 이번 편에서 이 세 문제를 한 번에 풉니다. ALB가 입구를 맡고, Auto Scaling 그룹(ASG)이 인스턴스의 수와 생사를 관리하는 구조입니다. 테라폼 관점에서 이번 편의 배울 거리는 리소스 다섯 종류(launch template, ALB, 대상 그룹, 리스너, ASG)가 참조로 엮이는 모습, 그리고 이미 있는 리소스를 코드에서 지우는 경험입니다.

launch template: 인스턴스 정의를 재사용 가능하게 #

ASG는 “이 정의대로 인스턴스를 띄워라"라는 틀이 필요합니다. 지난 편에 aws_instance에 적었던 내용을 launch template으로 옮깁니다.

compute.tf
# compute.tf 수정
resource "aws_launch_template" "web" {
  name_prefix   = "myapp-${var.env}-web-"
  image_id      = data.aws_ami.al2023.id
  instance_type = "t3.micro"

  vpc_security_group_ids = [aws_security_group.web.id]

  user_data = base64encode(<<-EOF
    #!/bin/bash
    dnf install -y nginx
    echo "myapp ${var.env} - $(hostname)" > /usr/share/nginx/html/index.html
    systemctl enable --now nginx
  EOF
  )

  lifecycle {
    create_before_destroy = true
  }
}

내용은 거의 그대로지만 다른 점이 둘 있습니다. user_data를 base64encode()로 감싸야 하고(launch template의 API 요구 사항), name_prefix와 create_before_destroy 짝을 여기도 걸었습니다. template이 바뀌면 새 버전이 만들어지고 ASG가 새 인스턴스부터 그것을 쓰는 구조라서, 지난 편에서 “user_data 변경은 교체"라던 성질이 여기서는 롤링 교체의 재료가 됩니다.

ALB, 대상 그룹, 리스너 #

로드밸런서는 리소스 세 개의 조합입니다. ALB 본체(입구), 대상 그룹(뒤에 선 서버 묶음과 헬스 체크), 리스너(입구와 묶음을 잇는 규칙)입니다.

lb.tf
# lb.tf (새 파일)
resource "aws_lb" "main" {
  name               = "myapp-${var.env}"
  load_balancer_type = "application"
  security_groups    = [aws_security_group.alb.id]
  subnets            = [for s in aws_subnet.public : s.id]
}

resource "aws_lb_target_group" "web" {
  name_prefix = "web-"
  port        = 80
  protocol    = "HTTP"
  vpc_id      = aws_vpc.main.id

  health_check {
    path                = "/"
    healthy_threshold   = 2
    unhealthy_threshold = 3
  }

  lifecycle {
    create_before_destroy = true
  }
}

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

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

ALB는 퍼블릭 서브넷에, 지난 편에 만들어 둔 ALB SG를 달고 놓입니다. 리스너는 일단 HTTP 80만 엽니다. HTTPS는 인증서가 필요한데 그것은 도메인과 함께 #10에서 다루기로 했으므로, 지금의 80 리스너는 그때 리다이렉트로 바뀔 예정입니다.

Auto Scaling 그룹과 스케일링 정책 #

이제 셋을 묶습니다.

compute.tf
resource "aws_autoscaling_group" "web" {
  name_prefix         = "myapp-${var.env}-web-"
  min_size            = 2
  max_size            = 4
  desired_capacity    = 2
  vpc_zone_identifier = [for s in aws_subnet.private : s.id]
  target_group_arns   = [aws_lb_target_group.web.arn]
  health_check_type   = "ELB"

  launch_template {
    id      = aws_launch_template.web.id
    version = "$Latest"
  }

  lifecycle {
    ignore_changes = [desired_capacity]
  }
}

resource "aws_autoscaling_policy" "cpu" {
  name                   = "cpu-target-tracking"
  autoscaling_group_name = aws_autoscaling_group.web.name
  policy_type            = "TargetTrackingScaling"

  target_tracking_configuration {
    predefined_metric_specification {
      predefined_metric_type = "ASGAverageCPUUtilization"
    }
    target_value = 60
  }
}

설계 포인트를 짚습니다.

  • min 2, 두 AZ의 프라이빗 서브넷: 한 대가 죽거나 한 AZ에 문제가 생겨도 서비스가 남는 최소 구성입니다.
  • health_check_type = “ELB”: EC2 상태 검사만이 아니라 대상 그룹의 헬스 체크 실패도 교체 사유로 삼습니다. nginx가 죽은 인스턴스를 ASG가 알아서 갈아 끼웁니다.
  • ignore_changes = [desired_capacity]: 기초 #7에서 예고한 바로 그 사례입니다. 스케일링 정책이 조정한 대수를 다음 apply가 2로 되돌리지 않게 합니다.
  • 타깃 추적 정책: 평균 CPU 60%를 유지하도록 알아서 늘리고 줄입니다. 단계별 알람을 직접 짜는 것보다 선언적이라 테라폼과 결이 맞습니다.

단일 인스턴스 지우기: 코드 삭제가 곧 리소스 삭제 #

지난 편의 aws_instance.web은 역할이 끝났습니다. IaC에서 리소스 제거는 명령이 아니라 코드 삭제입니다. compute.tf에서 aws_instance 블록을 지우고 plan을 보면, “1 to destroy"로 그 인스턴스만 정확히 제거 대상으로 잡힙니다. 새 리소스를 만들며 옛것을 지우는 이 한 번의 plan에 이번 편의 변경 전체가 요약되어 있으니, 시리즈에서 처음으로 plan을 위에서 아래까지 꼼꼼히 읽어 볼 만한 순간입니다.

apply가 끝나면 terraform output에 추가해 둔 ALB 주소로 접속합니다.

outputs.tf
output "alb_dns_name" {
  value = aws_lb.main.dns_name
}

브라우저에서 myapp dev - ip-10-0-11-x... 응답이 나오고, 새로 고침을 반복하면 호스트 이름이 번갈아 나옵니다. 두 대가 번갈아 응답하는, 이 시리즈의 첫 동작 확인입니다. ALB와 인스턴스가 켜져 있으니 오늘 마친다면 destroy를 잊지 않습니다.

정리 #

이번 글에서 다룬 내용입니다.

  • 인스턴스 정의를 launch template으로 옮겼습니다. user_data는 base64 인코딩이 필요하고, template 변경은 새 버전이 되어 롤링 교체의 재료가 됩니다
  • ALB는 본체, 대상 그룹, 리스너 세 리소스의 조합입니다. HTTPS는 #10에서 도메인과 함께 붙입니다
  • ASG는 min 2로 두 AZ에 걸치고, health_check_type ELB로 애플리케이션 수준의 자가 치유를 갖췄습니다
  • desired_capacity에 ignore_changes를 걸어 스케일링 정책과 테라폼이 싸우지 않게 했습니다
  • 리소스 제거는 코드 삭제입니다. 단일 인스턴스를 지우는 plan으로 IaC의 제거 흐름을 확인했습니다

다음 글(#5 RDS와 S3)에서는 데이터 계층을 만듭니다. 프라이빗 서브넷의 PostgreSQL RDS와 정적 자산용 S3 버킷, 그리고 데이터 리소스에 거는 안전장치입니다.

X