Programming/IaC

[IaC] State & 모듈

Hayley Shim 2023. 10. 28. 18:02

안녕하세요. 최근 도서 “테라폼으로 시작하는 IaC” 의 내용을 기준으로 스터디한 내용을 정리했습니다. 지난 [IaC] 기본 사용법( condition, function, provisioner, null_resource와 terraform_data, moved block, cli를 위한 시스템 환경 변수) & 프로바이더 글에 이어 해당 글에서는 State & 모듈에 대해 알아보겠습니다.

 

State

  1. State의 목적과 의미
  • Serial을 기준으로 State backup 관리
cat <<EOT > vpc.tf
provider "aws" {
  region  = "ap-northeast-2"
}

resource "aws_vpc" "myvpc" {
  cidr_block       = "10.10.0.0/16"

  tags = {
    Name = "t101-study"
  }
}
EOT


# 배포
terraform init && terraform plan && terraform apply -auto-approve

# 상태 파일 확인 : JSON 형식
ls
cat terraform.tfstate | jq | grep serial
...
"serial": 1,
...
  • 태그 수정 후 상태 파일 확인
at <<EOT > vpc.tf
provider "aws" {
  region  = "ap-northeast-2"
}

resource "aws_vpc" "myvpc" {
  cidr_block       = "10.10.0.0/16"

  tags = {
    Name = "tf-state"
  }
}
EOT

# 배포 : plan 시 tfstate 상태와 코드 내용을 비교해서 검토
terraform plan && terraform apply -auto-approve

# 상태 파일 비교 : 백업 파일 생성됨
ls terraform.tfstate*
terraform.tfstate        terraform.tfstate.backup

diff terraform.tfstate terraform.tfstate.backup
<   "serial": 3,
---
>   "serial": 1,
...
  • 한번 더 태그 수정 후 상태 파일 확인
cat <<EOT > vpc.tf
provider "aws" {
  region  = "ap-northeast-2"
}

resource "aws_vpc" "myvpc" {
  cidr_block       = "10.10.0.0/16"

  tags = {
    Name = "tf-state-tag-change"
  }
}
EOT


# 배포
terraform plan && terraform apply -auto-approve

# 상태 파일 비교(바로 직전 상태 백업)
ls terraform.tfstate*
terraform.tfstate        terraform.tfstate.backup

diff terraform.tfstate terraform.tfstate.backup
<   "serial": 5,
---
>   "serial": 3,
...

[참고 : hashicorp state]

  • Terraform은 관리형 인프라 및 구성에 대한 상태를 저장해야 함
  • 이 State는 Terraform에서 실제 리소스를 구성에 매핑하고, 메타데이터를 추적하고, 대규모 인프라의 성능을 향상시키는 데 사용됨

팀 단위에서 테라폼 운영 시 주의사항

기본적으로 “terraform.tfstate”라는 로컬 파일에 저장되지만 버전을 지정하고 암호화하고 팀과 안전하게 공유하려면 Terraform Cloud에 저장하는 것이 좋음

  1. 상태 파일을 저장하는 공유 스토리지 Shared storage for state files
  • 각 팀원이 동일한 테라폼 상태 파일 사용을 위해서, 공유 위치에 저장이 필요

2. 상태 파일 잠금 Locking state files

  • 잠금 기능 없이 두 팀원이 동시에 테라폼 실행 시 여러 테라폼 프로세스가 상태 파일을 동시에 업데이트하여 충돌 가능(경쟁 상태 race condition)

3. 상태 파일 격리 Isolating state files

  • 예를 들면 테스트 dev 와 검증 stage 과 상용 prodction 각 환경에 대한 격리가 필요

상태 파일 공유로 버전 관리 시스템 비추천

  1. 수동 오류 Manual error
  • 팀의 누군가가 이전 버전의 상태 파일로 테라폼을 실행하고, 그 결과 실수로 이전 버전으로 롤백하거나 이전에 배포된 인프라를 복제하는 문제가 발생 할 수 있음

2. 잠금 Locking

  • 대부분의 버전 관리 시스템(VCS)은 여러 명의 팀 구성원이 동시에 하나의 상태 파일에 terraform apply 명령을 실행하지 못하게 하는 잠금 기능이 제공되지 않음

3. 시크릿 Secrets

  • 테라폼 상태 파일의 모든 데이터는 평문으로 저장됨. 민감 정보가 노출될 위험

