6장 Module

김진원·2023년 7월 29일

IaC

목록 보기
8/10

"테라폼으로 시작하는 IaC"책을 기준으로 4주차 정리 내용입니다.

테라폼으로 인프라와 서비스를 관리하면 시간이 지날수록 구성이 복잡해지고 관리하는 리소스가 늘어나게 된다. 테라폼의 구성 파일과 디렉터리 구성에는 제약이 없기 때문에 단일 파일 구조상에서 지속적으로 업데이트할 수 있지만, 다음과 같은 문제가 발생한다.

  • 테라폼 구성에서 원하는 항목을 찾고 수정하는 것이 점점 어려워짐
  • 리소스들 간의 연관 관계가 복잡해질수록 변경 작업의 영향도를 분석하기 위한 노력이 늘어남
  • 개발/스테이징/프로덕션 환경으로 구분된 경우 비슷한 형태의 구성이 반복되어 업무 효율이 줄어듦
  • 새로운 프로젝트를 구성하는 경우 기존 구성에서 취해야 할 리소스 구성과 종속성 파악이 어려움

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

  • 루트 모듈 Root Module : 테라폼을 실행하고 프로비저닝하는 최상위 모듈
  • 자식 모듈 Chile Module : 루트 모듈의 구성에서 호출되는 외부 구성 집합

🧲 모듈은 테라폼 구성의 집합이다.

  • 관리성 : 모듈은 서로 연관 있은 구성의 묶음이다. 원하는 구성요소를 단위별로 쉽게 찾고 업데이트할 수 있다.
  • 캡슐화 : 테라폼 구성 내에서 각 모듈은 논리적으로 묶여져 독립적으로 프로비저닝 및 관리되며, 그 결과는 은닉성을 갖춰 필요한 항목만을 외부에 노출시킨다.
  • 재사용성 : 구성을 처음부터 작성하는 것에는 시간과 노력이 필요하고 작성 중간에 디버깅과 오류를 수정하는 반복 작업이 발생한다.
  • 일관성표준화 : 테라폼 구성 시 모듈을 활용하는 워크플로는 구성의 일관성을 제공하고 서로 다른 환경과 프로젝트에도 이미 검증한 모듈을 적용해 복잡한 구성과 보안 사고를 방지할 수 있다.

6.1 모듈 작성 기본 원칙

모듈은 대부분의 프로그래밍 언어에서 쓰이는 라이브러리나 패키지와 역할이 비슷하다.

📌 아래와 같은 기본 작성 원칙을 제안함

  • 모듈 디렉터리 형식을 terraform-<Provider>-<Module> 형식
  • 테라폼 구성은 궁극적으로 모듈화가 가능한 구조로 작성할 것
  • 각각의 모듈을 독립적으로 관리하기
  • 공개된 테라폼 레지스트리의 모듈을 참고하기
  • 작성된 모듈은 공개 또는 비공개로 게시해 팀 또는 커뮤니티와 공유하기

6.2 모듈화 해보기

모듈의 기본적 구조는 테라폼 구성으로 입력 변수를 구성하고 결과를 출력하기 위한 구조로 구성한다.

모듈 작성 실습!

하나의 프로비저닝에서 사용자와 패스워드를 여러 번 구성해야 하는 경우

  • random_pet는 이름을 자동으로 생성하고, random_password는 사용자의 패스워드를 설정
  • random_password는 random 프로바이더 리소스로 난수 형태로 패스워드를 만들 수 있다.
# 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

# 테스트를 위해 apply 시 변수 지정
terraform apply -auto-approve -var=isDB=true


두 가지 output이 보인다.


  • 자식 모듈 호출 실습

모듈을 활용하여 반복되는 리소스 묶음으로 최소화한다.

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
}



state 파일에 리소스가 어떤 모듈을 소스에서 배포했는지 추가되었다!


6.3 모듈 사용 방식

모듈에서 사용되는 모든 리소스는 관련 프로바이더의 정의가 필요하다. 여기서 사용자는 프로바이더 정의를 모듈 안과 밖을 고민한다.

# 모듈과 프로바이더

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

모듈에서 사용하는 프로바디어 버전과 구성 상세를 자식 모듈에서 고정하는 방법이다.

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

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

자식 모듈루트 모듈프로바이더 구성에 종속되는 방식이다.

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

첫 번째 사진에서 모듈을 참조하는 것이 보인다. 실행하면 두 개의 리전에 두 개의 Instance 생성을 볼 수 있다.

# 모듈과 반복문

  • 모듈 또한 리소스에서 반복문을 사용하듯 구성할 수 있다.
  • 모듈이라는 리소스 정의 묶음을 원하는 수량으로 프로비저닝할 수 있으므로 모듈 없이 구성하는 것과 대비해 리소스 종속성 관리와 유지 보수에 장점이 있다. count를 사용한 반복문 사용은 리소스에서의 사용 방식처럼 module 블록 내에 선언한다.
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 값이 2이므로 2개의 Instance가 생성되고 private 값의 output이 출력되었다.


필요한 인수 값이 다른 경우인 for_each를 사용 예시

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
  ]
}

dev와 prod로 이름이 다르고, type도 micro와 medium으로 다르게 배포됐다.


6.4 모듈 소스 관리

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

  • 로컬 디렉터리 경로
  • 테라폼 레지스트리 Terraform Registry
  • 깃허브 Github
  • 비트버킷 Bitbucket
  • 깃 Generic Git Repository
  • HTTP URLs
  • S3 Bucket
  • GCS Bucket google cloud storage

# 테라폼 레지스트리

  • 테라폼 모듈 레지스트리는 테라폼의 프로토콜을 사용해 모듈을 사용하는 방식이다.
  • 공개된 테라폼 모듈을 사용하거나 Terraform Cloud, Terraform Enterprise에서 제공되는 비공개 테라폼 모듈을 사용할 때 설정하는 소스 지정 방식이다.
module "vpc" {
  source  = "terraform-aws-modules/vpc/aws"
  version = "5.1.0"
}

AWS VPC 모듈 실습

깃허브에 공개된 테라폼 코드로 진행한다.

  • 모듈 다운로드
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
  • 코드 수정 : 서울 리전, CIDR 변경
# 서울 리전 변경
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

많은 output 내용이 출력되고, 내용에 맞게 VPC가 subnet과 Route table이 생성된 것을 볼 수 있다.

0개의 댓글