5주차 Module & Runner
본 스터디 내용 요약은 @CloudNet의 가시다님의 강의를 요약한 내용입니다.
1. 모듈
1.1 모듈 작성 기본 원칙
모듈 디렉토리 형식은
terraform-<프로바이더이름>-<모듈이름>
으로 구성하면 된다.테라폼 구성은 궁극적으로 모듈화가 가능한 구조로 작성해야한다.
각각의 모듈을 독립적으로 관리하기를 제안한다.
공개된 테라폼 레지스트리의 모듈을 참고해라
루트 모듈 하위에 자식 모듈을 구성하는 경우, 종속성이 발생하므로, 루트 모듈 에 모듈 디렉터리를 생성한다. 예시는 밑에서 다룰 예정이다.
// module-directory
|___modules // chile-modules
| |_ main.tf
| |_ output.tf
|___06-basic // root-module
|_ main.tf
1.2 모듈화 해보기
A. 모듈화 소개
모듈의 기본 구조는 테라폼 구성으로 기존에 작성된 모듈을 다른 모듈에서 참조해서 사용할 수 있다.

Variables로 인풋을 받고 Outputs에서 외부 모듈이 참조할 수 있는 값을 정할 수 있다. (캡슐화)
B. [실습] 모듈 작성
자식 모듈 생성
다음과 같은 디렉터리 구조를 생성합니다.
/module-directory
|___modules // chile-modules
| |_terraform-random-pwgen
| |_ main.tf
| |_ output.tf
| |_ variable.tf
|___06-basic // root-module
|_ main.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)
}
자식 모듈 호출 확인
# root 모듈을 설정 해 줍니다.
# 06-module-traning/06-01-basic/main.tf
```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
# 다음과 같은 결과를 확인 할 수 있습니다!

graph dot 파일을 확인하면 다음과 같이 나온다.

1.3 모듈 사용 방식
A. [실습] 모듈과 프로바이더
모듈에서 사용되는 프로바이더에 대한 정의가 필요한데 정의를 모듈 안 혹은 밖에 할 수 있다.
a. 자식 모듈에서 프로바이더 정의 하는 경우
모듈에서 사용하는 프로바이더 버전과 구성상세를 자식 모듈에서 고정한다.
루트 모듈와 자식 모듈에서 서로 다른 버전을 가지고 합의가 되지 않으면, 오류가 발생하고 반복문이 실행이 되지 않는다.
잘 사용되지 않는다.
b. 루트 모듈에서 프로바이더 정의
자식 모듈은 루트 모듈의 프로바이더에 종속 되는 방식이다.
자식 모듈이 사용하는 프로바이더 버전을 일괄적으로 적용하고, 자식 모듈들은 루트 모듈에서 정의하는 프로바이더에 맞게 업데이트 되어야한다.
// module-directory
|___modules // child-modules
| |_ main.tf
| |_ output.tf
|___06-basic // root-module
|_ main.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
}
# 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
}
모듈마다 다른 프로바이더 설정을 해주더라도 정상적으로 동작하는 것을 확인 할 수 있다.



B. [실습] 모듈의 반복문
모듈 또한 반복문을 사용해서 원하는 수량을 프로비져닝 할 수 있다.
모듈 없이 구성하는 것에 대비해 리소스 종속성 관리와 유지보수에 큰 도움이 된다.
provider "aws" {
region = "ap-northeast-2"
}
module "ec2_seoul" {
count = 2 // 카운트로 2개 생성
source = "../modules/terraform-aws-ec2"
instance_type = "t3.small"
}
output "module_output" {
value = module.ec2_seoul[*].private_ip
}
일관된 구조일때는, count를 사용하지만, 필요한 인수값이 다른 경우는 for_each를 활용하면 된다.
해당 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
]
}
1.4 모듈 소스 관리
모듈 블록에 정의된 소스 구성으로 모듈의 코드 위치를 정의한다.
init 할때, 지정 모듈을 다운로드해서 사용할 수 있다.
보통은 해당 모듈의 소스를 포크 떠와서 각 환경에 맞게 사용한다고 한다.
a. 지원하는 경로들
로컬 디렉터리
테라폼 레지스트리
깃허브
비트버킷
s3, gcs 버킷
b. 로컬 디렉터리 경로 지정하기
module "local_module" {
source = "../modules/my_local_module"
}
c. 테라폼 레지스트리로 경로 지정하기
공식 공개된 테라폼 모듈을 사용할 수 있다.
이 외에도 Terraform cloud 와 terraform enterprise와 같은 비공개 테라폼 모듈을 사용할 수 있다.
module "vpc" {
source = "terraform-aws-modules/vpc/aws" // 네임스페이스/이름/프로바이더 형태
version = "5.1.0"
}
d. 깃허브를 경로 지정하기
# main.tf // github repository에 저장할 tf 파일
provider "aws" {
region = "ap-southeast-1"
}
module "ec2_seoul" {
source = "github.com/jivebreaddev/terraform-module-repo/terraform-aws-ec2"
instance_type = "t3.small"
}
이렇게 github에서 가져오게 된다.

e. 이러한 모듈을 사용하는 이유는 무엇일까?
이미 생성된 천줄 이상 복잡한 구성을 논리적으로 구성 요소화 가능합니다.
별개의 논리적 구성 요소로 캡슐화가 가능합니다.
잘 작성된 모듈을 재사용 할 수 있습니다.
일관성이 있습니다.
f. [실습] 다른 모듈을 가져와서 사용하기
git clone https://github.com/hashicorp/learn-terraform-modules-use.git
cd learn-terraform-modules-use
- terraform.tf : 테라폼 블록 확인
- main.tf : 모듈 블록 확인(모듈 소스, 버전) - arguments 확인 , ec2 count 2 확인
다음과 같은 리소스의 input 섹션에서 어떻게 리소스를 생성할지 알 수 있습니다.
input variable들을 variable.tf 에서 확인하고 수정해서 사용하면 됩니다.
저같은 경우에는
variable "vpc_enable_nat_gateway" {
description = "Enable NAT gateway for VPC"
type = bool
default = false
}
variable "vpc_azs" {
description = "Availability zones for VPC"
type = list(string)
default = ["ap-northeast-2c"]
}
provider "aws" {
region = "ap-northeast-2c"
default_tags {
tags = {
hashicorp-learn = "module-use"
}
}
}
# 프로바이더 위치랑 리소스 위치를 전부 변경해줬습니다.
cat .terraform/modules/modules.json| jq
해당 커맨드로 init 이후 모듈 구조도 확인 가능합니다.

다음과 같이 리소스들이 정상 삭제가 되는것을 확인할 수 있습니다.
ec2의 버전이 낮아서 해당 부분은 최신버전으로 변경해서 진행했습니다.
2. Terraform Runner
2.1 Atlantis
Atlantis는 Terraform을 관리하기 위한 runner 이자 workflow로 다음과 같은 플로우로 관리됩니다.
Github으로 PR 이 MERGE가 됩니다.
WEB HOOK을 ATLANTIS로 전송하고
PLAN 결과를 PR 코멘트에 등록합니다.
PR comment에 apply 하게 되면 web hook이 atlantis로 전송됩니다.
apply 의 결과가 github에 전시됩니다.
편한 방법으로 개발자들의 리뷰를 통한 리소스 할당과 설정이 가능합니다.
2.2 설치 관련 주의점
주의점
Locking 에 대한 이해를 정확히 해야한다.
Security에 대해서 취약한 지점을 정확히 알고 대비해둬야한다.
알아야 하는 전략들
atlantis. 설정 파일
pre workflow hook의 shell 보안 취약 주의
post workflow hook 도 확인가능하다.
prometheus로 메트릭 수집이 가능하다.
slack 알람 설정도 hook 설정 가능하다.
2.3 [실습] 직접 설치하고 자원 배포해보기
a. aws ec2 생성
EC2에 다음 스크립트를 돌리면 설정이 된다.
AWS CONFIGURE도 설정해주면 된다.
외부 통신도 가능해야하니깐, 해당 EC2 의 OUTBOUND도 확인해주면 된다.
4141 TCP 통신을 위해 포트 오픈도 해둬야 GITHUB 과 ATLANTIS의 웹훅 통신이 가능하다.
hostnamectl --static set-hostname Atlantis
# Config convenience
echo 'alias vi=vim' >> /etc/profile
echo "sudo su -" >> /home/ubuntu/.bashrc
# Install Packages & Terraform
wget -O- https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list
apt update -qq && apt install tree jq unzip zip terraform -y
# Install aws cli version2
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/install
# Install atlantis
wget https://github.com/runatlantis/atlantis/releases/download/v0.28.3/atlantis_linux_amd64.zip -P /root
unzip /root/atlantis_linux_amd64.zip -d /root && rm -rf /root/atlantis_linux_amd64.zip
b. aws ec2 ssh 접속 확인

다음과 같이 git, atlantis, terraform 들이 정상적으로 다운로드 된것을 확인 할 수 있다.
c. 공인 IP 노출
URL="http://$(curl -s ipinfo.io/ip):4141"
echo $URL
// 다음과 같이 설정해서 결과값을 설정한다.
d. git repo를 private으로 생성

간단하게 git repo를 private으로 생성해준다.
e. git token 생성

Github → Settings → Developer settings ⇒ Personal access tokens : Tokens (classic) ← Repo 제한 가능 Fine-grained tokens 사용 권장
들어가서 atlantis web hook 통에서 사용될 git token을 추가합니다.
f. web hook 을 atlantis에 설정 및 atlantis 시작
./atlantis server \
--atlantis-url="$URL" \
--gh-user="$USERNAME" \
--gh-token="$TOKEN" \
--gh-webhook-secret="$SECRET" \
--repo-allowlist="$REPO_ALLOWLIST"
각 변수들을 선언해준뒤에 실행시키면 atlantis가 정상적으로 올라옵니다.

[실습] Atlantis 사용해보기

다음과 같이 github 리포지토리에 branch 에 대해 pr을 날렸다.
해당 링크를 눌러보면 TERRAFORM PLAN 에 대한 결과가 뜬다.


Atlantis 서버에는 plan 에 대한 수행 결과가 생겼다.

다음과 같은 Lock도 설정된 것을 확인 할 수 있다.

머지 후에 terraform 액션을 실행해보면 먹히지 않는다. 꼭, 머지전에 apply 를 해야한다.

[실습] IAM 유저를 만들어보자
버킷을 생성하기
aws s3 mb s3://jivebreaddev --region ap-northeast-2
vi main.tf
----------
terraform {
backend "s3" {
bucket = "jivebreaddev"
key = "terraform.tfstate" // s3 백엔드를 사용하면 된다.
region = "ap-northeast-2"
}
}
resource "aws_iam_user" "myuser" {
name = "t101user" // 생성할 유저
}
해당 결과 확인

Last updated