지원되는 원격 백엔드 : AWS S3, Azure Blob Storage, Google Cloud Storage, Consul, Postgres database 등 - 링크 → 위 제약사항에 대한 해결방안

  1. 수동 오류 해결 : plan/apply 실행 시 마다 해당 백엔드에서 파일을 자동을 로드, apply 후 상태 파일을 백엔드에 자동 저장
  2. 잠금(Lock) : apply 실행 시 테라폼은 자동으로 잠금을 활성화, -lock-timout=<TIME> 로 대기 시간 설정 지정 가능
  3. 시크릿 : 대부분 원격 백엔드는 기본적으로 데이터를 보내거나 상태 파일을 저장할 때 암호화(Encryption)하는 기능을 지원
  • 테라폼은 Stateful 애플리케이션. 프로비저닝 결과 State를 저장하고 추적에 활용
 
  • 개인 1인 : 로컬 환경으로 terraform.tfstate 파일에 JSON 형태로 저장
  • 팀이나 조직 : 공동 관리를 위해 원격 저장소에 저장해 공유 — 링크
  • State에는 작업자가 정의한 코드와 실제 반영된 프로비저닝 결과를 저장하고, 이 정보를 토대로 이후의 리소스 생성, 수정, 삭제에 대한 동작 판단 작업을 수행

State 역할

  • State에는 테라폼 구성과 실제를 동기화하고 각 리소스에 고유한 아이디(리소스 주소)로 맵핑
  • 리소스 종속성과 같은 메타데이터를 저장하고 추적
  • 테라폼 구성으로 프로비저닝 결과를 캐싱하는 역할을 수행
mkdir 5.1 && cd 5.1


#main.tf
resource "random_password" "mypw" {
  length           = 16
  special          = true
  override_special = "!#$%"
}


# 실행 : 랜덤 프로바이더는 테라폼 구성 내에서 무작위로 기입해야 되는 숫자, 패스워드, 문자열 등의 값을 생성하는 데 사용

# 
terraform init && terraform plan

#
terraform apply -auto-approve

# State List 및 생성된 Password 확인
terraform state list
terraform state show random_password.mypw

# State에 저장된 result를 확인해보자! -> 어떻게 관리해야할까요?
ls *.tfstate
cat terraform.tfstate | jq
cat terraform.tfstate | jq | grep result
YLGmKyb3jOuI8sEf

# (참고) sensitive value 내용은 테라폼 콘솔에서 보일까요? (정답: x)
echo "random_password.mypw" | terraform console
echo "random_password.mypw.result" | terraform console
  • 테라폼에서는 JSON 형태로 작성된 State를 통해 속성 인수를 읽고 확인할 수 있다.
  • 테라폼에서는 type name으로 고유한 리소스 분류하며, 해당 리소스의 속성 인수를 구성과 비교해 대상 리소스 생성, 수정, 삭제한다.
  • State는 테라폼만을 위한 API로 정의할 수도 있다.
  • Plan을 실행하면 암묵적으로 refresh 동작을 수행하면서 리소스 생성의 대상(클라우드 등)과 State를 기준으로 비교하는 과정을 거친다.
  • 이 작업은 프로비저닝 대상의 응답 속도와 기존 작성된 State의 리소스 양에 따라 속도 차이가 발생한다.
  • 대량의 리소스를 관리해야 하는 경우 Plan 명령에서 -refresh=false 플래그를 사용해 State를 기준으로 실행 계획을 생성하고, 이를 실행에 활용해 대상 환경과의 동기화 과정을 생략할 수 있다.
# 실행 계획 생성 시 저장되어 있는 State와 실제 형상을 비교하는 기본 실행
time terraform plan

# 실행 계획 생성 시 실제 형상과 비교하지 않고 실행 계획을 생성하는 -refresh=false 옵션
time terraform plan -refresh=false

2. State 동기화

  • 테라폼 구성 파일은 기존 State와 구성을 비교해 실행 계획에서 생성, 수정, 삭제 여부를 결정한다

3. 워크스페이스

환경을 격리하는 방법

  1. 작업 공간을 통한 격리 Isolation via workspaces
  • 동일한 구성에서 빠르고 격리된 테스트 환경에 유용
  • 논리적 격리 가능

