Terraform Study - w04

wdb·2023년 7월 29일

01. 원격저장소

소스코드
상태 파일(.tfstate)은 테라폼 코드가 실제 인프라에 적용된 결과를 정하는 파일로 배포 시마다 변경됩니다. 상태 파일은 테라폼 내부에서 private API로 사용됩니다.
만약 복수의 팀원이 하나의 인프라 환경을 테라폼으로 배포한다면 어떻게 될까요? 팀원들은 각각의 상태 파일을 가지고 있을 것입니다. 테라폼은 테라폼 상태 파일과 실제 인프라를 비교하여 테라폼 코드를 적용하는데, 각자 다른 상태 파일을 사용하여 배포하였다면, 각 팀원들의 테라폼 코드와 실제 인프라 사이의 괴리가 생겨 테라폼은 정상적으로 작동하지 못합니다.
이런 문제를 방지하기 위해 복수의 팀원은 하나의 상태 파일을 공유해야 합니다. 따라서 복수의 인원이 작업할 때는 상태 파일을 저장하기 위한 원격저장소가 필요합니다. AWS 인프라를 테라폼으로 배포할 때 많이 사용되는 원격저장소는 AWS S3입니다.

# backend.tf

resource "aws_s3_bucket" "s3_backend_bucket" {
  bucket = "${var.prefix}-t101study-tfstate"
}

resource "aws_s3_bucket_versioning" "s3_backend_bucket_versioning" {
  bucket = aws_s3_bucket.s3_backend_bucket.id
  versioning_configuration {
    status = "Enabled"
  }
}

output "s3_bucket_arn" {
  value       = aws_s3_bucket.s3_backend_bucket.arn
  description = "The ARN of the S3 bucket"
}

resource "aws_dynamodb_table" "ddb_backend_table" {
  name         = "terraform-locks"
  billing_mode = "PAY_PER_REQUEST"
  hash_key     = "LockID"

  attribute {
    name = "LockID"
    type = "S"
  }
}

output "dynamodb_table_name" {
  value       = aws_dynamodb_table.ddb_backend_table.name
  description = "The name of the DynamoDB table"
}

테라폼 저장소를 만들 땐 상태 파일의 버전 관리를 위해 버전 관리를 활성화해줍니다. 인프라를 과거의 상태로 되돌려야 할 때, 이전 버전의 상태 파일을 테라폼에 임포트하여 되돌릴 수 있습니다. LockID를 파티션 키로 사용하는 DynamoDB는 복수의 작업자가 동시에 하나의 상태 파일을 업데이트하지 못하도록 상태를 잠그는 역할을 합니다.

# provider.tf

terraform {
  required_version = "~> 1.5.2"
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = ">= 5.0, <=5.6"
    }
  }

  backend "s3" {
    bucket         = "wdb-t101study-tfstate"
    key            = "w04_01/terraform.tfstate"
    region         = "ap-northeast-2"
    dynamodb_table = "terraform-locks"
  }
}

provider "aws" {
  region  = var.region
  profile = var.account_profile
}

백엔드는 provider.tf에 설정합니다. 백엔드로 사용할 버킷명, 버킷 안에서 상태 파일이 저장될 경로, 버킷의 리전, 상태 파일 잠금을 위해 사용할 DynamoDB의 테이블명을 명시해주면 됩니다.

terraform apply 명령이 실행되면 설정한 경로에 상태 파일이 설정되어 있는 것을 확인할 수 있습니다.

02. 모듈화

소스코드
테라폼 모듈은 테라폼 코드를 하나의 패키지로 만든 형태입니다. 특정 리소스를 배포하는 코드를 모듈화하면 재사용 및 공유가 용이해집니다. IaC의 장점을 극대화할 수 있는 형태이기 때문에, 테라폼 구성은 모듈화가 가능한 형태로 작성할 것을 권장하고 있습니다.
인프라를 배포할 때 보안그룹은 일반적으로 여러개가 필요하고, 보안그룹의 설정들 역시 리스트 형태의 값들이 들어가야 하는 경우가 많습니다. 보안그룹 배포 코드를 모듈화하여 재사용성을 높여보았습니다.
폴더 구조는 아래와 같습니다.

modules
└── terraform-aws-sg
	├── main.tf
    ├── output.tf
    └── variable.tf
└── terraform-aws-sg-ingress
    ├── main.tf
    ├── output.tf
    └── variable.tf
├── main.tf
├── provider.tf
├── terraform.tfvars
└── variable.tf

보안그룹을 생성하는 코드입니다.

# terraform-aws-sg/main.tf

resource "aws_security_group" "sg" {
  name        = var.name
  description = var.description
  vpc_id      = var.vpc_id
  
  tags = {
    Name = var.name
  }
}

보안그룹의 id는 보안그룹의 인바운드 규칙을 생성될 때 다른 모듈에서 사용될 것이기 때문에 output의 value로 설정해줍니다.

# terraform-aws-sg/output.tf

output id {
 value = aws_security_group.sg.id 
}

보안그룹의 인바운드 규칙은 보통 여러 개 사용될뿐더러 규칙마다의 설정이 제각각이기 때문에 변수로 분리하더도 복잡해지는 경우가 많습니다. 보안그룹의 옵션 중 인바운드 규칙만 별도로 모듈화하여 보다 직관적으로 설정할 수 있도록 하였습니다.

# terraform-aws-sg-ingress/output.tf

