Terraform
Terraform은 IAC 도구 중 하나로, 클라우드 환경을 GUI가 아닌 Local에서 command 단위로 프로비저닝 할 수 있다는 장점이 있다.
이번 4-2 캡스톤 프로젝트에서 AWS 환경을 구성하기 위해 사용하였다.
IAM User, Access key 생성
Local에서 aws 환경에 접근하고 리소스들을 생성할 권한을 얻을 수 있도록 미리 IAM User를 생성하고 Local 개발환경(여기서는 Vs code)에 미리 연결 시켜줄 필요가 있다.
aws sts get-caller-identity
AWS CLI를 구성하고 IAM User를 연결한 뒤 위 명령어를 실행하면 현재 연결되어있는 IAM User를 확인할 수 있다 위와 같은 결과가 나올 경우 정상적으로 연결이 된 것으로 Terraform을 사용할 준비가 된 것이다.
리소스 모듈화
전체 폴더 구조이다. 가능한 모든 리소스들을 모듈화해서 사용하도록 노력하였다.
유지보수를 편하게 하기 위함이다.
Bastion
Bastion은 주로 AWS EC2 서버를 의미하며 관리용 서버로 사용된다. 우리 프로젝트에선 서비스들을 도커 컨테이너로 올릴 때 사용할 메인 서버로 사용한다.
보안 그룹 설정
EC2 내부에 접근할 서비스들의 Port들을 미리 설정해줄 수 있다. SSH, TCP, 여러 서비스 Port들을 설정한다.
resource "aws_security_group" "bastion_sg" {
vpc_id = var.vpc_id
# allow all outbound
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
# allow ssh
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
# TCP port 80 for HTTP
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
# TCP port 443 for HTTPS
ingress {
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
# allow test nginx
ingress {
from_port = 8000
to_port = 8000
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
description = "test nginx"
}
# fastapi 8080
ingress {
from_port = 8080
to_port = 8080
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
description = "fast-api"
}
# react 3000
ingress {
from_port = 3000
to_port = 3000
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
description = "react"
}
tags = {
Name = "${var.common_info.env}-${var.common_info.service_name}-bastion-sg"
}
}
SSH Key 생성
ec2 서버 접근의 보안을 위해서 ssh key 또한 생성해준다.
# Create RSA key of size 4096 bits
resource "tls_private_key" "bastion_key" {
algorithm = "RSA"
rsa_bits = 4096
}
# # Create local file
# resource "local_file" "bastion_key" {
# content = tls_private_key.bastion_key.private_key_pem
# filename = "./bastion_key.pem"
# }
# Create AWS key pair
resource "aws_key_pair" "bastion_key" {
key_name = "bastion_key"
public_key = tls_private_key.bastion_key.public_key_openssh
}
bastion 생성
resource "aws_instance" "bastion" {
ami = "ami-0ee82191e264e07cc" # Amazon Linux 2 AMI (region-specific) seoul
instance_type = "t2.micro"
subnet_id = var.subnets_public_ids[0]
key_name = "bastion_key" # SSH key
vpc_security_group_ids = [aws_security_group.bastion_sg.id]
iam_instance_profile = aws_iam_instance_profile.ec2_instance_profile.name
user_data = <<-EOF
#!/bin/bash
sudo yum update -y
sudo yum install -y docker
sudo service docker start
sudo usermod -a -G docker ec2-user
sudo timedatectl set-timezone Asia/Seoul
mkdir -p /home/ec2-user/.docker/cli-plugins
curl -SL https://github.com/docker/compose/releases/download/v2.29.6/docker-compose-linux-x86_64 -o /home/ec2-user/.docker/cli-plugins/docker-compose
chmod +x /home/ec2-user/.docker/cli-plugins/docker-compose
sudo dd if=/dev/zero of=/swapfile bs=128M count=16
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo '/swapfile swap swap defaults 0 0' | sudo tee -a /etc/fstab
EOF
tags = {
Name = "${var.common_info.env}-${var.common_info.service_name}-bastion-server"
}
}
시기에 따라 AMI id는 바뀔 수 있기에 AWS GUI에서 실제로 확인을 하고 하드코딩해야 정상적으로 실행된다.
AWS 프리티어 계정을 사용하기 위해서 t2.micro 인스턴스를 사용하였고, user_data를 미리 설정하여 도커를 설치하고, 시간을 동기화하고, OOM 문제를 예방하기 위해 스왑메모리를 사용한다.
VPC
우리 프로젝트에선 한 개의 가용영역에 하나의 Public subenet만을 이용할 것이기에 Nat Gateway는 따로 생성하지 않았다.
DB를 사용하였더라면 Private Subnet과 함께 생성했겠지만 S3 Bucket만을 사용할 예정이다.
Internet gateway
resource "aws_internet_gateway" "internet_gateway" {
vpc_id = aws_vpc.vpc.id
tags = {
"Name" = "${var.common_info.service_name}-igw"
}
}
Subnet
resource "aws_subnet" "subnets_public" {
for_each = var.vpc_info.cidr_blocks_public
vpc_id = aws_vpc.vpc.id
cidr_block = each.value.cidr_block
availability_zone = each.value.availability_zone
map_public_ip_on_launch = true
tags = merge(
{
Name = "${var.common_info.env}-${each.value.subnet_name}"
}
)
}
# resource "aws_subnet" "subnets_private" {
# for_each = var.vpc_info.cidr_blocks_private
# vpc_id = aws_vpc.vpc.id
# cidr_block = each.value.cidr_block
# availability_zone = each.value.availability_zone
# tags = merge(
# {
# Name = "${var.common_info.env}-${each.value.subnet_name}"
# }
# )
# }
#resource "aws_subnet" "subnets_private_db" {
# for_each = var.vpc_info.cidr_blocks_private_db
# vpc_id = aws_vpc.vpc.id
# cidr_block = each.value.cidr_block
# availability_zone = each.value.availability_zone
# tags = merge(
# {
# Name = "${var.common_info.env}-${each.value.subnet_name}"
# }
# )
#}
# resource "aws_subnet" "subnets_private_db" {
# vpc_id = aws_vpc.vpc.id
# cidr_block = var.vpc_info.cidr_blocks_private_db["private_db_a"].cidr_block
# availability_zone = var.vpc_info.cidr_blocks_private_db["private_db_a"].availability_zone
# tags = merge(
# {
# Name = "${var.common_info.env}-${var.vpc_info.cidr_blocks_private_db["private_db_a"].subnet_name}"
# }
# )
# }
Routing table
resource "aws_route_table" "route_table_public" {
vpc_id = aws_vpc.vpc.id
tags = merge(
{
Name = "${var.common_info.env}-${var.common_info.service_name}-public"
}
)
}
# resource "aws_route_table" "route_table_private" {
# vpc_id = aws_vpc.vpc.id
# for_each = var.vpc_info.cidr_blocks_private
# tags = merge(
# {
# Name = "${var.common_info.env}-${each.value.subnet_name}"
# },
# var.common_tags
# )
# }
# resource "aws_route_table" "route_table_private_db" {
# vpc_id = aws_vpc.vpc.id
# tags = merge(
# {
# Name = "${var.common_info.env}-${var.common_info.service_name}-private-db"
# },
# var.common_tags
# )
# }
resource "aws_route" "routes_public" {
route_table_id = aws_route_table.route_table_public.id
destination_cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.internet_gateway.id
}
# resource "aws_route" "routes_private" {
# count = length(var.vpc_info.cidr_blocks_private)
# route_table_id = aws_route_table.route_table_private[keys(var.vpc_info.cidr_blocks_private)[count.index]].id
# destination_cidr_block = "0.0.0.0/0"
# nat_gateway_id = aws_nat_gateway.nat_gateway.id
# }
# resource "aws_route" "routes_private" {
# for_each = var.vpc_info.cidr_blocks_private
# route_table_id = aws_route_table.route_table_private[each.key].id
# destination_cidr_block = "0.0.0.0/0"
# nat_gateway_id = lookup(var.vpc_info.private_to_public_map, each.key, null) != null ? aws_nat_gateway.nat_gateway[lookup(var.vpc_info.private_to_public_map, each.key)].id : null
# }
resource "aws_route_table_association" "route_table_association_public" {
for_each = var.vpc_info.cidr_blocks_public
subnet_id = aws_subnet.subnets_public[each.key].id
route_table_id = aws_route_table.route_table_public.id
}
# resource "aws_route_table_association" "route_table_association_private" {
# for_each = var.vpc_info.cidr_blocks_private
# subnet_id = aws_subnet.subnets_private[each.key].id
# route_table_id = aws_route_table.route_table_private[each.key].id
# }
#resource "aws_route_table_association" "route_table_association_private_db" {
# for_each = var.vpc_info.cidr_blocks_private_db
# subnet_id = aws_subnet.subnets_private_db[each.key].id
# route_table_id = aws_route_table.route_table_private_db.id
#}
# resource "aws_route_table_association" "route_table_association_private_db" {
# subnet_id = aws_subnet.subnets_private_db.id
# route_table_id = aws_route_table.route_table_private_db.id
# }
Vpc
resource "aws_vpc" "vpc" {
cidr_block = var.vpc_info.cidr_block_vpc
enable_dns_support = true
enable_dns_hostnames = true
tags = {
"Name" = "${var.common_info.env}-${var.vpc_info.vpc_name}"
}
}
Main.tf
모듈들을 main.tf에서 최종적으로 생성한다. Terraform cli는 이 Main.tf가 있는 폴더나 디렉토리에서 실행되어야 한다.
module "vpc" {
source = "../../modules/vpc"
common_info = local.common_info
common_tags = local.common_tags
vpc_info = local.vpc_info
}
module "bastion" {
source = "../../modules/bastion"
common_info = local.common_info
common_tags = local.common_tags
vpc_info = local.vpc_info
vpc_id = module.vpc.vpc_id
subnets_public_ids = module.vpc.subnets_public_ids
}
Terraform apply 및 결과 확인
정상적으로 생성이 된 것을 확인 가능하다.
Terraform destroy
삭제된 것 확인 가능하다.
Bastion ssh 접근 확인
Mobaxterm을 사용해서 확인하였다.
정상적으로 접근이 되는 것을 확인 가능하다.
'IAC > Terraform' 카테고리의 다른 글
[Terraform] AWS ALB 프로비저닝 (0) | 2024.12.09 |
---|---|
[Terraform] Karpenter 프로비저닝 (1) | 2024.12.09 |
[Terraform] EKS 프로비저닝 (0) | 2024.12.09 |
[Terraform] 고가용성 Multi AZ's VPC 환경 프로비저닝 (0) | 2024.12.09 |
[Terraform] S3 생성 (0) | 2024.12.05 |