2. 파일 레이아웃을 이용한 격리 Isolation via file layout

  • 보다 강력하게 분리해야 하는 운영 환경에 적합
  • 테라폼 프로젝트의 파일 레이아웃 설명
  • 각 테라폼 구성 파일을 분리된 폴더에 넣기. (예. stage , prod)
  • 각 환경에 서로 다른 백엔드 구성. (예. S3 버킷 백엔드의 AWS 계정을 분리)

워크스페이스 : state를 관리하는 논리적인 가상 공간  링크

  • 워크스페이스는 기본 default로 정의된다. 로컬 작업 환경의 워크스페이스 관리를 위한 CLI 명령어로 workspace가 있다.

terraform workspace list
* default

mkdir 5.3 && cd 5.3

# main.tf
resource "aws_instance" "mysrv1" {
  ami           = "ami-0ea4d4b8dc1e46212"
  instance_type = "t2.micro"
  tags = {
    Name = "t101-week4"
  }
}


# 실행
# [분할/터미널1] 모니터링
export AWS_PAGER=""
while true; do aws ec2 describe-instances --query "Reservations[*].Instances[*].{PublicIPAdd:PublicIpAddress,InstanceName:Tags[?Key=='Name']|[0].Value,Status:State.Name}" --filters Name=instance-state-name,Values=running --output text ; echo "------------------------------" ; sleep 1; done

# [분할/터미널2] 배포
terraform init && terraform apply -auto-approve
terraform state list

#
cat terraform.tfstate | jq -r '.resources[0].instances[0].attributes.public_ip'
cat terraform.tfstate | jq -r '.resources[0].instances[0].attributes.private_ip'

# terraform.tfstate에 private 담긴 내용은?
cat terraform.tfstate | jq -r '.resources[0].instances[0].private' | base64 -d | jq

# 워크스페이스 확인
terraform workspace list

# graph 확인
terraform graph > graph.dot


# 새 작업 공간 workspace 생성 : mywork1
terraform workspace new mywork1
terraform workspace show

# 서브 디렉터리 확인 -> 향후 저장되는 State는 여기에
tree terraform.tfstate.d
terraform.tfstate.d
└── mywork1


terraform plan

# apply
terraform apply -auto-approve


# 워크스페이스 확인
terraform workspace list

# State 확인
cat terraform.tfstate | jq -r '.resources[0].instances[0].attributes.public_ip'
cat terraform.tfstate.d/mywork1/terraform.tfstate | jq -r '.resources[0].instances[0].attributes.public_ip'

# graph 확인
terraform graph > graph.dot


# (실습생략)새 작업 공간 workspace 생성 : mywork2
terraform workspace new mywork2

# 서브 디렉터리 확인
tree terraform.tfstate.d
...

# plan & apply
terraform plan && terraform apply -auto-approve
cat terraform.tfstate | jq -r '.resources[0].instances[0].attributes.public_ip'
cat terraform.tfstate.d/mywork1/terraform.tfstate | jq -r '.resources[0].instances[0].attributes.public_ip'
cat terraform.tfstate.d/mywork2/terraform.tfstate | jq -r '.resources[0].instances[0].attributes.public_ip'

# workspace 정보 확인
terraform workspace show
terraform workspace list

# 실습 리소스 삭제
terraform workspace select default
terraform destroy -auto-approve
terraform workspace select mywork1
terraform destroy -auto-approve
terraform workspace select mywork2
terraform destroy -auto-approve

장점

  • 하나의 루트 모듈에서 다른 환경을 위한 리소스를 동일한 테라폼 구성으로 프로비저닝하고 관리
  • 기존 프로비저닝된 환경에 영향을 주지 않고 변경 사항 실험 가능
  • 깃의 브랜치 전략처럼 동일한 구성에서 서로 다른 리소스 결과 관리 — [참고 : 화해 — Git 브랜치 전략 수립을 위한 전문가의 조언들]

단점

  • State가 동일한 저장소(로컬 또는 백엔드)에 저장되어 State 접근 권한 관리가 불가능(어려움)
  • 모든 환경이 동일한 리소스를 요구하지 않을 수 있으므로 테라폼 구성에 분기 처리가 다수 발생 가능
  • 프로비저닝 대상에 대한 인증 요소를 완벽히 분리하기 어려움
  • → 가장 큰 단점은 완벽한 격리가 불가능
  • ⇒ 해결방안 1. 해결하기 위해 루트 모듈을 별도로 구성하는 디렉터리 기반의 레이아웃을 사용할 수 있다. ⇒ 해결방안 2. Terraform Cloud 환경의 워크스페이스를 활용

