웹 서버 클러스터 배포
# 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