• Home
  • About
    • lahuman photo

      lahuman

      열심히 사는 아저씨

    • Learn More
    • Facebook
    • LinkedIn
    • Github
  • Posts
    • All Posts
    • All Tags
  • Projects

#2 ASG

23 Oct 2022

Reading time ~8 minutes

ASG(Auto Scaling groups) 배포

💡 아래 설정(옵션들)과 동작 이해를 위해서 ‘AWS 관리 콘솔’에서 수동으로 직접 ELB, EC2 ASG(오토 스케일링) 등을 배포 후에 아래 테라폼 실습을 따라 할 것!

목표 : 오토스케일링 그룹으로 EC2 배포

graph LR; A(((User))); A --> B(EC2 Instance); A --> C(EC2 Instance); A --> D(EC2 Instance); A --> E(EC2 more...); subgraph AWS; B & C & D & E; end;

수명 주기(lifecycle) 설정 : 리소스를 교체하는 순서를 반대로 하여 교체 리소스를 먼저 생성하고 기존 리소스 삭제 동작 설정 가능

  • 오토스케일링 시작 구성(launch configuration) : aws_instance 리소스와 거의 동일한 매개 변수를 사용
  • 일부 파라미터는 지원하지 않을 수 있으며, 몇몇 파라미터는 이름이 다르기도 하다
    • ami → image_id
    • vpc_security_group_ids → security_groups
  • 아래 ASG 예시 : EC2 2대~10대 사이 중 초기 시작은 기본값 2대로 실행되고, 태그 지정됨
# 샘플 코드 : 실행을 하지는 않음
resource "aws_autoscaling_group" "example" {
    launch_configuration = aws_launch_configuration.example.name

    min_size = 2
    max_size = 10

    tag {
    key                 = "Name"
    value               = "terraform-asg-example"
    propagate_at_launch = true
    }
}
  • ASG는 시작 구성 정보를 참고하여 인스턴스를 생성하는데 한 가지 문제가 발생 → 시작 구성은 변경할 수 없으므로 시작 구성 매개 변수를 변경 시 테라폼이 이를 대체하려고 합니다.
  • 일반적으로 리소스를 교체할 때 테라폼은 이전 리소스를 먼저 삭제한 다음 대체 리소스를 생성합니다. 그러나 ASG에 이전 리소스에 대한 참조가 있기 때문에 테라폼은 해당 리소스를 삭제 할 수 없습니다.
  • 이 문제 해결을 위해 수명 주기 설정(lifecycle)를 할 수 있습니다.
  • 모든 테라폼 리소스는 리소스 생성, 업데이트 및 삭제 방법을 구성하는 몇 가지 수명 주기(lifecycle) 설정을 지원합니다.
  • 특히 create_before_destroy 를 true 로 설정하면 테라폼은 리소스를 교체하는 순서를 반대로 하여 교체 리소스를 먼저 생성하고(이전 리소스가 가리키고 있던 참조를 업데이트하여 교체한 리소스를 가리킴) 기존 리소스를 삭제합니다.
  • 아래와 같인 lifecycle 블록을 aws_launch_configuration 에 추가합니다
# 샘플 코드 : 실행을 하지는 않음
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**
    }
}

ASG 배포 : asg.tf

Terraform Registry

  • 코드 파일 작성 : CMD창에서 바로 복사&붙여넣기로 main.tf 파일 생성 시 ** 주의 ⇒ 그냥 직접 **IDE에 붙여넣을때는 **** 제거하세요
cat <<EOT > asg.tf
data "aws_ami" "my_amazonlinux2" {
  most_recent = true
  filter {
    name   = "owner-alias"
    values = ["amazon"]
  }

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

  owners = ["amazon"]
}