State 이관방법 — [참고 : 공식 Docs, Hashitalks 영상]

Module

module example
module example
  • 모듈은 테라폼 구성의 집합으로 테라폼으로 관리하는 대상의 규모가 커지고 복잡해져 생긴 문제를 보완하고 관리 작업을 수월하게 하기 위한 방안으로 활용한다.

모듈은 루트 모듈과 자식 모듈로 구분된다

  • 루트 모듈 Root Module : 테라폼을 실행하고 프로비저닝하는 최상위 모듈
  • 자식 모듈 Child Module : 루트 모듈의 구성에서 호출되는 외부 구성 집합
https://linuxhint.com/terraform-modules/
  1. 모듈 작성 기본 원칙
  • 모듈 디렉터리 형식을 terraform-<프로바이더 이름>-<모듈 이름> 형식을 제안한다. 이 형식은 Terraform Cloud, Terraform Enterprise에서도 사용되는 방식으로 1) 디렉터리 또는 레지스트리 이름이 테라폼을 위한 것이고, 2) 어떤 프로바이더의 리소스를 포함하고 있으며, 3) 부여된 이름이 무엇인지 판별할 수 있도록 한다.
  • 테라폼 구성은 궁극적으로 모듈화가 가능한 구조로 작성할 것을 제안한다. 처음부터 모듈화를 가정하고 구성파일을 작성하면 단일 루트 모듈이라도 후에 다른 모듈이 호출할 것을 예상하고 구조화할 수 있다. 또한 작성자는 의도한 리소스 묶음을 구상한 대로 논리적인 구조로 그룹화할 수 있다.
  • 각각의 모듈을 독립적으로 관리하기를 제안한다. 리모트 모듈을 사용하지 않더라도 처음부터 모듈화가 진행된 구성들은 때로 루트 모듈의 하위 파일 시스템에 존재하는 경우가 있다. 하위 모듈 또한 독립적인 모듈이므로 루트 모듈 하위에 두기보다는 동일한 파일 시스템 레벨에 위치하거나 별도 모듈만을 위한 공간에서 불러오는 것을 권장한다. 이렇게 하면 VCS를 통해 관리하기가 더 수월하다.
  • 공개된 테라폼 레지스트리의 모듈을 참고 하기를 제안한다. 대다수의 테라폼 모듈은 공개된 모듈이 존재하고 거의 모든 인수에 대한 변수 처리, 반복문 적용 리소스, 조건에 따른 리소스 활성/비활성 등을 모범 사례로 공개해두었다. 물론 그대로 가져다 사용하는 것보다는 프로비저닝하려는 상황에 맞게 참고하는 것을 권장한다.
  • 작성된 모듈은 공개 또는 비공개로 게시해 팀 또는 커뮤니티와 공유하기를 제안한다. 모듈의 사용성을 높이고 피드백을 통해 더 발전된 모듈을 구성할 수 있는 자극이 된다.

2. 모듈화 해보기

자식 모듈 작성

mkdir -p 06-module-traning/modules/terraform-random-pwgen

# main.tf
resource "random_pet" "name" {
  keepers = {
    ami_id = timestamp()
  }
}

# DB일 경우 Password 생성 규칙을 다르게 반영 
resource "random_password" "password" {
  length           = var.isDB ? 16 : 10
  special          = var.isDB ? true : false
  override_special = "!#$%*?"
}


# variable.tf
variable "isDB" {
  type        = bool
  default     = false
  description = "패스워드 대상의 DB 여부"
}

# output.tf
output "id" {
  value = random_pet.name.id
}

output "pw" {
  value = nonsensitive(random_password.password.result) 
}

자식 모듈 동작 테스트

#
cd 06-module-traning/modules/terraform-random-pwgen


#
ls *.tf
terraform init && terraform plan

# 테스트를 위해 apply 시 변수 지정
terraform apply -auto-approve -var=isDB=true
Apply complete! Resources: 2 added, 0 changed, 0 destroyed.
Outputs:
id = "knowing-aardvark"
pw = "Y5eeP0i2KLLE9gBa"

# 확인
terraform state list
terraform state show random_pet.name
terraform state show random_password.password

# tfstate에 모듈 정보 확인 -> 없음
cat terraform.tfstate | grep module

