AWS - Terraform (ASG , LB)

2023. 11. 21. 11:48·💻 개발 일지/AWS

웹 서버 클러스터 배포

# ASG 구성

=> EC2 인스턴스 클러스터 시작, 각 인스턴스 상태 모니터링, 실패한 인스턴스 교체, 부하에 따른 클러스터 크기 조정 등 많은 작업을 자동으로 처리

=> 가용성을 제공

 

#1 시작 구성(Launch Configuration) 정의

        ~~~~~~~

       ASG에서 EC2 인스턴스를 구성하는 방법을 지정

 

aws_launch_configuration 리소스를 사용 =>  https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/launch_configuration

 

- aws_instance 리소스와 거의 동일한 파라미터를 사용

- tags와 user_data_replace_on_change 파라미터를 지원하지 않음

   ~~~~   ~~~~~~~~~~~~~~~~~~~~~~~~~~~

   |      +-- ASG는 기본적으로 새 인스턴스를 시작하므로 필요하지 않음  

  +--- aws_autoscaling_group 리소스에서 처리

- 파라미터 중 다음 두 개는 다른 이름을 가짐

   -- ami → image_id

   -- vpc_security_group_ids → security_groups

### main.tf ###

provider "aws" {
  region = "ap-northeast-2"
}
<==================================================
# resource "aws_instance" "example" {
#   ami                    = "ami-086cae3329a3f7d75"
#   instance_type          = "t2.micro"
#   vpc_security_group_ids = [aws_security_group.webserversg.id]
#   user_data              = <<-EOF
#     #!/bin/bash
#     echo "Hello, World" > index.html
#     nohup busybox httpd -f -p ${var.service_port} &
#   EOF

#   user_data_replace_on_change = true

#   tags = {
#     Name = "terraform-example"
#   }
# }

resource "aws_launch_configuration" "webserverlg" {
  image_id        = "ami-086cae3329a3f7d75"
  instance_type   = "t2.micro"
  security_groups = [aws_security_group.webserversg.id]
  user_data       = <<-EOF
    #!/bin/bash
    echo "Hello, World" > index.html
    nohup busybox httpd -f -p ${var.service_port} &
  EOF
}
==================================================>

