Terraform을 사용해서 이번 프로젝트에서 구축해야하는 AWS 리소스들을 코드로 관리할 수 있게끔 했다.
이번에는 프로젝트를 진행하면서 작성한 Terraform을 사용해서 어떻게 IaC 구현을 했는지 대한 기록을 남겨두려고 한다.
Terraform Backend 구성하기
테라폼 백엔드를 사용해서 tfstate 파일을 깃허브 레포지토리로부터 분리해 외부로 현재 인프라가 어떻게 구성되었는지에 대한 정보가 노출되지 않도록 했다. s3만 사용할 수도 있지만, 다이나모 DB를 사용해서 locking까지 할 수 있도록 했다.
먼저 따로 만들어둔 테라폼 백엔드 작업용 디렉토리에서 tfstate파일을 저장할 저장소를 terraform을 사용해서 생성한다.
참고 : https://devops-james.tistory.com/123
저장소 생성 코드
#terraformbackendresource.tf
provider "aws" {
region = "ap-northeast-2"
}
resource "aws_s3_bucket" "mys3bucket" {
bucket = "Devops-Final-tfstate"
}
# Enable versioning so you can see the full revision history of your state files
resource "aws_s3_bucket_versioning" "mys3bucket_versioning" {
bucket = aws_s3_bucket.mys3bucket.id
versioning_configuration {
status = "Enabled"
}
}
resource "aws_dynamodb_table" "mydynamodbtable" {
name = "terraform-locks"
billing_mode = "PAY_PER_REQUEST"
hash_key = "LockID"
attribute {
name = "LockID"
type = "S"
}
}
output "s3_bucket_arn" {
value = aws_s3_bucket.mys3bucket.arn
description = "[S3버킷의 ARN을 입력하세요]"
}
output "dynamodb_table_name" {
value = aws_dynamodb_table.mydynamodbtable.name
description = "[다이나모 DB 테이블 이름 적어주세요]"
그리고 원격 백엔드를 둘 경우에는 먼저 둘 곳인 S3 저장소가 생성된 이후에 다시 terraform init을 해서 Terraform Backend를 적용시켜줘야한다.
Terraform Backend 적용 코드
terraform {
backend "s3" {
bucket = "Devops-Final-tfstate"
key = "TF-backend/terraform.tfstate"
region = "ap-northeast-2"
dynamodb_table = "terraform-locks"
}
}
그리고 terraform init을 하면 아래처럼 테라폼 백엔드가 적용되는 것을 볼 수 있다.
terraform init
Initializing the backend...
Do you want to copy existing state to the new backend?
Pre-existing state was found while migrating the previous "local" backend to the
newly configured "s3" backend. No existing state was found in the newly
configured "s3" backend. Do you want to copy this state to the new "s3"
backend? Enter "yes" to copy and "no" to start with an empty state.
Terraform 코드를 사용한 리소스 생성.
VPC.tf
VPC.tf 에서 서브넷과 인터넷 게이트웨이, 보안 그룹, 라우팅 테이블 구성을 해준다. 그리고 VPC내부에서 보호되고있는 컴퓨팅 유닛이 VPC외부의 AWS리소스와 상호작용 할 수 있도록 VPC 엔드 포인트도 만들어줬다.
소스코드 더보기
resource "aws_vpc" "my-vpc" {
cidr_block = "10.0.0.0/16"
enable_dns_support = true
enable_dns_hostnames = true
tags = {
Name = "Terraform VPC"
}
}
# create subnet
resource "aws_subnet" "PublicSubnet01" {
vpc_id = aws_vpc.my-vpc.id
cidr_block = "10.0.1.0/24"
availability_zone = "ap-northeast-2a"
map_public_ip_on_launch = true
tags = {
Name = "my-public-subnet01"
}
}
resource "aws_subnet" "PublicSubnet02" {
vpc_id = aws_vpc.my-vpc.id
cidr_block = "10.0.2.0/24"
availability_zone = "ap-northeast-2c"
map_public_ip_on_launch = true
tags = {
Name = "my-public-subnet02"
}
}
resource "aws_subnet" "PrivateSubnet01" {
vpc_id = aws_vpc.my-vpc.id
cidr_block = "10.0.3.0/24"
availability_zone = "ap-northeast-2a"
tags = {
Name = "my-private-subnet01"
}
}
resource "aws_subnet" "PrivateSubnet02" {
vpc_id = aws_vpc.my-vpc.id
cidr_block = "10.0.4.0/24"
availability_zone = "ap-northeast-2c"
tags = {
Name = "my-private-subnet02"
}
}
# 인터넷 게이트웨이 ( 외부 인터넷에 연결하기 위함 )
resource "aws_internet_gateway" "my-IGW" {
vpc_id = aws_vpc.my-vpc.id
}
# 라우팅 테이블
## 1. 퍼블릭 라우팅 테이블 정의
resource "aws_route_table" "my-public-route" {
vpc_id = aws_vpc.my-vpc.id
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.my-IGW.id
}
}
## 퍼블릭 라우팅 테이블 연결
resource "aws_route_table_association" "my-public-RT-Assoication01" {
subnet_id = aws_subnet.PublicSubnet01.id
route_table_id = aws_route_table.my-public-route.id
}
resource "aws_route_table_association" "my-public-RT-Assoication02" {
subnet_id = aws_subnet.PublicSubnet02.id
route_table_id = aws_route_table.my-public-route.id
}
## 보안 그룹
resource "aws_security_group" "my-SG" {
vpc_id = aws_vpc.my-vpc.id
name = "my SG"
description = "my SG"
tags = {
Name = "my SG"
}
}
## 보안 그룹 규칙
resource "aws_security_group_rule" "my-ingress-rule-22" {
type = "ingress"
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
security_group_id = aws_security_group.my-SG.id
lifecycle {
create_before_destroy = true
}
}
resource "aws_security_group_rule" "my-ingress-rule-80" {
type = "ingress"
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
security_group_id = aws_security_group.my-SG.id
lifecycle {
create_before_destroy = true
}
}
resource "aws_security_group_rule" "my-ingress-rule-443" {
type = "ingress"
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
security_group_id = aws_security_group.my-SG.id
lifecycle {
create_before_destroy = true
}
}
resource "aws_security_group_rule" "my-ingress-rule-3000" {
type = "ingress"
from_port = 3000
to_port = 3000
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
security_group_id = aws_security_group.my-SG.id
lifecycle {
create_before_destroy = true
}
}
resource "aws_security_group_rule" "my-ingress-rule-3306" {
type = "ingress"
from_port = 3306
to_port = 3306
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
security_group_id = aws_security_group.my-SG.id
lifecycle {
create_before_destroy = true
}
}
resource "aws_security_group_rule" "my-egress-rule" {
type = "egress"
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
security_group_id = aws_security_group.my-SG.id
lifecycle {
create_before_destroy = true
}
}
# vpc내부의 컴퓨팅 유닛이 다이나모 DB에 연결할 수 있도록 하기위해 vpc 엔드포인트 생성.
resource "aws_vpc_endpoint" "my-vpc-endpoint" {
vpc_id = aws_vpc.my-vpc.id
service_name = "com.amazonaws.ap-northeast-2.dynamodb"
vpc_endpoint_type = "Gateway"
}
resource "aws_vpc_endpoint_route_table_association" "my_vpc_endpoint_rt_association" {
vpc_endpoint_id = aws_vpc_endpoint.my-vpc-endpoint.id
route_table_id = aws_route_table.my-public-route.id
}
alb.tf
인터넷과 연결되지 않고 VPC 내부의 컴퓨팅 유닛으로 Load Balancing을 수행하는 ALB를 만들어줬다.
소스코드 더보기
resource "aws_alb" "my_alb" {
name = "Taskmanagement-ALB2"
internal = true
load_balancer_type = "application"
security_groups = [aws_security_group.my-SG.id]
subnets = [aws_subnet.PrivateSubnet01.id, aws_subnet.PrivateSubnet02.id]
enable_cross_zone_load_balancing = true
}
# 80포트를 리슨하고
resource "aws_lb_listener" "lb_listener" {
load_balancer_arn = aws_alb.my_alb.arn
port = 80
protocol = "HTTP"
default_action {
type = "forward"
target_group_arn = aws_lb_target_group.my-target-group.arn
}
}
# 3000포트를 타겟한다.
resource "aws_lb_target_group" "my-target-group" {
name = "Taskmanagement-TG2"
port = 3000
protocol = "HTTP"
vpc_id = aws_vpc.my-vpc.id
target_type = "ip"
health_check {
enabled = true # 헬스 체크 활성화
interval = 30 # 헬스 체크 간격(초)
path = "/" # 헬스 체크에 사용할 경로
protocol = "HTTP" # 사용할 프로토콜
timeout = 5 # 각 헬스 체크에 대한 타임아웃(초)
healthy_threshold = 3 # 건강한 상태로 판단하기 전에 연속적으로 통과해야 하는 헬스 체크 수
unhealthy_threshold = 3 # 건강하지 않은 상태로 판단하기 전에 연속적으로 실패해야 하는 헬스 체크 수
matcher = "200-299" # 헬스 체크 응답에 대한 HTTP 상태 코드
}
}
apigateway.tf
APIGateway는 VPC 내부의 private ALB와 연결돼야하기 때문에 HTTP API로 생성했다.
api 설계에 맞춰서 구현된 alb와 lambda의 엔드포인트로 넘어가는 요청 메서드들을 설정해줬다.
그리고 private ALB에 연결할 수 있도록 VPC Link를 만들어줬다.
코드보기
module "apigateway" {
source = "terraform-aws-modules/apigateway-v2/aws"
version = "2.2.2"
create_api_domain_name = false
name = "Task-management API-tf"
description = "Task-management API created by terraform"
protocol_type = "HTTP"
#cors 설정. 테스트를 위해서 모든 헤더와 메서드, 송신자를 허용한다.
cors_configuration = {
allow_headers = ["*"]
allow_methods = ["*"]
allow_origins = ["*"]
}
default_route_settings = {
data_trace_enabled = true
detailed_metrics_enabled = true
logging_level = "INFO"
throttling_burst_limit = 5000
throttling_rate_limit = 10000
}
integrations = {
"POST /user" = {
lambda_arn = "[람다 ARN을 입력하세요]"
payload_format_version = "1.0"
timeout_milliseconds = 12000
}
"GET /" = {
connection_type = "VPC_LINK"
vpc_link = "my-vpc-Link"
integration_uri = "[로드밸런서의 리스너 ARN을 입력하세요]"
integration_type = "HTTP_PROXY"
integration_method = "GET"
}
"POST /" = {
connection_type = "VPC_LINK"
vpc_link = "my-vpc-Link"
integration_uri = "[로드밸런서의 리스너 ARN을 입력하세요]"
integration_type = "HTTP_PROXY"
integration_method = "POST"
}
"PUT /{Task_id}" = {
connection_type = "VPC_LINK"
vpc_link = "my-vpc-Link"
integration_uri = "[로드밸런서의 리스너 ARN을 입력하세요]"
integration_type = "HTTP_PROXY"
integration_method = "PUT"
}
"DELETE /{Task_id}" = {
connection_type = "VPC_LINK"
vpc_link = "my-vpc-Link"
integration_uri = "[로드밸런서의 리스너 ARN을 입력하세요]"
integration_type = "HTTP_PROXY"
integration_method = "DELETE"
}
}
#VPC에 의해서 외부로부터 보호받고있는 ALB에 연결하기 위해서
vpc_links = {
my-vpc-Link = {
name = "Task-VPC-Link-tf"
security_group_ids = ["서브넷 아이디 입력하세요"] # 내가 연결하고싶은 vpc의 서브넷에 연결한다.
subnet_ids = ["서브넷 아이디 입력하세요", "서브넷 아이디 입력하세요"]
}
}
'DevOps' 카테고리의 다른 글
Kubectl - 쿠버네티스 클러스터를 관리해보자! (0) | 2023.08.14 |
---|---|
[DevOps] 최종 프로젝트 회고 - 일정 및 작업 관리 (0) | 2023.07.11 |
[DevOps] 최종 프로젝트 회고 - 구현(클라우드 리소스) (0) | 2023.06.20 |
[DevOps] 최종 프로젝트 회고 - 구현(CI/CD) (0) | 2023.06.19 |
[DevOps] 최종 프로젝트 회고 - 리소스 아키텍처 설계 (0) | 2023.06.19 |