# graph 확인
terraform graph > graph.dot

자식 모듈 호출 실습

mkdir -p 06-module-traning/06-01-basic

module "mypw1" {
  source = "../modules/terraform-random-pwgen"
}

module "mypw2" {
  source = "../modules/terraform-random-pwgen"
  isDB   = true
}

output "mypw1" {
  value  = module.mypw1
}

output "mypw2" {
  value  = module.mypw2
}

3. 모듈 사용 방식

  • 모듈에서 사용되는 모든 리소스는 관련 프로바이더의 정의가 필요

유형 1. 자식 모듈에서 프로바이더 정의

  • 모듈에서 사용하는 프로바이더 버전과 구성 상세를 자식 모듈에서 고정하는 방법이다.
  • 프로바이더 버전과 구성에 민감하거나, 루트 모듈에서 프로바이더 정의 없이 자식 모듈이 독립적인 구조일 때 고려할 방법이다
  • 하지만 동일한 프로바이더가 루트와 자식 양쪽에 또는 서로 다른 자식 모듈에 버전 조건 합의가 안 되면, 오류가 발생하고 모듈에 반복문을 사용할 수 없다는 단점이 있으므로 잘 사용하지 않는다.

유형 2. 루트 모듈에서 프로바이더 정의(실습)

  • 자식 모듈 루트 모듈 프로바이더 구성에 종속되는 방식이다.
  • 디렉터리 구조로는 분리되어 있지만 테라폼 실행 단계에서 동일 계층으로 해석되므로 프로바이더 버전과 구성은 루트 모듈의 설정이 적용된다. 프로바이더를 모듈 내 리소스와 데이터 소스에 일괄 적용하고, 자식 모듈에 대한 반복문 사용에 자유로운 것이 장점이다. 자식 모듈에 특정 프로바이더 구성의 종속성은 반영할 수 없으므로 자식 모듈을 프로바이더 조건에 대해 기록하고, 자식 모듈을 사용하는 루트 모듈에서 정의하는 프로바이더에 맞게 업데이트 해야 한다.
  • 다음은 동일한 모듈에 사용되는 프로바이더 조건이 다른 경우 각 모듈별로 프로바이더를 맵핑하는 방안이다.
  • 리소스와 데이터 소스에 provider 메타인수로 지정하는 방식과 비슷하나 모듈에는 다수의 프로바이더가 사용될 가능성이 있으므로 map 타입으로 구성하는 provider로 정의한다. 실습을 위한 디렉터리 구성의 예는 다음과 같다.

디렉터리 생성 및 06-module-traning/modules/terraform-aws-ec2/main.tf variable.tf output.tf 파일 생성

mkdir -p 06-module-traning/modules/terraform-aws-ec2/

# main.tf
terraform {
  required_providers {
    aws = {
      source = "hashicorp/aws"
    }
  }
}

resource "aws_default_vpc" "default" {}

data "aws_ami" "default" {
  most_recent = true
  owners      = ["amazon"]

  filter {
    name   = "owner-alias"
    values = ["amazon"]
  }

  filter {
    name   = "name"
    values = ["amzn2-ami-hvm*"]
  }
}

resource "aws_instance" "default" {
  depends_on    = [aws_default_vpc.default]
  ami           = data.aws_ami.default.id
  instance_type = var.instance_type

  tags = {
    Name = var.instance_name
  }
}


# variable.tf
variable "instance_type" {
  description = "vm 인스턴스 타입 정의"
  default     = "t2.micro"
}

variable "instance_name" {
  description = "vm 인스턴스 이름 정의"
  default     = "my_ec2"
}


# output.tf
output "private_ip" {
  value = aws_instance.default.private_ip
}

작성된 모듈을 사용할 루트 모듈 디렉터리 생성 및 06-module-traning/multi_provider_for_module/main.tf output.tf 파일 생성

mkdir -p 06-module-traning/multi_provider_for_module/


# main.tf
provider "aws" {
  region = "ap-southeast-1"  
}

provider "aws" {
  alias  = "seoul"
  region = "ap-northeast-2"  
}

module "ec2_singapore" {
  source = "../modules/terraform-aws-ec2"
}

module "ec2_seoul" {
  source = "../modules/terraform-aws-ec2"
  providers = {
    aws = aws.seoul
  }
  instance_type = "t3.small"
}