resource "aws_security_group" "webserversg" {
  name = "terraform-example-webserversg"

  ingress {
    from_port   = var.service_port
    to_port     = var.service_port
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

variable "service_port" {
  description = "Service Port for HTTP Request"
  type        = number
  default     = 8080
}

output "public_ip" {
  value       = aws_instance.example.public_ip
  description = "Public IP for WebServer"
}

output "service_url" {
  value       = "http://${aws_instance.example.public_ip}:${var.service_port}"
  description = "WebServer Service URL"
}

 

#2 ASG 정의

aws_autoscaling_group 리소스로 정의 ⇒ https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/autoscaling_group

### main.tf ###

provider "aws" {
  region = "ap-northeast-2"
}

# resource "aws_instance" "example" {
#   ami                    = "ami-086cae3329a3f7d75"
#   instance_type          = "t2.micro"
#   vpc_security_group_ids = [aws_security_group.webserversg.id]
#   user_data              = <<-EOF
#     #!/bin/bash
#     echo "Hello, World" > index.html
#     nohup busybox httpd -f -p ${var.service_port} &
#   EOF

#   user_data_replace_on_change = true

#   tags = {
#     Name = "terraform-example"
#   }
# }

resource "aws_launch_configuration" "webserverlg" {
  image_id        = "ami-086cae3329a3f7d75"
  instance_type   = "t2.micro"
  security_groups = [aws_security_group.webserversg.id]
  user_data       = <<-EOF
    #!/bin/bash
    echo "Hello, World" > index.html
    nohup busybox httpd -f -p ${var.service_port} &
  EOF
}

<=========================================================
resource "aws_autoscaling_group" "webserverasg" {
  launch_configuration = aws_launch_configuration.webserverlg.name
  min_size             = 2
  max_size             = 5
  tag {
    key                 = "Name"
    value               = "terraform-asg"
    propagate_at_launch = true
  }
}
=========================================================>

resource "aws_security_group" "webserversg" {
  name = "terraform-example-webserversg"

  ingress {
    from_port   = var.service_port
    to_port     = var.service_port
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

variable "service_port" {
  description = "Service Port for HTTP Request"
  type        = number
  default     = 8080
}

output "public_ip" {
  value       = aws_instance.example.public_ip
  description = "Public IP for WebServer"
}

output "service_url" {
  value       = "http://${aws_instance.example.public_ip}:${var.service_port}"
  description = "WebServer Service URL"
}

 

#3 수명 주기 설정

create_before_destroy를 true로 설정하면 테라폼은 리소스를 교체하는 순서를 반전하여 먼저 대체 리소스를 생성한 다음  (이전 리소스를 가리키는 모든 참조를 새로운 리소스를 가리키도록 업데이트한 후) 이전 리소스를 삭제함

### main.tf ###

provider "aws" {
  region = "ap-northeast-2"
}

# resource "aws_instance" "example" {
#   ami                    = "ami-086cae3329a3f7d75"
#   instance_type          = "t2.micro"
#   vpc_security_group_ids = [aws_security_group.webserversg.id]
#   user_data              = <<-EOF
#     #!/bin/bash
#     echo "Hello, World" > index.html
#     nohup busybox httpd -f -p ${var.service_port} &
#   EOF

#   user_data_replace_on_change = true

#   tags = {
#     Name = "terraform-example"
#   }
# }

resource "aws_launch_configuration" "webserverlg" {
  image_id        = "ami-086cae3329a3f7d75"
  instance_type   = "t2.micro"
  security_groups = [aws_security_group.webserversg.id]
  user_data       = <<-EOF
    #!/bin/bash
    echo "Hello, World" > index.html
    nohup busybox httpd -f -p ${var.service_port} &
  EOF
<========================================================================
  # 시작 구성을 재생성해야 하는 경우, 새로운 시작 구성을 먼저 생성한 후 
  예전 시작 구성을 삭제하도록 설정
  lifecycle {
    create_before_destroy = true
  }
========================================================================>
}

resource "aws_autoscaling_group" "webserverasg" {
  launch_configuration = aws_launch_configuration.webserverlg.name
  min_size             = 2
  max_size             = 5
  tag {
    key                 = "Name"
    value               = "terraform-asg"
    propagate_at_launch = true
  }
}

resource "aws_security_group" "webserversg" {
  name = "terraform-example-webserversg"

  ingress {
    from_port   = var.service_port
    to_port     = var.service_port
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

variable "service_port" {
  description = "Service Port for HTTP Request"
  type        = number
  default     = 8080
}

output "public_ip" {
  value       = aws_instance.example.public_ip
  description = "Public IP for WebServer"
}

output "service_url" {
  value       = "http://${aws_instance.example.public_ip}:${var.service_port}"
  description = "WebServer Service URL"
}

 

#4 데이터 소스를 이용해서 서브넷 정보를 조회

 

데이터 소스

- 테라폼을 실행할 때 마다 공급자에서 가져오는 읽기 전용 정보

- 공급자의 API를 호출해서 해당 데이터를 나머지 테라폼 코드에서 사용할 수 있도록 하는 방법

- 예) AWS 공급자의 경우, VPC 데이터, Subnet 데이터, AMI ID, IP 주소 범위, 현재 사용자의 ID 등

- data "<PROVIDER>_<TYPE>" "<NAME>" {

   [CONFIG ...]

   }

    -- PROVIDER 공급자 이름 (예: aws)

    -- TYPE 데이터 소스 유형 (예: vpc)

    -- NAME 데이터 소스를 참조할 때 사용할 식별자

    -- CONFIG 데이터 소스와 관련된 parametar

- 조회방법  data.<PROVIDER>_<TYPE>.<NAME>.<ATTRIBUTE>

 

### main.tf ###

provider "aws" {
  region = "ap-northeast-2"
}

# resource "aws_instance" "example" {
#   ami                    = "ami-086cae3329a3f7d75"
#   instance_type          = "t2.micro"
#   vpc_security_group_ids = [aws_security_group.webserversg.id]
#   user_data              = <<-EOF
#     #!/bin/bash
#     echo "Hello, World" > index.html
#     nohup busybox httpd -f -p ${var.service_port} &
#   EOF

#   user_data_replace_on_change = true

#   tags = {
#     Name = "terraform-example"
#   }
# }

resource "aws_launch_configuration" "webserverlg" {
  image_id        = "ami-086cae3329a3f7d75"
  instance_type   = "t2.micro"
  security_groups = [aws_security_group.webserversg.id]
  user_data       = <<-EOF
    #!/bin/bash
    echo "Hello, World" > index.html
    nohup busybox httpd -f -p ${var.service_port} &
  EOF
  lifecycle {
    create_before_destroy = true
  }
}

<========================================================
# https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/vpc
data "aws_vpc" "default" {
  default = true
}

# https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/subnets
data "aws_subnets" "default" {
  filter {
    name   = "vpc-id"
    values = [data.aws_vpc.default.id]
  }
}
========================================================>

resource "aws_autoscaling_group" "webserverasg" {
  launch_configuration = aws_launch_configuration.webserverlg.name
<========================================================
  vpc_zone_identifier  = data.aws_subnets.default.ids
========================================================>
  min_size             = 2
  max_size             = 5
  tag {
    key                 = "Name"
    value               = "terraform-asg"
    propagate_at_launch = true
  }
}

resource "aws_security_group" "webserversg" {
  name = "terraform-example-webserversg"

  ingress {
    from_port   = var.service_port
    to_port     = var.service_port
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

variable "service_port" {
  description = "Service Port for HTTP Request"
  type        = number
  default     = 8080
}

<========================================================
# aws_instance 리소스를 삭제했으므로 관련한 출력을 주석 처리
# output "public_ip" {
#   value       = aws_instance.example.public_ip
#   description = "Public IP for WebServer"
# }

# output "service_url" {
#   value       = "http://${aws_instance.example.public_ip}:${var.service_port}"
#   description = "WebServer Service URL"
# }
========================================================>
data "aws_vpc" "default" {
  default = true
}
default vpc를 가져옴
filter {
    name   = "vpc-id"
    values = [data.aws_vpc.default.id]
  }
default vpc 이름을 지닌 vpc의 서브넷을 추출
vpc_zone_identifier  = data.aws_subnets.default.ids default 값의 vpc의 서브넷 아이디를 추출

 

 

***error

디폴트 서브넷 중 일부는 t2.micro 인스턴스 타입을 지원하지 않는 가용영역에 생성되어 있으므로 인스턴스 생성 시 아래와 같은 오류가 발생할 수 있음

aws_autoscaling_group.webserverasg: Creating...
╷
│ Error: waiting for Auto Scaling Group (terraform-20231121005600354100000002) capacity satisfied: scaling activity (60b62fbd-8263-1441-9a0f-8165a3723c81): Failed: Authentication Failure. Launching EC2 instance failed.
│ scaling activity (66b62fbd-8260-c3f6-484d-9ea65ebf73b0): Failed: Authentication Failure. Launching EC2 instance failed.
│
│   with aws_autoscaling_group.webserverasg,
│   on main.tf line 50, in resource "aws_autoscaling_group" "webserverasg":
│   50: resource "aws_autoscaling_group" "webserverasg" {

 

해결방안 : 가용 영역 지우기 or filter 사용

 

- t2.micro 인스턴스 타입을 지원하지 않는 가용영역에 생성된 서브넷을 삭제하고 테스트를 진행

C:\aws> terraform apply
data.aws_vpc.default: Reading...
data.aws_vpc.default: Read complete after 1s [id=vpc-06c8a58be6f0a60f4]
data.aws_subnets.default: Reading...
data.aws_subnets.default: Read complete after 0s [id=ap-northeast-2]

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

...

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

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

aws_security_group.webserversg: Creating...
aws_security_group.webserversg: Creation complete after 1s [id=sg-0a292fea0dd4310dc]
aws_launch_configuration.webserverlg: Creating...
aws_launch_configuration.webserverlg: Creation complete after 1s [id=terraform-20231121012509275900000001]
aws_autoscaling_group.webserverasg: Creating...
aws_autoscaling_group.webserverasg: Still creating... [10s elapsed]
aws_autoscaling_group.webserverasg: Still creating... [20s elapsed]
aws_autoscaling_group.webserverasg: Still creating... [30s elapsed]
aws_autoscaling_group.webserverasg: Still creating... [40s elapsed]
aws_autoscaling_group.webserverasg: Creation complete after 44s [id=terraform-20231121012509767100000002]

Apply complete! Resources: 3 added, 0 changed, 0 destroyed.

 

 

 


propagate_at_launch : 인스턴스 이름 생성

 

# 로드밸런서 배포

로드밸런서를 배포해서 서버 전체에 트래픽을 분산하고 모든 사용자들에게 로드밸런서의 주소를 통해서 접근할 수 있도록 제한

 

#1 ALB(Aws Load Balancer) 생성 = aws_lb

  https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lb

 

resource "aws_lb" "webserverlb" {
  name               = "terraform-lb"
  load_balancer_type = "application"
  subnets            = data.aws_subnets.default.ids
}

 

#2 로드밸런스에 대한 리스너를 정의 = aws_lb_listener

  https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lb_listener

resource "aws_lb_listener" "http" {
  load_balancer_arn = aws_lb.webserverlb.arn
  port              = 80
  protocol          = "HTTP"
  # 리스너 규칙과 일치하지 않는 요청에 대해 기본 응답으로 404 페이지를 반환
  default_action {
    type = "fixed-response"          
    fixed_response {
      status_code  = 404
      content_type = "text/plain"
      message_body = "404 Not Found"
    }
  }
}

 

#3 보안 그룹 추가

들어오는 포드 80 허용 나가는 모든 포트 허용

resource "aws_security_group" "lbsg" {
  name = "terraform-lbsg"
  ingress {
    from_port   = 80
    to_port     = 80
    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 로드밸런스에 보안그룹을 적용

resource "aws_lb" "webserverlb" {
  name               = "terraform-lb"
  load_balancer_type = "application"
  subnets            = data.aws_subnets.default.ids
  security_groups    = [aws_security_group.lbsg.id]			# 추가
}

 

#5 로드밸런스의 대상그룹을 정의 = aws_lb_target-group

  https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lb_target_group 

resource "aws_lb_target_group" "webservertg" {
  name     = "terraform-tg"
  port     = var.service_port
  protocol = "HTTP"
  vpc_id   = data.aws_vpc.default.id
  health_check {
    path                = "/"
    protocol            = "HTTP"
    matcher             = "200"
    interval            = 15
    timeout             = 3
    healthy_threshold   = 2
    unhealthy_threshold = 2
  }
}

 

 

#6 ASG에 대상 그룹 지정

인스턴스가 추가될 경우(새로 생성되는 경우) ALB 의 대상그룹에 포함되어야함

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

<=================================================================
  target_group_arns = [aws_lb_target_group.webservertg.arn]	# 추가
  health_check_type = "ELB"						# 추가
=================================================================>

  min_size = 2
  max_size = 5
  tag {
    key                 = "Name"
    value               = "terraform-asg"
    propagate_at_launch = true
  }
}

 

#7 리스너 규칙을 추가 

    로드밸런서로 들어와 ASG에 전달해야하므로

http 80으로 들어오면 모든 경로를 target그룹으로 포워드(전송)

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.webservertg.arn
  }  
}

 

#8 로드밸런스의 주소(DNS 이름)를 출력

output "alb_dns_name" {
  value       = aws_lb.webserverlb.dns_name
  description = "Domain name of the ELB"
}

 

 

### main.tf ###

provider "aws" {
  region = "ap-northeast-2"
}

# resource "aws_instance" "example" {
#   ami                    = "ami-086cae3329a3f7d75"
#   instance_type          = "t2.micro"
#   vpc_security_group_ids = [aws_security_group.webserversg.id]
#   user_data              = <<-EOF
#     #!/bin/bash
#     echo "Hello, World" > index.html
#     nohup busybox httpd -f -p ${var.service_port} &
#   EOF

#   user_data_replace_on_change = true

#   tags = {
#     Name = "terraform-example"
#   }
# }

resource "aws_launch_configuration" "webserverlg" {
  image_id        = "ami-086cae3329a3f7d75"
  instance_type   = "t2.micro"
  security_groups = [aws_security_group.webserversg.id]
  user_data       = <<-EOF
    #!/bin/bash
    echo "Hello, World" > index.html
    nohup busybox httpd -f -p ${var.service_port} &
  EOF
  lifecycle {
    create_before_destroy = true
  }
}


data "aws_vpc" "default" {
  default = true
}

data "aws_subnets" "default" {
  filter {
    name   = "vpc-id"
    values = [data.aws_vpc.default.id]
  }
}

#1 ALB(Aws Load Balancer) 생성 = aws_lb
resource "aws_lb" "webserverlb" {
  name               = "terraform-lb"
  load_balancer_type = "application"
  subnets            = data.aws_subnets.default.ids
  security_groups    = [aws_security_group.lbsg.id]			#4 로드밸런스에 보안그룹을 적용
}
#5 로드밸런스의 대상그룹을 정의
resource "aws_lb_target_group" "webservertg" {
  name     = "terraform-tg"
  port     = var.service_port
  protocol = "HTTP"
  vpc_id   = data.aws_vpc.default.id
  health_check {
    path                = "/"
    protocol            = "HTTP"
    matcher             = "200"
    interval            = 15
    timeout             = 3
    healthy_threshold   = 2
    unhealthy_threshold = 2
  }
}
#2 로드밸런스에 대한 리스너를 정의 
resource "aws_lb_listener" "http" {
  load_balancer_arn = aws_lb.webserverlb.arn
  port              = 80
  protocol          = "HTTP"
  # 리스너 규칙과 일치하지 않는 요청에 대해 기본 응답으로 404 페이지를 반환
  default_action {
    type = "fixed-response"          
    fixed_response {
      status_code  = 404
      content_type = "text/plain"
      message_body = "404 Not Found"
    }
  }
}
#3 보안 그룹 추가
resource "aws_security_group" "lbsg" {
  name = "terraform-lbsg"
  ingress {
    from_port   = 80
    to_port     = 80
    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"]
  }
}

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

  #6 ASG에 대상 그룹 추가
  target_group_arns = [aws_lb_target_group.webservertg.arn]	# 추가
  health_check_type = "ELB"						# 추가

  min_size             = 2
  max_size             = 5
  tag {
    key                 = "Name"
    value               = "terraform-asg"
    propagate_at_launch = true
  }
}
#7 리스너 규칙을 추가
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.webservertg.arn
  }  
}


resource "aws_security_group" "webserversg" {
  name = "terraform-example-webserversg"

  ingress {
    from_port   = var.service_port
    to_port     = var.service_port
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

variable "service_port" {
  description = "Service Port for HTTP Request"
  type        = number
  default     = 8080
}

# aws_instance 리소스를 삭제했으므로 관련한 출력을 주석 처리
# output "public_ip" {
#   value       = aws_instance.example.public_ip
#   description = "Public IP for WebServer"
# }

# output "service_url" {
#   value       = "http://${aws_instance.example.public_ip}:${var.service_port}"
#   description = "WebServer Service URL"
# }

#8 로드밸런스의 주소(DNS 이름)를 출력
output "alb_dns_name" {
  value       = aws_lb.webserverlb.dns_name
  description = "Domain name of the ELB"
}
C:\aws> terraform apply
data.aws_vpc.default: Reading...
aws_security_group.webserversg: Refreshing state... [id=sg-0a292fea0dd4310dc]
aws_launch_configuration.webserverlg: Refreshing state... [id=terraform-20231121012509275900000001]
data.aws_vpc.default: Read complete after 0s [id=vpc-06c8a58be6f0a60f4]
data.aws_subnets.default: Reading...
data.aws_subnets.default: Read complete after 0s [id=ap-northeast-2]
aws_autoscaling_group.webserverasg: Refreshing state... [id=terraform-20231121012509767100000002]

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

...

aws_lb.webserverlb: Creation complete after 2m51s [id=arn:aws:elasticloadbalancing:ap-northeast-2:561845507088:loadbalancer/app/terraform-lb/793b1b20c2d72a4a]
aws_lb_listener.http: Creating...
aws_lb_listener.http: Creation complete after 1s [id=arn:aws:elasticloadbalancing:ap-northeast-2:561845507088:listener/app/terraform-lb/793b1b20c2d72a4a/77f59508b44f6040]
aws_lb_listener_rule.asg: Creating...
aws_lb_listener_rule.asg: Creation complete after 0s [id=arn:aws:elasticloadbalancing:ap-northeast-2:561845507088:listener-rule/app/terraform-lb/793b1b20c2d72a4a/77f59508b44f6040/ff57d8ec2a971a3d]

Apply complete! Resources: 5 added, 1 changed, 0 destroyed.

Outputs:

alb_dns_name = "terraform-lb-977828332.ap-northeast-2.elb.amazonaws.com"

 







리소스 정리

C:\aws> terraform destroy
728x90
'💻 개발 일지/AWS' 카테고리의 다른 글
  • AWS - EKS
  • Terraform - 선언 블록
  • AWS - 웹 서비스 환경 구성 실습
  • AWS - Terraform
_민주
_민주
🌍여행을 좋아하는 개발자🌍
  • _민주
    _민주
    🌍여행을 좋아하는 개발자🌍
    • 전체 글 (52)
      • 💻 개발 일지 (2)
        • JavaScript (6)
        • Python (1)
        • Git (1)
        • RDBMS (1)
        • Linux (0)
        • Next (2)
        • K8s (3)
        • React (13)
        • AWS (16)
      • 🧩 기억의 조각 (1)
        • 일상 그리고 운동 (4)
        • ✈️ 여행 (0)
  • 태그

    approuter
    github actions
    js
    terraform
    next-auth
    s3
    EC2
    로그인
    결혼식
    React
    app-router
    운동
    googleprovider
    next14
    flask
    javascript
    AWS
    김나영콘서트
    mysql
    axios
  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
_민주
AWS - Terraform (ASG , LB)
상단으로

티스토리툴바