
이번 포스팅은 아래의 아키텍쳐를 테라폼 코드로 만들어 보겠다(항상 테라폼 코드로 작성하기전 관련리소스들을 직접 aws 콘솔에서 구현 해보고 어떤 리소스들이 필요한지 확인하고 코드로 작성하는걸 추천한다. VPC관련 구현은 이링크를 통해 만들어보자)

프로젝트 구성은 아래의 그림과같다

# variable.tf
variable "server_port" {
type = string
description = "app-port"
default = 8080
}
# 로컬에서 export TF_VAR_db_username=로 민감정보 보호
variable "db_username" {
type = string
description = "admin"
}
# 로컬에서 export TF_VAR_db_password=로 민감정보 보호
variable "db_password" {
type = string
description = "admin-password"
}
variable "image_id" {
type = string
description = "ubuntu-18"
default = "ami-0cb1d752d27600adb"
}
variable "instance_type" {
type = string
description = "instance-type"
default = "t2.micro"
먼저 공통으로 사용하는것들을 variable을 이용해서 변수로 만들어주자
# vpc.tf
provider "aws" {
region = "ap-northeast-2"
}
resource "aws_vpc" "my_vpc" {
cidr_block = "10.0.0.0/16"
tags = {
Name = "my-vpc"
}
}
먼저 프로바이더는 벤더사들을 의미 한다고 보면 되겠다 aws를 지정하고 리전을 서울 리전인 ap-northeast-2로 지정했다면 terraform을 진행 해주자 그리고 꼭 하나의 리소스를 만들었다면 plan명령어로 확인하고 apply를 해서 정확하게 의도대로 프로비저닝이 됐는지 콘솔에서 확인 하자!!그렇지 않으면 에러가 수두룩하게 발생하고 어디서 문제가 발생했는지 정확하게 알 수 없다 VPC가 만들어졌다면 퍼블릭 서브넷을 만들어보자
# publicSubnet.tf
resource "aws_subnet" "public_subnet_1" {
vpc_id = aws_vpc.my_vpc.id
cidr_block = "10.0.1.0/24"
availability_zone = "ap-northeast-2a"
tags = {
Name = "my-pub-subnet-1"
}
}
resource "aws_subnet" "public_subnet_2" {
vpc_id = aws_vpc.my_vpc.id
cidr_block = "10.0.2.0/24"
availability_zone = "ap-northeast-2c"
tags = {
Name = "my-pub-subnet-2"
}
}
resource "aws_internet_gateway" "my_igw" {
vpc_id = aws_vpc.my_vpc.id
tags = {
Name = "my-igw"
}
}
resource "aws_route_table" "my_route_table" {
vpc_id = aws_vpc.my_vpc.id
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.my_igw.id
}
tags = {
Name = "my-route-table"
}
}
resource "aws_route_table_association" "my_route_table_association_1" {
subnet_id = aws_subnet.public_subnet_1.id
route_table_id = aws_route_table.my_route_table.id
}
resource "aws_route_table_association" "my_route_table_association_2" {
subnet_id = aws_subnet.public_subnet_2.id
route_table_id = aws_route_table.my_route_table.id
}
퍼블릭 서브넷을 두개를 만들건데 서브넷이 두개의 가용영역을 걸치게 만들어주자 또한 퍼블릭 서브넷은 외부와의 통신이 가능해야 하기 때문에 인터넷 게이트웨이 리소스 만들어주자 만들었다면 라우팅 테이블에 라우팅을 해줘야한다 라우팅 테이블에 인터넷 게이트웨이를 라우팅 했다면 나머지 서브넷 두개도 라우팅을 해주자 이제 퍼블릭 서브넷을 만들어주자만들어줬다면 RDS가 있어야할 프라이빗 스브넷을 만들어보겠다
# privateSubnet.tf
resource "aws_subnet" "private_subnet_1" {
vpc_id = aws_vpc.my_vpc.id
cidr_block = "10.0.3.0/24"
availability_zone = "ap-northeast-2a"
tags = {
Name = "my-private-subnet-1"
}
}
resource "aws_subnet" "private_subnet_2" {
vpc_id = aws_vpc.my_vpc.id
cidr_block = "10.0.4.0/24"
availability_zone = "ap-northeast-2b"
tags = {
Name = "my-private-subnet-2"
}
}
resource "aws_eip" "nat_1" {
vpc = true
lifecycle {
create_before_destroy = true
}
}
resource "aws_nat_gateway" "my_nat_gateway" {
allocation_id = aws_eip.nat_1.id
subnet_id = aws_subnet.public_subnet_1.id
tags = {
Name = "my-nat-gw"
}
}
resource "aws_route_table" "my_route_table_private" {
vpc_id = aws_vpc.my_vpc.id
tags = {
Name = "my-private-route-table"
}
}
resource "aws_route_table_association" "my_route_table_association_private_1" {
subnet_id = aws_subnet.private_subnet_1.id
route_table_id = aws_route_table.my_route_table_private.id
}
resource "aws_route_table_association" "my_route_table_association_private_2" {
subnet_id = aws_subnet.private_subnet_2.id
route_table_id = aws_route_table.my_route_table_private.id
}
resource "aws_route" "private_nat" {
route_table_id = aws_route_table.my_route_table_private.id
destination_cidr_block = "0.0.0.0/0"
nat_gateway_id = aws_nat_gateway.my_nat_gateway.id
}
퍼블릭 서브넷을 구현하는코드와 비슷하지만 프라이빗 서브넷은 외부와 통신이필요하지않기 때문에 인터넷 게이트웨이를 만들어주지않는다 먼저 아키텍쳐대로 구성하기위해서 nat 게이트웨이를 만들어줘야하는데 nat 게이트웨이는 퍼블릭 서브넷에 만들어줘야한다 꼭!! 그이유프라이빗 서브넷을 만들어주는 이유는 데이터베이스 서버나 백엔드 서버같은 중요한 민감정보나 중요 로직은 외부에서 맘대로 접속 하면안되기 때문이다 하지만 벡엔드 서버에서 필요한 의존성들을 설치하려면 외부에서 다운로드를 해야하기 때문에 NAT 게이트웨이를 만들어서 퍼블릭 서브넷을 통해 외부로 통신이 가능하게끔 하는것이다.
# sg.tf
resource "aws_security_group" "ec2_mysql_sg" {
name = "my-sg"
vpc_id = aws_vpc.my_vpc.id
ingress = [{
cidr_blocks = ["0.0.0.0/0"]
description = "port 22"
from_port = 22
ipv6_cidr_blocks = []
prefix_list_ids = []
protocol = "tcp"
security_groups = []
self = false
to_port = 22
},
{
cidr_blocks = ["0.0.0.0/0"]
description = "port 8080"
from_port = 8080
ipv6_cidr_blocks = []
prefix_list_ids = []
protocol = "tcp"
security_groups = []
self = false
to_port = 8080
},
{
cidr_blocks = ["0.0.0.0/0"]
description = "port 80"
from_port = 80
ipv6_cidr_blocks = []
prefix_list_ids = []
protocol = "tcp"
security_groups = []
self = false
to_port = 80
}]
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
resource "aws_security_group" "rds_sg" {
vpc_id = aws_vpc.my_vpc.id
name = "rds-sg"
ingress = [{
cidr_blocks = ["0.0.0.0/0"]
description = "mysql-sg"
from_port = 3306
ipv6_cidr_blocks = []
prefix_list_ids = []
protocol = "tcp"
security_groups = []
self = false
to_port = 3306
}]
egress = [{
cidr_blocks = ["0.0.0.0/0"]
description = "mysql-en-sg"
from_port = 0
ipv6_cidr_blocks = []
prefix_list_ids = []
protocol = "-1"
security_groups = []
self = false
to_port = 0
}]
}
ec2와 rds를 위한 보안그룹을 만들건데 ec2는 ssh접속을 위한 22번 포트 busybox로 확인 하기위한 8080포트 로드벨런서로 타겟포트 포트포워딩을 위해 80포트를 인바운드로 설정하고 아웃바운드는 모든 프로토콜로 설정 RDS는 mysql로 만들거기 때문에 3306포트를 열어주겠다
# alb.tf
resource "aws_lb" "my_alb" {
name = "my-alb"
internal = false
load_balancer_type = "application"
security_groups = [aws_security_group.ec2_mysql_sg.id]
subnets = [
aws_subnet.public_subnet_1.id,
aws_subnet.public_subnet_2.id
]
enable_cross_zone_load_balancing = true
# enable_deletion_protection = true
tags = {
Environment = "my-alb"
}
}
output "alb_dns_name" {
value = aws_lb.my_alb.dns_name
description = "The domain name of the load balancer"
}
resource "aws_lb_target_group" "my_alb_tg" {
name = "my-alb-tg"
port = 8080
protocol = "HTTP"
vpc_id = aws_vpc.my_vpc.id
health_check {
interval = 30
path = "/"
healthy_threshold = 3
unhealthy_threshold = 3
}
}
resource "aws_lb_listener" "alb_lintener" {
load_balancer_arn = aws_lb.my_alb.arn
port = 80
protocol = "HTTP"
default_action {
type = "forward"
target_group_arn = aws_lb_target_group.my_alb_tg.arn
}
}
오토 스케일링으로 ec2를 스케일업을 진행할건데 ec2를 부하 분한을 위해 로드벨런서를 앞단에 구성할거다 L7 로드벨런서로 타입을 application으로 지정 하고 타겟 그룹은 busybox를 위한 8080포트를 타겟으로하고 리스너는 80포트로 들어오면 8080으로 포워딩을 위해 타입을 forward로 지정
# autoScalingGroup.tf
resource "aws_key_pair" "ec2_key" {
key_name = "my-ec2"
public_key = file("~/.ssh/id_rsa.pub")
}
resource "aws_launch_configuration" "ec2_auto_scaling" {
image_id = var.image_id
instance_type = var.instance_type
key_name = aws_key_pair.ec2_key.key_name
security_groups = [aws_security_group.ec2_mysql_sg.id]
associate_public_ip_address = true
user_data = <<-EOF
#!/bin/bash
PUBIP=$(ec2metadata | grep public-ipv4)
echo "<h1> IP:($PUBIP) </h1>" > index.html
nohup busybox httpd -f -p ${var.server_port} &
EOF
lifecycle {
create_before_destroy = true
}
}
resource "aws_autoscaling_group" "my_asg" {
name = "my-asg"
min_size = 2
max_size = 10
health_check_grace_period = 300
health_check_type = "ELB"
desired_capacity = 4
force_delete = true
target_group_arns = [aws_lb_target_group.my_alb_tg.arn]
launch_configuration = aws_launch_configuration.ec2_auto_scaling.name
vpc_zone_identifier = [aws_subnet.public_subnet_1.id, aws_subnet.public_subnet_2.id]
}
오토스케일링 위해 먼저 스케일업이될 ec2 AMI를 세팅하고 로드벨런싱이 되는지 확인하기위해 ec2matadata 명령어로 각 인스턴스의 IP를 파싱(ec2matadata명령어는 우분투만 가능 아마존 리눅스는 전에 사용했을때 안먹혔음.. 지금은 잘모름) 오토 스케일링 그룹에서 최소 유지해야할 인스턴스의 사이즈는 2개 최대는 10개로 지정하고 기본으로 4개가 스케일링이되게 설정한다
# rds.tf
resource "aws_db_subnet_group" "privat_subnet_group" {
subnet_ids = [aws_subnet.private_subnet_1.id, aws_subnet.private_subnet_2.id]
tags = {
Name = "private-subnet-group"
}
}
resource "aws_db_instance" "mysql_rds" {
allocated_storage = 8
engine = "mysql"
db_name = "mydb"
engine_version = "5.7"
instance_class = "db.t3.micro"
db_subnet_group_name = aws_db_subnet_group.privat_subnet_group.name
availability_zone = "ap-northeast-2a"
skip_final_snapshot = true
deletion_protection = true
username = var.db_username
password = var.db_password
vpc_security_group_ids = [aws_security_group.rds_sg.id]
tags = {
Name = "my-rds"
}
}
rds설정을 위해 위에서만든 프라이빗 서브넷으로 서브넷 그룹을 만들어주자 rds를 세팅하고 apply, destroy를 하게되면 rds를 실행하고 삭제하고 시간이 오래 걸리게된다 destroy할때 rds만 제외하고 destroy를할 수 있게 도와주는 옵션으로 deletion_protection=true 를 추가해주자 이렇게 되면 rds는 제외하고 destroy를 하게되는데 rds와 의존하고있는 다른 리소스도 포함해서 제외되니 참고 하면되겠다
다음 포스팅으로는 module화를 진행 해보도록 하겠다
'DevOps > Terraform' 카테고리의 다른 글
[Terraform] 테라폼의 개념 (0) | 2023.02.08 |
---|