# output.tf
output "module_output_singapore" {
  value = module.ec2_singapore.private_ip
}

output "module_output_seoul" {
  value = module.ec2_seoul.private_ip
}

실행 : 프로바이더 구성을 테스트

#
cd 06-module-traning/multi_provider_for_module/
terraform init
cat .terraform/modules/modules.json | jq

#
terraform apply -auto-approve
terraform output
terraform state list
terraform state show module.ec2_seoul.data.aws_ami.default
terraform state show module.ec2_singapore.data.aws_ami.default
cat terraform.tfstate | grep module

# graph 확인
terraform graph > graph.dot

# aws cli로 ec2 확인
aws ec2 describe-instances --region ap-northeast-2 --query "Reservations[*].Instances[*].{PublicIPAdd:PublicIpAddress,InstanceName:Tags[?Key=='Name']|[0].Value,Status:State.Name}" --filters Name=instance-state-name,Values=running --output text
aws ec2 describe-instances --region ap-southeast-1 --query "Reservations[*].Instances[*].{PublicIPAdd:PublicIpAddress,InstanceName:Tags[?Key=='Name']|[0].Value,Status:State.Name}" --filters Name=instance-state-name,Values=running --output text

# 실습 완료 후 리소스 삭제
terraform destroy -auto-approve
  • 자식 모듈에서 필요로 하는 프로바이더의 버전이 루트 모듈의 정의와 다른 경우, 테라폼 구성에서 정의한 내용이 서로 호환되지 않아 오류가 발생할 수 있다.

모듈 반복문

디렉터리 생성 및 06-module-traning/module_loop_count/main.tf 파일 생성

mkdir -p 06-module-traning/module_loop_count/

provider "aws" {
  region = "ap-northeast-2"  
}

module "ec2_seoul" {
  count  = 2
  source = "../modules/terraform-aws-ec2"
  instance_type = "t3.small"
}

output "module_output" {
  value  = module.ec2_seoul[*].private_ip   
}

실행 : 모듈의 반복문 테스트

#
cd 06-module-traning/module_loop_count/
terraform init
cat .terraform/modules/modules.json | jq

#
terraform apply -auto-approve
terraform output
terraform state list
cat terraform.tfstate | grep module

# graph 확인
terraform graph > graph.dot

# aws cli로 ec2 확인
aws ec2 describe-instances --region ap-northeast-2 --query "Reservations[*].Instances[*].{PublicIPAdd:PublicIpAddress,InstanceName:Tags[?Key=='Name']|[0].Value,Status:State.Name}" --filters Name=instance-state-name,Values=running --output text

# 실습 완료 후 리소스 삭제
terraform destroy -auto-approve
  • 모듈 묶음에 일관된 구성과 구조로 프로비저닝이 되는 경우라면 count가 간편한 방안이지만, 동일한 모듈 구성에 필요한 인수 값이 다르다면 for_each를 활용한다.

06-module-traning/module_loop_count/main.tf 파일 수정

locals {
  env = {
    dev = {
      type = "t3.micro"
      name = "dev_ec2"
    }
    prod = {
      type = "t3.medium"
      name = "prod_ec2"
    }
  }
}

module "ec2_seoul" {
  for_each = local.env
  source = "../modules/terraform-aws-ec2"
  instance_type = each.value.type
  instance_name = each.value.name
}

output "module_output" {
  value  = [
    for k in module.ec2_seoul: k.private_ip
  ]
}

실행 : 모듈의 반복문 테스트

#
terraform plan
terraform apply -auto-approve
terraform output
terraform state list
cat terraform.tfstate | grep module

# graph 확인
terraform graph > graph.dot

# aws cli로 ec2 확인
aws ec2 describe-instances --region ap-northeast-2 --query "Reservations[*].Instances[*].{PublicIPAdd:PublicIpAddress,InstanceName:Tags[?Key=='Name']|[0].Value,Status:State.Name}" --filters Name=instance-state-name,Values=running --output text

# 실습 완료 후 리소스 삭제
terraform destroy -auto-approve

4. 모듈 소스 관리

모듈 소스 관리

Module 블록에 정의된 소스 구성으로 모듈의 코드 위치를 정의한다. terraform init을 수행할 때 지정된 모듈을 다운로드해 사용한다 — 링크

