안녕하세요. 스타트업에서 software engineering 직책을 맡고 있는 백진우입니다.
회사에서 다양한 aws 서비스를 사용하게 되면서, 그에 따라 아래와 같은 이유로 다양한 환경을 생성하거나 관리하기 어려워지기 시작하였습니다.
따라서, 저는 위와 같은 문제를 해결하면서 인프라를 효율적으로 구상할 필요를 느껴 Infrastructure as Code, IaC 도구인 Terraform을 사용하기로 하였습니다.
도입하면서 배웠던 점, 시행착오들을 정리해보고자 합니다. 이 글을 잃고 다양한 의견과 조언해주시면 감사하겠습니다:)
세 개의 아티클을 참고하여 마지막 large-terraform 구조를 사용하기로 결정하였습니다.
Standard Module Structure : terraform 기본 구조를 어떻게 해야하는 지 설명
Terraform code structures : terraform 전체적인 코드 구조 설명 및 예제
large-terraform : terraform best practices의 large terraform github 코드 예제
아래는 제가 만든 terraform 초기 module 구조입니다. 세부적인 코드 설명은 아래에서 진행하도록 하겠습니다!
├── prod
├── stage
├── develop
├── master
│ ├── main.tf
│ ├── outputs.tf
│ ├── terraform.tfvars
│ └── variables.tf
├── modules
│ ├── database
│ │ ├── aurora
│ │ │ ├── main.tf
│ │ │ ├── outputs.tf
│ │ └── └── variables.tf
└── └── s3
Terraform으로 인프라를 관리할 때 설정파일을 만들고 이를 terraform apply하면 terraform.tfstate 파일이 생기게 됩니다. Terraform Backend 는 terraform.tfstate을 어디에 저장을 하고, 가져올지에 대한 설정입니다. 기본적으로는 로컬에 저장이 되지만, aws s3등에 저장할 수 있게 설정할 수 있습니다. 아래는 backend 적용시 장점입니다.
├── prod
├── stage
├── develop
├── master
│ ├── main.tf
│ ├── outputs.tf
│ ├── terraform.tfvars
│ └── variables.tf
├── modules
│ ├── database
│ │ ├── aurora-postgresql
│ │ │ ├── main.tf
│ │ │ ├── outputs.tf
│ │ └── └── variables.tf
└── ├── s3
├── init # terraform backend 코드가 있는 곳
└── └── init.tf
Terraform backend는 한번 만 생성되면 되기 때문에 init
이라는 폴더를 따로 만들어 backend를 관리해주었습니다.
init.tf
provider "aws" {
region = "ap-northeast-2" # Please use the default region ID
}
# S3 bucket for backend
resource "aws_s3_bucket" "terraform_tfstate" {
bucket = "terraform-tfstate"
versioning {
enabled = true # Prevent from deleting tfstate file
}
}
# DynamoDB for terraform state lock
resource "aws_dynamodb_table" "terraform_lock" {
name = "silvia-terraform-lock"
hash_key = "LockID"
billing_mode = "PAY_PER_REQUEST"
attribute {
name = "LockID"
type = "S"
}
}
S3 bucket as backend
테라폼의 상태를 저장하기 위해 S3 버킷을 생성합니다.
DynamoDB Table for Lock
동시에 같은 파일을 수정하지 못하도록 하기 위해 DynamoDB에 작업에 대한 Lock을 생성합니다.
terraform의 aws module 코드는 Terraform AWS 에서 가져와 사용하였습니다. 모듈중에 aws aurora의 postgresql을 생성해보겠습니다.
main.tf
locals {
region = "ap-northeast-2"
}
#####
# VPC and subnets
#####
data "aws_vpc" "default" {
default = true
}
data "aws_subnet_ids" "all" {
vpc_id = data.aws_vpc.default.id
}
module "aurora_postgresql" {
source = "terraform-aws-modules/rds-aurora/aws"
name = var.name
master_username = var.master_username
master_password = var.master_password
vpc_id = data.aws_vpc.default.id
subnets = data.aws_subnet_ids.all.ids
engine = "aurora-postgresql"
engine_version = "12.7"
engine_mode = "provisioned"
instance_class = var.instance_class
instances = {
1 = {}
2 = {}
}
preferred_backup_window = "07:00-09:00"
autoscaling_min_capacity = 2
autoscaling_max_capacity = 5
backup_retention_period = 7
port = 5432
publicly_accessible = true
apply_immediately = true
skip_final_snapshot = true
storage_encrypted = true
autoscaling_enabled = true
copy_tags_to_snapshot = true
# Disable creation of subnet group - provide a subnet group
create_db_subnet_group = false
# Disable creation of security group - provide a security group
create_security_group = false
# Disable creation of monitoring IAM role - provide a role ARN
create_monitoring_role = false
# Disable creation of random password - AWS API provides the password
create_random_password = false
# tags = {
# Environment = var.env
# Engine = "aurora-postgresql"
# }
}
data "aws_vpc" "default" {
default = true
}
data "aws_subnet_ids" "all" {
vpc_id = data.aws_vpc.default.id
}
Terraform에서 Data Sources를 통해 Terraform 외부에서 정의되거나 다른 별도의 Terraform 구성에 의해 정의되거나 기능에 의해 수정된 정보를 사용할 수 있습니다. 위 코드는 아래와 같이 작동됩니다.
해당 부분은 추후에 별개의 vpc로 구성할 예정입니다.
module "aurora_postgresql" {
source = "terraform-aws-modules/rds-aurora/aws"
name = var.name
master_username = var.master_username
master_password = var.master_password
vpc_id = data.aws_vpc.default.id
subnets = data.aws_subnet_ids.all.ids
우선 Terraform Registry 에서 다른 사람들이 구현해 놓은 aurora_postgresql을 찾아 source를 통해 해당 부분을 적용시켰습니다. 아키텍쳐가 쉬운 경우에는 Terraform AWS 찾아도 괜찮습니다.
The source argument in a module block tells Terraform where to find the source code for the desired child module.
name = var.name
master_username = var.master_username
master_password = var.master_password
위의 부분은 variables.tf 에서 정의된 변수를 사용한 부분으로 유동적으로 바뀔 수 있는 부분입니다.
vpc_id와 subnets은 data sources에서 가져온 것을 그대로 사용하였습니다.
outputs.tf
output "cluster_id" {
description = "The RDS Cluster Identifier"
value = module.aurora_postgresql.cluster_id
}
output "cluster_master_username" {
description = "The database master username"
value = module.aurora_postgresql.cluster_master_username
sensitive = true
}
output "cluster_master_password" {
description = "The database master password"
value = module.aurora_postgresql.cluster_master_password
sensitive = true
}
Root module 에서는 terraform apply 를 통해 infrastructure 가 구성된 후 정의된 outputs 들이 command line 창에 나타나게 됩니다.
Child module 에서는 outputs 들이 attribute 로 root module 에 전달됩니다.
output으로 내보낼 수 있는 것은 정해져 있습니다. ( aws aurora output )
variables.tf
variable "name" {
description = "Name used across resources created"
type = string
}
variable "instance_class" {
description = "instance class"
type = string
default = "db.r6g.large"
}
variable "master_username" {
description = "(Required) Username for the master DB user"
type = string
}
variable "master_password" {
description = "(Required) Password for the master DB user"
type = string
}
필요한 변수를 정의하는 곳입니다. 정의한 변수에 값을 주입하기 위해 가장 일반적인 방법은 terraform.tfvars 파일을 생성하는 것입니다.
Environment Code는 실제로 생성할 환경에 대한 다양한 설정값을 명시한 코드입니다.
아래 코드는 develop 환경 기준입니다!
main.tf
terraform {
backend "s3" {
bucket = "terraform-tfstate"
key = "terraform/develop/database/aurora_postgresql/terraform.tfstate" # develop
region = "ap-northeast-2"
encrypt = true
dynamodb_table = "terraform-lock"
}
}
provider "aws" {
region = "ap-northeast-2" # Please use the default region ID
}
module "aurora_postgresql" {
source = "../../../../modules/database/aurora-postgresql"
name = var.postgresql_cluster_name[terraform.workspace]
instance_class = var.postgresql_instance_class[terraform.workspace]
master_username = var.postgresql_master_username
master_password = var.postgresql_master_password
}
module에서 작성한 aurora_postgresql을 사용하기 위해 source는 module 위치로 지정해줍니다. 또한 backend을 init.tf
에서 만든 s3로 지정해줍니다.
outputs.tf
위 module 코드와 동일합니다.
variables.tf
variable "postgresql_cluster_name" {
description = "Name used across resources created"
type = string
}
variable "postgresql_master_username" {
description = "(Required) Username for the master DB user"
type = string
}
variable "postgresql_master_password" {
description = "(Required) Password for the master DB user"
type = string
}
variable "postgresql_instance_class" {
description = "instance_class"
type = string
default = "db.t4g.medium"
}
필요한 변수를 정의하는 곳입니다. terraform.tfvars 에서 해당 변수에 값을 주입합니다.
terraform.tfvars
postgresql_cluster_name = "develop"
postgresql_instance_class = "db.t4g.medium"
postgresql_master_password = "admin"
postgresql_master_password = "admin"
variables.tf
에서 선언한 변수들에 값을 주입합니다.
현재 테라폼 구조는 prod, stage, develop, master 환경마다 같은 코드를 반복 작성해야 합니다. 이는 서비스 추가시마다 성가신 일이기 때문에 다른 방법이 없는지 찾아보다 Terrafrom workspace 를 발견하였습니다.
Terraform workspace는 Terraform state를 담는 그릇으로 생각할 수 있습니다. workspace를 쓰게 되면 코드베이스를 하나만 관리해주면 됩니다. 명령어는 아래와 같이 사용하면 됩니다.
# 워크스페이스 생성
$ terraform workspace new prod
# 워크스페이스 선택
$ terraform workspace select prod
# Plan or Apply
$ terraform plan
$ terraform apply
├── applications # 실제 terraform apply 할 폴더
│ ├── backend
│ │ ├── database
│ │ │ ├── aurora-postgresql
│ │ │ │ ├── main.tf
│ │ │ │ ├── outputs.tf
│ │ │ │ ├── terraform.tfvars
│ │ └── └── └── variables.tf
│ └── frontend
├── modules
│ ├── database
│ │ ├── aurora-postgresql
│ │ │ ├── main.tf
│ │ │ ├── outputs.tf
│ │ │ ├── terraform.tfvars
│ │ └── └── variables.tf
└── ├── s3
├── init
└── └── init.tf
기존에 있던 prod, stage, master, develop 폴더를 없애고 application이라는 폴더로 통합관리를 하도록 하였습니다. 만약 aws aurora-postgresql를 만들고 싶으면 applications/backend/database/aurora-postgresql
폴더에 들어가서 아래의 명령어를 입력해주면 됩니다.
# develop workspace 생성
$ terraform workspace new aurora-db-develop
# develop workspace 선택
$ terraform workspace select aurora-db-develop
# Plan or Apply
$ terraform plan
$ terraform apply
해당 글을 참고하여 workspace의 이름을 결정하였습니다.
https://helloworld.kurly.com/blog/terraform-adventure/
https://techblog.woowahan.com/2646/
https://terraform101.inflearn.devopsart.dev/advanced/backend/
도움이 되셨다면 좋아요 부탁드립니다!