ASG (Auto Scaling Group)

클라우드의 장점 중의 하나로 서버를 Scale Out해 주는 기능이 있다.

대용량 트래픽을 처리하기 위해, 단일 서버에 대한 장애 대처를 위해, 시스템 고가용성을 위해,,, 등등 일반적으로 ASG (Auto Scaling Group) 을 사용하여 해결한다.

ASG는 EC2 인스턴스 클러스터 시작, 각 인스턴스 상태 모니터링, 실패한 인스턴스 교체, 로드에 따른 클러스터 사이즈 조정 등 많은 작업을 자동으로 처리할 수 있다.

Launch Configuration (시작 구성)

image

ASG는 Launch Configuration (시작 구성) 을 참고하여 인스턴스를 생성한다. Launch Configuration은 ASG에서 각 EC2 인스턴스를 어떻게 구성할 것인지 설정하는 시작 구성을 의미한다! aws_launch_configuration 리소스로 정의하며 aws_instance 리소스와 거의 동일한 매개 변수를 사용한다!

Launch Configuration은 변경할 수 없고 매개 변수를 변경하면 테라폼이 이를 대체하려고 한다.

테라폼에서는 aws_autoscaling_group 리소스를 사용하여 ASG 자체를 생성할 수 있다.

resource "aws_autoscaling_group" "example" {
  launch_configuration = aws_launch_configuration.example.name
  vpc_zone_identifier  = data.aws_subnets.default.ids

  min_size = 2
  max_size = 10

  tag {
    key                 = "Name"
    value               = "terraform-asg-example"
    propagate_at_launch = true
  }
}

vpc_zone_identifier 인수를 이용해 aws_subnet_ids 데이터 소스에서 Subnet ID를 가져와서 ASG가 이 Subnet을 사용하도록 지시할 수 있다

Lifecycle (생명 주기)

ASG는 최소 2개의 EC2 인스턴스로 시작하여 최대 10개의 인스턴스를 생성할 수 있다.

근데 여기서 문제는!

테라폼에서 일반적으로 리소스를 교체할 때는 이전 리소스를 먼저 삭제한 다음 대체 리소스를 생성한다. 근데 ASG에 이전 리소스에 대한 참조가 있기 때문에 해당 리소스를 삭제할 수 없게 된다.

이 때~! lifecycle 생명 주기 (리소스 생성, 업데이트 및 삭제 방법) 설정을 사용하여 해결할 수 있다 ㅎㅎ 생명 주기… 알아 두자!

aws_launch_configurationlifecycle 블록을 아래와 같이 추가해 주면 된다.

resource "aws_launch_configuration" "example" {
  image_id        = "ami-0e9bfdb247cc8de84"
  instance_type   = "t2.micro"
  security_groups = [aws_security_group.instance.id]

  user_data = <<-EOF
              #!/bin/bash
              echo "Hello, World" > index.html
              nohup busybox httpd -f -p ${var.server_port} &
              EOF

  # Required when using a launch configuration with an auto scaling group.
  lifecycle {
    create_before_destroy = true
  }
}

create_before_destroy는 유용하게 사용한다는 생명 주기 설정인데 테라폼은 리소스 교체 순서를 반대로 하여 교체 리소스 생성하고 기존 리소스를 삭제하는 설정이다.

이제 ASG를 진짜진짜!

아래와 같이 생성한다!

aws_ami를 아마존 리눅스로 설정하고

aws_launch_configuration를 설정하고 aws_autoscaling_group를 설정한다!

lifecycle은 꼬옥-!

data "aws_ami" "yooga_amazonlinux2" {
  most_recent = true
  filter {
    name   = "owner-alias"
    values = ["amazon"]
  }

  filter {
    name   = "name"
    values = ["amzn2-ami-hvm-*-x86_64-ebs"]
  }00

  owners = ["amazon"]
}