테라폼 공식 문서 안내와 같이 루트 경로에 모듈을 배치하는 것 외에 패키지 하위 디렉터리 경로를 참조하는 것도 하나의 방안이다. [참고 — Modules in Package Sub-directories]

로컬 디렉터리 경로

  • 로컬 경로를 지정할 때는 테라폼 레지스트리와 구분하기 위해 하위 디렉터리는 ./로, 상위 디렉터리는 ../로 시작한다.
  • 대상 모듈은 이미 같은 로컬 파일 시스템에 존재하므로 다운로드 없이 바로 사용한다. 앞서 언급한 대로 재사용성이 고려된다면 상위 디렉터리에 별도 관리하는 것을 권장하고, 항상 루트 모듈과 함께 동작해야 하는 경우 하위 디렉터리에 모듈을 정의한다.
  • 코드 예시) 작업 환경을 기준으로 하는 로컬 경로 상위에 위치한 모듈 경로 선언
module "local_module" {
  source = "../modules/my_local_module"
}

테라폼 레지스트리

  • 테라폼 모듈 레지스트리는 테라폼의 프로토콜을 사용해 모듈을 사용하는 방식이다.
  • 공개된 테라폼 모듈을 사용하거나 Terraform Cloud, Terraform Enterprise에서 제공되는 비공개(Private) 테라폼 모듈을 사용할 때 설정하는 소스 지정 방식이다.
  • 공개된 모듈 — 링크 aws_vpc
  • 공개된 테라폼 모듈을 source에 선언할 때는 <네임스페이스>/<이름>/<프로바이더> 형태로 설정한다.
module "vpc" {
  source  = "terraform-aws-modules/vpc/aws"
  version = "5.1.0"
}

aws vpc 모듈 실습 — aws_vpc Github SimpleVPC

# 모듈 다운로드
#
git clone https://github.com/terraform-aws-modules/terraform-aws-vpc/
tree terraform-aws-vpc/examples -L 1
tree terraform-aws-vpc/examples -L 2
cd terraform-aws-vpc/examples/simple
ls *.tf
cat main.tf

# 코드 수정 : 서울 리전 변경, main.tf 파일 내용 확인

# 서울 리전 변경
grep 'eu-west-1' main.tf
sed -i -e 's/eu-west-1/ap-northeast-2/g' main.tf

# VPC CIDR 변경
grep '10.0.0.0' main.tf
sed -i -e 's/10.0.0.0/10.10.0.0/g' main.tf

# main.tf 파일 내용 확인
cat main.tf
...
module "vpc" {
  source = "../../"

  name = local.name
  cidr = local.vpc_cidr

  azs             = local.azs
  private_subnets = [for k, v in local.azs : cidrsubnet(local.vpc_cidr, 4, k)]

  tags = local.tags
}

# 모듈 확인
tree ../../modules

# 실행
#
terraform init
cat .terraform/modules/modules.json| jq

# 생성된 리소스들 확인해보자!
terraform apply -auto-approve
terraform output
terraform state list

# 실습 완료 후 리소스 삭제
terraform destroy -auto-approve

깃허브

  • 깃의 원격 저장소로 널리 알려진 깃허브는 테라폼 구성에 대한 CI 용도로 사용할 수 있고, 저장된 구성을 테라폼 모듈의 소스로 선언할 수도 있다

[방법]

  1. 깃허브에 로그인
  2. 새로운 깃허브 저장소 생성 [New repository]
  • Owner : 원하는 소유자 선택
  • Repository name : 예시) terraform-module-repo
  • Public 선택
  • Add .gitignore의 드롭박스에서 [Terraform]을 선택

3. 맨 아래 [Create repository] 클릭

4. 해당 저장소에 예시) ‘terraform-aws-ec2’ 디렉터리 생성 후 main.tf , variable.tf, output.tf 추가 후 업로드

# 디렉터리 생성 후 main.tf 파일 생성
mkdir module-source-mygithub

# main.tf
provider "aws" {
  region = "ap-southeast-1"  
}

module "ec2_seoul" {
  source = "github.com/<Owner Name>/terraform-module-repo/terraform-aws-ec2"
  instance_type = "t3.small"
}


# 실행
#
cd module-source-mygithub
terraform init

# 아래 디렉터리에 깃허브에 있던 파일이 다운로드 되어 있음을 확인
tree .terraform/modules

# 배포
terraform apply -auto-approve
terraform state list

# 실습 완료 후 삭제
terraform destroy -auto-approve