Module
- Terraform을 사용하여 인프라와 서비스를 관리하는 과정에서 시간이 지남에 따라 구성이 복잡해지고 관리하는 리소스가 증가하게 됩니다.
- Terraform의 구성 파일과 디렉터리 구조에는 제약이 없어 단일 파일 구조에서 지속적으로 업데이트할 수 있지만, 다음과 같은 문제들이 발생할 수 있습니다
- 원하는 항목을 찾고 수정하는 것이 어려워짐
- 리소스 간의 연관 관계가 복잡해짐에 따라 변경 작업의 영향도를 분석하기 위한 노력이 증가함
- 개발, 스테이징, 프로덕션 환경으로 구분된 경우 비슷한 형태의 구성이 반복되어 업무 효율이 저하됨
- 새로운 프로젝트 구성 시 기존 구성에서 필요한 리소스 구성과 종속성 파악이 어려움
모듈의 개념
- 모듈은 Terraform 구성의 집합으로, 규모가 커지고 복잡해진 구성을 관리하기 위한 방안입니다. 모듈은 크게 루트 모듈과 자식 모듈로 구분됩니다
- 루트 모듈 (Root Module) : Terraform을 실행하고 프로비저닝하는 최상위 모듈
- 자식 모듈 (Child Module) : 루트 모듈의 구성에서 호출되는 외부 구성 집합
모듈의 장점
- 모듈을 사용하면 다음과 같은 장점을 제공합니다
- 관리성 : 모듈은 연관된 구성의 묶음으로, 원하는 구성요소를 단위별로 쉽게 찾고 업데이트할 수 있습니다. 모듈은 다른 구성에서 쉽게 추가하거나 삭제할 수 있으며, 모듈이 업데이트되면 이를 사용하는 모든 구성에서 일관된 변경 작업을 진행할 수 있습니다.
- 캡슐화 : 각 모듈은 논리적으로 묶여져 독립적으로 프로비저닝 및 관리되며, 그 결과는 은닉성을 갖춰 필요한 항목만을 외부에 노출시킵니다.
- 재사용성 : 모듈화된 구성은 이후 비슷한 프로비저닝에 이미 검증된 구성을 바로 사용할 수 있어 처음부터 작성하는 시간과 노력을 줄일 수 있습니다.
- 일관성과 표준화 : 모듈을 활용한 구성은 일관성을 제공하며, 서로 다른 환경과 프로젝트에도 이미 검증된 모듈을 적용해 복잡한 구성과 보안 사고를 방지할 수 있습니다.
6.1 모듈 작성 기본 원칙
- 모듈은 대부분의 프로그래밍 언어에서 사용되는 라이브러리나 패키지와 유사한 역할을 합니다. 다음과 같은 기본 작성 원칙을 제안합니다.
모듈 작성 기본 원칙
- 모듈 디렉터리 형식
- terraform-<프로바이더 이름>-<모듈 이름> 형식을 사용합니다.
- 이 형식은 Terraform Cloud, Terraform Enterprise에서도 사용되며, 디렉터리 또는 레지스트리 이름이 Terraform을 위한 것이고, 어떤 프로바이더의 리소스를 포함하며, 부여된 이름을 판별할 수 있게 합니다.
- 모듈화 가능한 구조로 작성
- 처음부터 모듈화를 염두에 두고 구성 파일을 작성합니다.
- 단일 루트 모듈이라도 후에 다른 모듈이 호출할 것을 예상하고 구조화합니다.
- 리소스 묶음을 논리적인 구조로 그룹화합니다.
- 모듈 독립적으로 관리
- 리모트 모듈을 사용하지 않더라도 모듈화된 구성은 루트 모듈의 하위 파일 시스템에 존재하지 않도록 합니다.
- 독립적인 모듈은 동일한 파일 시스템 레벨에 위치하거나 별도 모듈만을 위한 공간에서 불러오는 것을 권장합니다.
- VCS를 통해 관리하기 더 수월합니다.
- 공개된 테라폼 레지스트리 모듈 참고
- 대부분의 Terraform 모듈은 공개되어 있으며, 변수 처리, 반복문 적용 리소스, 조건에 따른 리소스 활성/비활성 등을 모범 사례로 제공하고 있습니다.
- 그대로 사용하는 것보다는 상황에 맞게 참고하여 사용합니다.
- 작성된 모듈 공유
- 작성된 모듈을 공개 또는 비공개로 게시하여 팀이나 커뮤니티와 공유합니다.
- 모듈의 사용성을 높이고 피드백을 통해 발전된 모듈을 구성할 수 있습니다.
모듈 관리 디렉터리 구조
- 모듈을 독립적으로 관리하기 위해 디렉터리 구조를 생성할 때 모듈을 위한 별도 공간을 생성하는 방식을 사용합니다.
- 특정 루트 모듈 하위에 자식 모듈을 구성하는 경우 단순히 복잡한 코드를 분리하는 용도로 명시되며 종속성이 발생합니다. 따라서 루트 모듈 사이에 모듈 디렉터리를 지정합니다.
6.2 모듈화 해보기
모듈화 소개
- 모듈의 기본 구조
- Terraform 모듈은 기본적으로 입력 변수를 받아 결과를 출력하는 구조로 구성됩니다.
- 입력 변수: 모듈에 필요한 값들을 설정하는 부분
- 출력 값: 모듈이 생성한 결과를 외부에 제공하는 부분
- 모듈화의 개념
- 모듈화는 이 구조를 재활용하기 위한 템플릿 작업을 의미합니다.
- 모듈화를 통해 작성된 모듈을 다른 루트 모듈에서 가져다 사용할 수 있으며, 이를 통해 재사용성과 표준화를 구축할 수 있습니다.
- 모듈의 사용 방식
- 기존에 작성된 모듈은 다른 모듈에서 참조하여 사용할 수 있습니다. 이는 리소스를 사용하는 방식과 유사합니다.
- 모듈에서 필요한 값은 variable로 선언하여 설정하고, 모듈에서 생성된 값 중 외부 모듈에서 참조하고 싶은 값은 output으로 설정합니다. 이는 Java 개발 시 getter, setter로 캡슐화된 클래스를 활용하는 것과 비슷합니다.
모듈 작성 실습
- 하나의 프로비저닝에서 사용자와 패스워드를 여러 번 구성해야 하는 경우를 가상의 시나리오로 삼아 모듈화를 진행합니다.
- random_pet을 사용해 이름을 자동으로 생성하고, random_password를 사용해 사용자의 패스워드를 설정합니다.
- random_password는 random 프로바이더 리소스로 난수 형태의 패스워드를 생성할 수 있습니다.
자식 모듈 작성
- 디렉터리 생성 및 파일 생성
mkdir -p 06-module-traning/modules/terraform-random-pwgen
cd 06-module-traning/modules/terraform-random-pwgen
touch main.tf variable.tf output.tf
- main.tf 파일 작성
resource "random_pet" "name" {
keepers = {
ami_id = timestamp()
}
}
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)
}
- 자식 모듈 동작 테스트
# 파일 확인 및 초기화
ls *.tf
terraform init && terraform plan
# 테스트를 위해 apply 시 변수 지정
terraform apply -auto-approve -var=isDB=true
# 리소스 목록 출력
terraform state list
# 특정 리소스의 상세 정보 출력
terraform state show random_pet.name
# 콘솔에서 특정 리소스의 속성 값을 조회
echo "random_pet.name.id" | terraform console
echo "random_pet.name.keepers" | terraform console
# 특정 리소스의 상세 정보 출력
terraform state show random_password.password
# 콘솔에서 특정 리소스의 속성 값을 조회
echo "random_password.password.length" | terraform console
echo "random_password.password.special" | terraform console
# state 파일에서 특정 정보를 추출
cat terraform.tfstate | grep result
cat terraform.tfstate | grep module
# Graph 확인
terraform graph > graph.dot
자식 모듈 호출 실습
- 다수의 리소스를 동일한 목적으로 여러 번 사용해야 하는 경우, 각 리소스를 개별적으로 정의하고 고유한 이름을 지정해야 하는 번거로움이 있습니다.
- 하지만 모듈을 활용하면 이러한 반복되는 리소스 묶음을 최소화할 수 있습니다.
- 디렉터리 생성 및 파일 생성
mkdir -p 06-module-traning/06-01-basic
cd 06-module-traning/06-01-basic
touch main.tf
- main.tf 파일 작성
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
}
- 실행 및 출력 확인
# 초기화 및 적용
terraform init && terraform plan && terraform apply -auto-approve
# 리소스 목록 출력
terraform state list
# state 파일에서 특정 정보를 추출 : module
cat terraform.tfstate | grep module
# 디렉터리 구조를 트리 형식으로 출력 : modules.json 파일 확인
tree .terraform
# 모듈로 묶여진 리소스는 module 정의를 통해 쉽게 재활용하고 반복 사용할 수 있다.
# 모듈의 결과는 module.<모듈 이름>.<output 이름> 형식으로 참조하여 사용할 수 있다.
cat .terraform/modules/modules.json | jq
# Graph 확인
terraform graph > graph.dot
6.3 모듈 사용 방식
모듈과 프로바이더
- 모듈에서 사용되는 모든 리소스는 관련 프로바이더의 정의를 필요로 합니다. 여기서 프로바이더를 어디(자식 or 루트)에 정의할지에 대한 고민이 필요합니다.
[유형 1] 자식 모듈에서 프로바이더 정의
- 프로바이더 구성을 자식 모듈에서 직접 정의하는 방식입니다.
- 프로바이더 구성에 민감한 경우나 자식 모듈이 독립적인 구조를 가질 때 유용합니다.
- 그러나 루트 모듈과 자식 모듈 사이 혹은 서로 다른 자식 모듈 간의 프로바이더 버전이 일치하지 않으면 오류가 발생할 수 있고 반복문을 사용할 수 없다는 단점 때문에 이 방식은 일반적으로 권장되지 않습니다.
[유형 2] 루트 모듈에서 프로바이더 정의
- 루트 모듈에서 정의된 프로바이더 구성이 자식 모듈에게도 적용되는 방식입니다.
- 디렉터리가 분리되어 있어도 테라폼 실행 시 모든 모듈이 동일 계층으로 해석되므로 루트 모듈의 프로바이더 설정이 자식 모듈에도 적용됩니다.
- 이 구조는 리소스와 데이터 소스에 일관된 프로바이더를 적용하며, 자식 모듈을 반복적으로 사용할 때 유연성을 제공합니다.
- 자식 모듈은 특정 프로바이더 구성에 종속될 수 없기 때문에 자식 모듈의 프로바이더 요구사항을 문서화하고 루트 모듈에서 정의된 프로바이더 설정에 맞춰 자식 모듈을 업데이트해야 합니다.
- 동일한 모듈에 다양한 프로바이더 조건을 적용할 필요가 있을 때 각 모듈별로 프로바이더를 맵핑하는 방식을 사용할 수 있습니다.
- 리소스와 데이터 소스에 provider 메타인수를 지정하는 기존 방식과 비슷하지만 모듈에 여러 프로바이더가 사용될 가능성을 고려하여 map 타입으로 provider 를 정의합니다.
- 디렉터리 생성 및 파일 생성
mkdir -p 06-module-traning/modules/terraform-aws-ec2/
cd 06-module-traning/modules/terraform-aws-ec2/
touch main.tf variable.tf output.tf
- 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
}
- 루트 모듈 디렉터리 생성 및 파일 생성
mkdir -p 06-module-traning/multi_provider_for_module/
cd 06-module-traning/multi_provider_for_module/
touch main.tf output.tf
- 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
}
- 실행 및 확인
# 초기화
terraform init
# 모듈의 세부 정보 출력
cat .terraform/modules/modules.json | jq
# 리소스 배포
terraform apply -auto-approve
# output.tf 파일에 정의된 모든 출력 변수의 값 출력
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
# state 파일에서 특정 정보를 추출 : module
cat terraform.tfstate | grep module
# Graph 확인
terraform graph > graph.dot
# 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
- 자식 모듈이 요구하는 프로바이더 버전이 루트 모듈에서 지정한 버전과 다를 경우, 호환성 문제로 오류가 발생할 수 있습니다.
모듈의 반복문
- 모듈도 리소스처럼 반복문을 사용하여 구성할 수 있습니다.
- 반복문을 사용함으로써 원하는 수량만큼 리소스 정의 묶음인 모듈을 프로비저닝할 수 있으며, 이는 모듈을 사용하지 않는 구성에 비해 리소스 종속성 관리와 유지 보수에서 큰 장점을 제공합니다.
- 리소스에서 사용하는 방식과 유사하게 모듈 블록 내에서 count를 선언하여 반복문을 사용합니다.
- 디렉터리 생성 및 파일 생성
mkdir -p 06-module-traning/module_loop_count/
cd 06-module-traning/module_loop_count/
touch main.tf
- main.tf 파일 작성
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
}
- 실행 및 확인
# 초기화
terraform init
# 모듈의 세부 정보 출력
cat .terraform/modules/modules.json | jq
# 리소스 배포
terraform apply -auto-approve
# output.tf 파일에 정의된 모든 출력 변수의 값 출력
terraform output
# 리소스 목록 출력
terraform state list
# state 파일에서 특정 정보를 추출 : module
cat terraform.tfstate | grep module
# Graph 확인
terraform graph > graph.dot
# 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를 사용하는 것이 더 적합합니다.
- 예를 들어, 동일한 모듈을 사용하여 개발 환경과 상용 환경에 대한 입력 변수를 다르게 처리하는 실습을 통해 이 개념을 더 깊이 이해할 수 있습니다.
- 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
# output.tf 파일에 정의된 모든 출력 변수의 값 출력
terraform output
# 리소스 목록 출력
terraform state list
# state 파일에서 특정 정보를 추출 : module
cat terraform.tfstate | grep module
# Graph 확인
terraform graph > graph.dot
# 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
6.4 모듈 소스 관리
모듈 소스 관리
- 테라폼 init 명령어를 사용할 때 정의된 모듈 소스 위치를 바탕으로 필요한 모듈을 다운로드하게 됩니다. 모듈 소스를 지정하는 방법은 여러 가지가 있습니다
- 로컬 디렉터리 경로
- 테라폼 레지스트리
- 깃허브
- 비트버킷
- 깃
- HTTP URLs
- S3 Bucket
- GCS Bucket
로컬 디렉터리 경로
- 테라폼 레지스트리와 구분하기 위해 하위 디렉터리는 ./로 시작하고, 상위 디렉터리는 ../로 시작합니다.
- 해당 모듈은 이미 같은 파일 시스템에 존재하기 때문에 별도의 다운로드 과정이 필요 없습니다.
- 모듈이 재사용 가능하도록 설계되었다면 상위 디렉터리에 모듈을 별도로 관리하는 것이 좋습니다. 반면 모듈이 특정 루트 모듈과 밀접하게 연관되어 동작해야 할 경우는 하위 디렉터리에 모듈을 배치하는 것이 바람직합니다.
- 예를 들어, 상위 디렉터리에 있는 모듈을 참조할 때는 다음과 같이 선언합니다.
module "local_module" {
source = "../modules/my_local_module"
}
테라폼 레지스트리
- 테라폼 레지스트리는 테라폼의 프로토콜을 사하여 모듈을 관리하고 사용하는 시스템입니다.
- 공개된 테라폼 모듈과 Terraform Cloud 또는 Terraform Enterprise에서 제공하는 비공개 테라폼 모듈에 접근할 수 있습니다.
- 공개된 모듈을 사용할 때는 <네임스페이스>/<이름>/<프로바이더> 형식으로 소스를 설정합니다.
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "5.1.0"
}
깃허브
- 깃허브는 깃의 원격 저장소로 테라폼 구성에 대한 CI 용도로 사용할 수 있으며 저장된 구성을 테라폼 모듈의 소스로 선언할 수도 있습니다.
- 깃허브에 테라폼 모듈 업로드하기
- 깃허브에 로그인합니다.
- 새로운 깃허브 저장소를 생성합니다.
- Owner: 원하는 소유자를 선택
- Repository name: terraform-module-repo
- Public 선택
- Add .gitignore의 드롭다운 메뉴에서 [Terraform]을 선택
- 맨 아래 Create repository 버튼을 클릭합니다.
- 해당 저장소에 terraform-aws-ec2 디렉터리를 생성하고, main.tf, variables.tf, outputs.tf 파일을 추가하여 업로드합니다.
깃허브 모듈 사용하기
- 디렉터리 생성 및 파일 생성
mkdir module-source-mygithub
cd module-source-mygithub
touch main.tf
- 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"
}
- 실행 및 확인
# 초기화
terraform init
# 아래 디렉터리에 깃허브에 있던 파일이 다운로드 되어 있음을 확인
tree .terraform/modules
# 리소스 배포
terraform apply -auto-approve
# 리소스 목록 출력
terraform state list
# 실습 완료 후 리소스 정리
terraform destroy -auto-approve
'Terraform > Terraform 101 Study' 카테고리의 다른 글
7주차 1편 테라폼으로 AWS EKS 배포 - EKS Blueprints for Terraform (0) | 2024.07.28 |
---|---|
5주차 2편 테라폼 Runner (1) | 2024.07.14 |
4주차 2편 테라폼 State (0) | 2024.07.07 |
4주차 1편 테라폼 프로바이더 (0) | 2024.07.07 |