resource "aws_launch_configuration" "yoogalauchconfig" {
  name_prefix     = "t101-lauchconfig-"
  image_id        = data.aws_ami.yooga_amazonlinux2.id
  instance_type   = "t2.micro"
  security_groups = [aws_security_group.myasg.id]
  associate_public_ip_address = true

  user_data = <<-EOF
              #!/bin/bash
              wget https://busybox.net/downloads/binaries/1.31.0-defconfig-multiarch-musl/busybox-x86_64
              mv busybox-x86_64 busybox
              chmod +x busybox
              RZAZ=$(curl http://169.254.169.254/latest/meta-data/placement/availability-zone-id)
              IID=$(curl 169.254.169.254/latest/meta-data/instance-id)
              LIP=$(curl 169.254.169.254/latest/meta-data/local-ipv4)
              echo "<h1>RegionAz($RZAZ) : Instance ID($IID) : Private IP($LIP) : Web Server</h1>" > index.html
              nohup ./busybox httpd -f -p 80 &
              EOF

  # Required when using a launch configuration with an auto scaling group.
  lifecycle {
    create_before_destroy = true
  }
}

resource "aws_autoscaling_group" "yoogaasg" {
  name                 = "yoogaasg"
  launch_configuration = aws_launch_configuration.yoogalauchconfig.name
  vpc_zone_identifier  = [aws_subnet.yoogasubnet1.id, aws_subnet.yoogasubnet2.id]
  min_size = 2
  max_size = 10

  tag {
    key                 = "Name"
    value               = "terraform-asg"
    propagate_at_launch = true
  }
}

LB (Load Balancer)

ASG으로 인스턴스 서버 수를 Scale In/Out 하고 있다면?

앞단에 로드밸런서를 배포하여 서버 전체에 트래픽을 분산시키고 모든 사용자에게 하나의 DNS명을 제공해 줘야 한다. (하나의 로드밸런서 IP로!)

AWS LB의 종류는 아래와 같다.

  • ALB (Application Load Balancer) : 7 Layer에 해당되는 HTTP 및 HTTPS 트래픽 처리
  • NLB (Network Load Balancer) : 4 Layer에 해당되는 TCP, UDP, TLS 트래픽 처리
  • CLB (Classic Load Balancer) : ALB, NLB에서처럼 트래픽 처리는 동일하지만 기능은 훨씬 적다

aws_lb 리소스를 사용하여 로드 밸런서를 구성할 것인데 이 포스팅에서는 ALB를 구성할 것이다!

resource "aws_lb" "example" {
  name               = "terraform-ash-example"
  load_balancer_type = "application"
  subnets            = data.aws_subnet_ids.default.ids
}

load_balancer_typeapplication으로 설정하면 ALB로 구성할 수 있다.

subnets는 default로 구성해도 되지만 아래와 같이 내가 사전에 구성한 리소스로 설정해도 된다.

	subnets            = [aws_subnet.yoogasubnet1.id, aws_subnet.yoogasubnet2.id]
  security_groups = [aws_security_group.yoogasg.id]

aws_lb_listener 리소스로 기본 80포트를 수신하고 HTTP 프로토콜을 사용하고 리스너 규칙에 일치하지 않은 요청에 대한 default 응답은 404로 반환할 것이다.

resource "aws_lb_listener" "yoogahttp" {
  load_balancer_arn = aws_lb.yoogaalb.arn
  port              = 80
  protocol          = "HTTP"

  # By default, return a simple 404 page
  default_action {
    type = "fixed-response"

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

기본적으로 모든 AWS 리소스는 Inbound/Outbound 트래픽은 All Deny이므로 새로운 보안 그룹을 생성해줘야 수신이 가능하다!

resource "aws_security_group" "yooga-web-sg" {
  vpc_id      = aws_vpc.yooga-vpc.id
  name        = "T101 SG"
  description = "T101 Study SG"
}

resource "aws_security_group_rule" "yoogasginbound" {
  type              = "ingress"
  from_port         = 0
  to_port           = 80
  protocol          = "tcp"
  cidr_blocks       = ["0.0.0.0/0"]
  security_group_id = aws_security_group.yooga-web-sg.id
}

resource "aws_security_group_rule" "yoogasgoutbound" {
  type              = "egress"
  from_port         = 0
  to_port           = 0
  protocol          = "-1"
  cidr_blocks       = ["0.0.0.0/0"]
  security_group_id = aws_security_group.yooga-web-sg.id
}

Inbound로 들어 오는 HTTP 트래픽을 허용해 주고

Outbound로 나가는 모든 트래픽은 허용해 줬다!

그 다음 aws_lb_target_group 리소스를 사용하여 ASG의 대상 그룹을 생성해야 하는데!

resource "aws_lb_target_group" "yoogaalbtg" {
  name = "t101-alb-tg"
  port     = 80
  protocol = "HTTP"
  vpc_id   = aws_vpc.yoogavpc.id

  health_check {
    path                = "/"
    protocol            = "HTTP"
    matcher             = "200-299"
    interval            = 5
    timeout             = 3
    healthy_threshold   = 2
    unhealthy_threshold = 2
  }
}

matcher와 일치하는 응답을 반환하는 경우에만 health check가 통과되어 인스턴스를 정상으로 간주한다. (healthy)

output "yoogaalb_dns" {
  value       = aws_lb.yoogaalb.dns_name
  description = "The DNS Address of the ALB"
}

마지막으로 EC2 인스턴스의 Public IP 출력문을 ALB의 DNS 명 출력문으로 바꿔 주었다!

Categories:

Updated:

Leave a comment