resource "aws_launch_configuration" "mylauchconfig" {
  name_prefix     = "t101-lauchconfig-"
  image_id        = data.aws_ami.my_amazonlinux2.id
  instance_type   = "t2.micro"
  security_groups = [aws_security_group.mysg.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" "myasg" {
  name                 = "myasg"
  launch_configuration = aws_launch_configuration.mylauchconfig.name
  vpc_zone_identifier  = [aws_subnet.mysubnet1.id, aws_subnet.mysubnet2.id]
  min_size = 2
  max_size = 10

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

배포

# [터미널1] EC2 생성 모니터링
# macOS
export AWS_PAGER=""
while true; do aws ec2 describe-instances --query "Reservations[*].Instances[*].{PublicIPAdd:PublicIpAddress,InstanceName:Tags[?Key=='Name']|[0].Value,Status:State.Name}" --filters Name=instance-state-name,Values=running --output text ; echo "------------------------------" ; sleep 1; done

# 배포
terraform plan && terraform apply -auto-approve
terraform state list
aws_autoscaling_group.myasg
aws_launch_configuration.mylauchconfig
...

# curl 접속 확인
IP1=<EC2 1번 Public IP>
IP2=<EC2 2번 Public IP>
echo "$IP1 - WebSrv" ; curl -s $IP1 ; echo ; echo "$IP2 - WebSrv" ; curl -s $IP2 ; echo

AWS 관리 콘솔 : ASG 시작 구성, AS 그룹 확인

# ASG 시작 구성, AS 그룹 정보 확인
aws autoscaling describe-auto-scaling-groups --auto-scaling-group-names | jq
aws autoscaling describe-auto-scaling-groups --auto-scaling-group-names --output table
aws autoscaling describe-auto-scaling-groups --auto-scaling-group-names myasg| jq
aws autoscaling describe-scaling-activities --auto-scaling-group-name myasg | jq

로드 밸런서 배포

목표 : ASG 에 ALB 연동 배포

graph LR; A(((User))); F{Elastic Load Balancer}; A --> F; subgraph AWS; F --> B(EC2 Instance); F --> C(EC2 Instance); F --> D(EC2 Instance); F --> E(EC2 more...); end;

ALB 구성 : Listener, Listener rule, Target groups

graph LR; A(((Request))); A --> F; subgraph Listeners; F(Port 80, HTTP); G(Port 443, HTTPS); end; subgraph Listeners Rules; F --> B(foo.example.com); F --> C(bar.example.com); G --> D(/foo/*); G --> E(/bar/*); end; subgraph Target Group1; H(foo); I(foo); end; B & D ---> H & I; subgraph Target Group2; J(bar); K(bar); end; C & E ---> J & K;

배포 : alb.tf

Terraform Registry

  • ALB 생성 ```bash cat «EOT > alb.tf resource “aws_lb” “myalb” { name = “t101-alb” load_balancer_type = “application” subnets = [aws_subnet.mysubnet1.id, aws_subnet.mysubnet2.id] security_groups = [aws_security_group.mysg.id]

tags = { Name = “t101-alb” } }

output “myalb_dns” { value = aws_lb.myalb.dns_name description = “The DNS Address of the ALB” }

EOT

```bash
# 배포 : 2분 10초 정도 소요
terraform plan && terraform apply -auto-approve
Outputs:
myalb_dns = "t101-alb-1753585046.ap-northeast-2.elb.amazonaws.com"

terraform state list
aws_lb.myalb
...

# ALB 정보 확인
aws elbv2 describe-load-balancers --output table
aws elbv2 describe-load-balancers | jq

# [터미널2] 출력된 ALB DNS주소로 cul 접속 확인
cd my-vpc-ec2-asg
terraform output -raw myalb_dns
t101-alb-1753585046.ap-northeast-2.elb.amazonaws.com

ALBDNS=$(terraform output -raw myalb_dns)
while true; do curl --connect-timeout 1  http://$ALBDNS/ ; echo; echo "------------------------------"; date; sleep 1; done
  • ALB의 리스너 생성 : http 80 인입, 리스너 규칙과 일치하지 않을 경우 404 페이지 리턴

Terraform Registry

cat <<EOT > alb.tf
resource "aws_lb" "myalb" {
  name               = "t101-alb"
  load_balancer_type = "application"
  subnets            = [aws_subnet.mysubnet1.id, aws_subnet.mysubnet2.id]
  security_groups = [aws_security_group.mysg.id]

  tags = {
    Name = "t101-alb"
  }
}

resource "aws_lb_listener" "myhttp" {
  load_balancer_arn = aws_lb.myalb.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
    }
  }
}

output "myalb_dns" {
  value       = aws_lb.myalb.dns_name
  description = "The DNS Address of the ALB"
}
EOT
  • 배포 후 접속
# 배포
terraform plan && terraform apply -auto-approve

# ALB DNS 주소 curl 접속 확인
  • 다음으로 aws_lb_target_group 리소스를 사용하여 ASG의 대상 그룹을 생성

Terraform Registry

cat <<EOT > alb.tf
resource "aws_lb" "myalb" {
  name               = "t101-alb"
  load_balancer_type = "application"
  subnets            = [aws_subnet.mysubnet1.id, aws_subnet.mysubnet2.id]
  security_groups = [aws_security_group.mysg.id]

  tags = {
    Name = "t101-alb"
  }
}

resource "aws_lb_listener" "myhttp" {
  load_balancer_arn = aws_lb.myalb.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
    }
  }
}

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

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

output "myalb_dns" {
  value       = aws_lb.myalb.dns_name
  description = "The DNS Address of the ALB"
}
EOT
# 배포
terraform plan && terraform apply -auto-approve

# ALB 대상 그룹 확인 : 아직은 타켓 대상(EC2) 없음

ALB + ASG 연동 : asg.tf

  • 대상그룹은 어느 EC2에 요청을 보내는지 아나요? → ASG + ALB 통합 이점 활용(aws_autoscaling_group 리소스에 target_group_arns 인수를 설정)하여 새 대상 그룹을 지정
  • 이때 헬스체크도 EC2가 아닌 ELB로 변경 → 이 경우 EC2 불량 시 자동으로 인스턴스가 교체되고 대상그룹에 등록됨
  • 코드 파일 작성 : CMD창에서 바로 복사&붙여넣기로 main.tf 파일 생성 시 ** 주의 ⇒ 그냥 직접 **IDE에 붙여넣을때는 **** 제거하세요
cat <<EOT > asg.tf
data "aws_ami" "my_amazonlinux2" {
  most_recent = true
  filter {
    name   = "owner-alias"
    values = ["amazon"]
  }

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

  owners = ["amazon"]
}

resource "aws_launch_configuration" "mylauchconfig" {
  name_prefix     = "t101-lauchconfig-"
  image_id        = data.aws_ami.my_amazonlinux2.id
  instance_type   = "t2.micro"
  security_groups = [aws_security_group.mysg.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" "myasg" {
  name                 = "myasg"
  launch_configuration = aws_launch_configuration.mylauchconfig.name
  vpc_zone_identifier  = [aws_subnet.mysubnet1.id, aws_subnet.mysubnet2.id]
  min_size = 2
  max_size = 10
  health_check_type = "ELB"
  target_group_arns = [aws_lb_target_group.myalbtg.arn]

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

Terraform Registry

  • 마지막으로 aws_lb_listener_rule 리소스를 사용해 리스너 규칙을 생성하여 연결
cat <<EOT > alb.tf
resource "aws_lb" "myalb" {
  name               = "t101-alb"
  load_balancer_type = "application"
  subnets            = [aws_subnet.mysubnet1.id, aws_subnet.mysubnet2.id]
  security_groups = [aws_security_group.mysg.id]

  tags = {
    Name = "t101-alb"
  }
}

resource "aws_lb_listener" "myhttp" {
  load_balancer_arn = aws_lb.myalb.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
    }
  }
}

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

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

resource "aws_lb_listener_rule" "myalbrule" {
  listener_arn = aws_lb_listener.myhttp.arn
  priority     = 100

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

  action {
    type             = "forward"
    target_group_arn = aws_lb_target_group.myalbtg.arn
  }
}

output "myalb_dns" {
  value       = aws_lb.myalb.dns_name
  description = "The DNS Address of the ALB"
}
EOT

배포 완료 후 curl 접속 확인

# 배포
terraform plan && terraform apply -auto-approve

# ALB DNS주소로 curl 접속 확인 
ALBDNS=$(terraform output -raw myalb_dns)
for i in {1..100}; do curl -s http://$ALBDNS/ ; done | sort | uniq -c | sort -nr

# EC2 최소 2대 => 3대로 증가 수정
sed -i -e 's/min_size = 2/min_size = 3/g' asg.tf

# 배포
terraform plan && terraform apply -auto-approve

# ALB DNS주소로 curl 접속 확인 
for i in {1..100}; do curl -s http://$ALBDNS/ ; done | sort | uniq -c | sort -nr

# 강제로 EC2 1대 삭제 후 curl 접속 확인 : 종료 확인 후 치료를 위한 인지까지 3분 정도 시간 소요 - (자가치료) 알아서 1대 추가

삭제: **terraform destroy -auto-approve**

  • destroy 는 ‘실행 취소(undo)’를 할 수 없으니, 실제 운영 환경에서는 주의해서 실행해야 함


terraformstudy Share Tweet +1