⭐ 가시다(gasida) 님이 진행하는 Terraform T101 4기 실습 스터디 게시글입니다.
책 '테라폼으로 시작하는 IaC'을 참고했습니다!
게시글 상 소스코드, 사진에서 **굵게** 혹은 '''코드쉘''' 에 대한 부분이 들어가있을수도 있습니다.
Terraform 에서 Module 이 필요한 이유?
Terraform 으로 인프라 프로비저닝을 사용한다고 하였을때, 시간이 지날수록 구성이 복잡해지고 관리하는 리소스가 늘어나게된다.
이에따라 자연스레 아래의 문제가 발생하게된다.
- 테라폼 구성에서 원하는 항목을 찾고 수정하는 것이 점점 어려워짐
- 리소스들 간의 연관 관계가 복잡해질수록 변경 작업의 영향도를 분석하기 위한 노력이 늘어남
- 개발/스테이징/프로덕션 환경으로 구분된 경우 비슷한 형태의 구성이 반복되어 업무 효율이 줄어듦
- 새로운 프로젝트를 구성하는 경우 기존 구성에서 취해야 할 리소스 구성과 종속성 파악이 어려움
이러한 이유로 Terraform 의 Module 기능이 필요해진다
Module 구분
Module 은 Root Module, Child Module 로 구분된다.
- Root(루트) Module: 테라폼을 실행하고 프로비저닝하는 최상위 모듈
- Child(자식) Module: 루트 모듈의 구성에서 호출되는 외부 구성 집합
Module 은 테라폼 구성의 집합으로, Terraform 으로 관리하는 대상의 규모가 커지고 복잡해질때 생긴 문제를 보완하기 위한 방안으로 사용한다.
모듈 작성원칙
- 모듈 디렉토리 형식을
terraform-<프로바이더 이름>-<모듈 이름>
으로 지정하기를 권고한다. - Terraform 구성을 Module화가 가능한 구조로 작성하는것을 권고한다.
- 각각의 모듈을 독립적으로 관리하는걸 권고한다.
- 모듈을 독립적으로 관리하기 위해 디렉토리 구조를 만들때, 모듈을 위한 별도공간을 생성하는 방식으로 진행한다.
모듈화?
Terraform 에서 권장 모듈의 구조: 테라폼 구성으로 입력변수 구성, 결과를 출력하는 구조
모듈화: 구조를 재활용 하기 위한 템플릿 작업을 지칭함.
모듈단위(루트, 자식) 로 모듈화를 진행하면, 아래의 구조로 만들 수 있다.
모듈작성 실습
시나리오: 하나의 Provisioning → User/Password 를 여러번 구성해야 하는 상황
단순 배포
실습구조 생성
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)
}
배포
terraform init && terraform plan
**terraform apply -auto-approve -var=isDB=true**
배포 결과 중 확인가능한 정보: 단일 id/pw 로 배포 확인
Apply complete! Resources: 2 added, 0 changed, 0 destroyed.
Outputs:
id = "active-duckling"
pw = "U8e4DSQNfKwHsnez"
자식 모듈 호출 실습
- 시나리오 : 다수의 리소스 생성을 목적으로 여러번 반복하여 리소스를 생성해야하는 경우
- 자식모듈을 사용하지 않는경우: 리소스 수만큼 반복해 구성파일 정의. 이름을 고유하게 설정해줘야함
- 자식모듈을 사용하는경우: 모듈을 활용하면 반복되는 리소스 묶음을 최소화 하는게 가능함
환경구성
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
배포 결과 중 확인가능한 정보: 다중 id/pw 로 배포 확인
mypw1 = {
"id" = "enabling-lark"
"pw" = "8NHdWc7XFW"
}
mypw2 = {
"id" = "accurate-mole"
"pw" = "o%mXhnq$W*cfMw1e"
}
배포확인
terraform state list
module.mypw1.random_password.password
module.mypw1.random_pet.name
module.mypw2.random_password.password
module.mypw2.random_pet.name
tree .terraform
.terraform
├── modules
│ └── modules.json
└── providers
└── registry.terraform.io
└── hashicorp
└── random
└── 3.6.2
└── darwin_arm64
├── LICENSE.txt
└── terraform-provider-random_v3.6.2_x5
8 directories, 3 files
Module & Provider?
문제 상황: Module 에서 사용되는 모든 리소스는 Provider 의 정의가 필요함.
문제점: Provider 정의를 모듈 안 or 밖 어디에 해야할까?
CASE1. 자식 모듈에서 프로바이더 정의
- 프로바이더 버전과 구성에 민감하거나, 루트 모듈에서 프로바이더 정의 없이 자식 모듈이 독립적인 구조일 때 고려할 방법이다
- 하지만 동일한 프로바이더가 루트와 자식 양쪽에 또는 서로 다른 자식 모듈에 버전 조건 합의가 안 되면, 오류가 발생하고 모듈에 반복문을 사용할 수 없다는 단점이 있으므로 잘 사용하지 않는다.
CASE2. 루트 모듈에서 프로바이더 정의(실습)
- 자식 모듈은 루트 모듈의 프로바이더 구성에 종속되는 방식이다.
- 디렉터리 구조로는 분리되어 있지만 테라폼 실행 단계에서 동일 계층으로 해석되므로 프로바이더 버전과 구성은 루트 모듈의 설정이 적용된다.
- 모듈에는 다수의 프로바이더가 사용될 가능성이 있으므로 map 타입으로 구성하는 provider로 정의한다.
CASE2의 구조는 아래와 같다.
실습환경구성(자식모듈)
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
}
배포
cd 06-module-traning/multi_provider_for_module/
terraform init
**terraform apply -auto-approve**
배포확인
terraform output
terraform state list
# 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
TMI) AWS 싱가포르 리전에 접근했다고 이런메일도 보내준다
Module & loop
- 모듈 또한 리소스에서 반복문을 사용하듯 구성할 수 있다.
- 모듈 없이 구성하는 것과 대비해 리소스 종속성 관리와 유지 보수에 장점이 있다
실습 환경구성
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
}
- 모듈 묶음에 일관된 구성과 구조로 프로비저닝이 되는 경우라면 count가 간편한 방안이지만, 동일한 모듈 구성에 필요한 인수 값이 다르다면 for_each를 활용한다.
배포
cd 06-module-traning/module_loop_count/
terraform init
**terraform apply -auto-approve**
배포확인
terraform output
terraform state list
# 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**
코드수정
cd 06-module-traning/module_loop_count
vi 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
# 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
Module Source 관리
- Module 블록에 정의된 소스 구성으로 모듈의 코드 위치를 정의한다
- terraform init을 수행할 때 지정된 모듈을 다운로드해 사용한다
e.g) Local Dir, Terraform Registry, Github, S3 ….
로컬 디렉터리 경로
- 하위 디렉터리는 ./로, 상위 디렉터리는 ../로 시작
- 재사용성이 고려된다면 상위 디렉터리에 별도 관리하는 것을 권장하고, 항상 루트 모듈과 함께 동작해야 하는 경우 하위 디렉터리에 모듈을 정의
#e.g.
module "local_module" {
source = "../modules/my_local_module"
}
테라폼 레지스트리
- 테라폼의 프로토콜을 사용해 모듈을 사용하는 방식
- 공개된 테라폼 모듈, Terraform Cloud, Terraform Enterprise 에서 제공되는 모듈을 사용가능함
#e.g.
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "5.1.0"
}
Module Source : Git example
Github 로그인 → Repo 생성
- 이번 예제에서 User Owner 는 digzect 로 표현합니다.
- 실습을 따라할 때엔 digzect 대신에, 각자의 id 로 지정합니다.
환경구성
git clone https://github.com/digzect/terraform-module-repo.git
cd terraform-module-repo
mkdir 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
}
}
#variables.tf
variable "instance_type" {
description = "EC2 인스턴스 타입"
type = string
default = "t2.micro"
}
variable "instance_name" {
description = "EC2 인스턴스 이름"
type = string
default = "my_ec2"
}
#outputs.tf
output "private_ip" {
value = aws_instance.default.private_ip
}
코드 푸쉬
git add .
git commit -m "Add EC2 module"
git push
로컬 코드 작업환경구성
mkdir module-source-mygithub
cd module-source-mygithub
코드추가
#main.tf
provider "aws" {
region = "ap-southeast-1"
}
module "ec2_instance" {
source = "github.com/digzect/terraform-module-repo//terraform-aws-ec2"
instance_type = "t3.small"
instance_name = "github_module_ec2"
}
output "ec2_private_ip" {
value = module.ec2_instance.private_ip
}
구조확인
tree .
.
├── module-source-mygithub
│ └── main.tf
└── terraform-aws-ec2
├── main.tf
├── outputs.tf
└── variables.tf
3 directories, 4 files
git status
현재 브랜치 main
브랜치가 'origin/main'에 맞게 업데이트된 상태입니다.
추적하지 않는 파일:
(커밋할 사항에 포함하려면 "git add <파일>..."을 사용하십시오)
module-source-mygithub/
커밋할 사항을 추가하지 않았지만 추적하지 않는 파일이 있습니다 (추적하려면 "git
add"를 사용하십시오)
테라폼배포
terraform init
terraform plan
terraform apply -auto-approve
결과
- 성공적으로 github repo 에 있는 Terraform Module 정보를 가져와서 Provisioning 하였다.
'외부활동' 카테고리의 다른 글
[T101] 4기 스터디: AWS EKS Blueprints 로 ArgoCD 배포 (0) | 2024.07.28 |
---|---|
Notion 서드파티 오픈소스에 기여한 썰 푼다 (0) | 2024.07.17 |
[T101] 4기 스터디: Confluent Cloud with Terraform (0) | 2024.06.29 |
[AEWS] 2기 스터디: Terraform (0) | 2024.04.28 |
[AEWS] 2기 스터디: CI (1) | 2024.04.21 |