resource "aws_vpc_security_group_ingress_rule" "sg_ingress_rules" {
  security_group_id = var.sg_id
  from_port         = var.from_port
  to_port           = var.to_port
  ip_protocol       = var.ip_protocol
  cidr_ipv4         = var.cidr_ipv4
}

이제 모듈을 이용하여 보안그룹을 배포해보겠습니다.
보안그룹 배포에 사용될 변수는 아래와 같습니다.

# variable.tf

...
variable "sg_def" {}
variable "sg_ingress_web" {}
variable "sg_ingress_db" {}

보안그룹의 개략적인 설정을 정의한 sg_def, 웹의 인바운드 규칙인 sg_ingress_web, DB의 인바운드 규칙인 sg_ingress_db 모두 for_each를 사용하여 반복적으로 배포하기 위해 map 형태의 데이터를 대입합니다. 인바운드 규칙의 경우 필요한 속성이 다른 경우가 많아 반복문을 통해 배포하게 되면 불편함이 따를 수 있어 용도 별로 변수를 만들었습니다.
tfavars 파일에 필요한 변수 값을 넣어주었습니다.

terraform.tfvars

...
###########################################
################### SG ####################
###########################################

sg_def = {
  web = {
    name        = "web-sg"
    description = "sg for web server"
  },
  db = {
    name        = "db-sg"
    description = "sg for db"
  }
}

sg_ingress_web = {
  http = {
    description = "http"
    from_port   = 80
    to_port     = 80
    ip_protocol = "tcp"
  }
  ssh = {
    description = "ssh"
    from_port   = 22
    to_port     = 22
    ip_protocol = "tcp"
  }
}

sg_ingress_db = {
  sql = {
    description = "sql"
    from_port   = 3306
    to_port     = 3306
    ip_protocol = "tcp"
  }
}

보안그룹을 배포하는 코드입니다. 보안그룹은 VPC에 속하는 리소스이므로, VPC를 먼저 만들어주었습니다.

main.tf

resource "aws_vpc" "vpc_test" {
  cidr_block           = "10.10.0.0/16"
  enable_dns_support   = true
  enable_dns_hostnames = true

  tags = {
    Name = "${var.prefix}-vpc"
  }
}

module "sg" {
  source = "./modules/terraform-aws-sg"

  for_each    = var.sg_def
  name        = "${var.prefix}-${each.value.name}"
  description = each.value.description
  vpc_id      = aws_vpc.vpc_test.id
}

module "sg_ingress_rule_web" {
  depends_on = [module.sg]
  source     = "./modules/terraform-aws-sg-ingress"

  for_each    = var.sg_ingress_web
  sg_id       = module.sg["web"].id
  from_port   = each.value.from_port
  to_port     = each.value.to_port
  ip_protocol = each.value.ip_protocol
}

module "sg_ingress_rule_db" {
  depends_on = [module.sg]
  source     = "./modules/terraform-aws-sg-ingress"

  for_each    = var.sg_ingress_db
  sg_id       = module.sg["db"].id
  from_port   = each.value.from_port
  to_port     = each.value.to_port
  ip_protocol = each.value.ip_protocol
}

for_each를 통해 필요한 보안그룹을 먼저 생성하고, 각각의 인바운드 규칙 리소스를 생성한 보안그룹의 id와 연결해주는 형태입니다.

03. 공개된 모듈

소스코드

https://registry.terraform.io/browse/modules

테라폼 레지스트리에는 각 유즈케이스 별로 공개된 모듈들이 존재합니다. 테라폼 레지스트리의 공개 모듈은 테라폼에서 제시하는 best practice일뿐만 아니라, 테라폼 코드에서 임포트하는 방식 역시 용이합니다.

IAM 유저를 생성하는 모듈을 임포트하여 리소스를 배포해보았습니다. 이 모듈은 기본적으로 4개의 IAM 유저를 생성합니다.

https://registry.terraform.io/modules/terraform-aws-modules/iam/aws/latest/examples/iam-user

테라폼의 임포트하는 방식은 해당 모듈의 페이지에서 안내하고 있습니다.

Provision Instructions의 코드를 복사하여 테라폼 코드에 붙여넣으면 해당 모듈을 바로 사용할 수 있습니다. terraform apply 명령을 했을 때 곧바로 리소스가 생성되는 것을 확인할 수 있었습니다.

04. 모듈 배포

소스코드
테라폼에서 제공하는 공개 모듈 뿐만 아니라, github에 개인이 업로드한 모듈 역시 테라폼에서 임포트하여 사용할 수 있습니다. 이전 단계에서 작성했던 보안그룹 모듈을 불러와보았습니다.

# main.tf

resource "aws_vpc" "vpc_test" {
  cidr_block           = "10.10.0.0/16"
  enable_dns_support   = true
  enable_dns_hostnames = true

  tags = {
    Name = "${var.prefix}-vpc"
  }
}

module "sg" {
  source =  "github.com/widalida26/terraform-study/week_04/02_modulize/modules/terraform-aws-sg"

  for_each    = var.sg_def
  name        = "${var.prefix}-${each.value.name}"
  description = each.value.description
  vpc_id      = aws_vpc.vpc_test.id
}

source에 로컬 경로를 넣는 대신 github 경로를 넣으면 업로드한 모듈을 임포트하여 리소스를 배포할 수 있습니다.

0개의 댓글