brunch

You can make anything
by writing

C.S.Lewis

by Master Seo Aug 01. 2023

32탄-10. 4주 차-테라폼-Module


<1> 모듈 작성 기본 원칙

<2> 모듈 실습

<3> 모듈과 프로바이더  실습1

<4> 루트 모듈에서 프로바이더 정의(실습)

<5> 모듈의 반복문 - count  사용

<6> 모듈의 반복문 -   for each 문

<7> 모듈 소스 관리

<8> 테라폼 레지스트리에 공개된 모듈을 사용하여 리소스 배포 



<1> 모듈 작성 기본 원칙


1

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


2

모듈 디렉터리 형식을 terraform-<프로바이더 이름>-<모듈 이름> 형식을 제안한다.

예) terraform-aws-module

디렉토리 또는 레지스트리 이름 - 어떤 프로바이더 - 모듈 이름




<2>  모듈 실습


1

구성 ?




random_pet는 이름을 자동으로 생성

random_password는 사용자의 패스워드를 설정


https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/pet



2

자식 모듈을 만든다.

root 모듈을 만들어  자식모듈을 불러 쓴다.



3

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

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


4

자식 모듈 만든다 ?

main.tf ,  variable.tf ,  output.tf   파일 3개를 만든다.



vi 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 = "!#$%*?"

}



5

vi variable.tf


variable "isDB" {

  type        = bool

  default     = false

  description = "패스워드 대상의 DB 여부"

}



6

vi output.tf


output "id" {

  value = random_pet.name.id

}


output "pw" {

  value = nonsensitive(random_password.password.result) 

}



7

자식 모듈 테스트


#

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


#

ls *.tf

terraform init && terraform plan


# 테스트를 위해 apply 시 변수 지정

terraform apply -auto-approve -var=isDB=true


// 변수를 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





8

자식 모듈 호출 실습


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



vi 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

}



9

실행 : 자식 모듈을 호출해 반복 재사용하는 루트 모듈의 결과


#

cd 06-module-traning/06-01-basic


#

terraform init && terraform plan && terraform apply -auto-approve

Apply complete! Resources: 2 added, 0 changed, 0 destroyed.

Outputs:


mypw1 = {

  "id" = "equipped-mustang"

  "pw" = "OXST1EYqQc"

}


mypw2 = {

  "id" = "diverse-impala"

  "pw" = "y8mEbOJhS6dCTiK#"

}


// 반복문처럼 Output이 나온다. 자식모듈을 호출해서 반복 사용을 했다.

// 패스워드 길이가 다르다. 10자리, 16자리



# 확인

terraform state list



10

# tfstate에 모듈 정보 확인

cat terraform.tfstate | grep module

테라폼 root 모듈  state 파일의   resources 부분에 module 내용이 생긴다.




11

# terraform init 시 생성되는 modules.json 파일 확인


tree .terraform

.terraform

├── modules

│   └── modules.json

...


## 모듈로 묶여진 리소스는 module이라는 정의를 통해 단순하게 재활용하고 반복 사용할 수 있다.

## 모듈의 결과 참조 형식은 module.<모듈 이름>.<output 이름>으로 정의된다.


cat .terraform/modules/modules.json | jq

{

  "Modules": [

    {

      "Key": "",

      "Source": "",

      "Dir": "."

    },

    {

      "Key": "mypw1",

      "Source": "../modules/terraform-random-pwgen",

      "Dir": "../modules/terraform-random-pwgen"

    },

    {

      "Key": "mypw2",

      "Source": "../modules/terraform-random-pwgen",

      "Dir": "../modules/terraform-random-pwgen"

    }

  ]

}



# graph 확인

terraform graph > graph.dot






<3> 모듈과 프로바이더  실습1


1

Root module에 공통적으로 프로바이더를 설정하라.

Child module에 설정하면 문제가 될수 있다.

자식모듈은 루트 모듈의 프로바이더에 종속된다.

모듈에는 다수의 프로바이더가 사용될 가능성이 있으므로 map타입으로 구성하는 provider로 정의한다.


문제 ?

Root와 자식 모듈간에 조건 합의가 안되면 오류가 발생한다.

Child module에서 프로바이더를 쓰면, 반복문을 쓸수 없다.



2

실습을 위한 디렉터리 구성의 예

06-module-traning

├─ modules                                      # child

│   └── terraform-aws-ec2

│         └──main.tf

└─ multi_provider_for_module   #root

             └──main.tf


3

# child


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



4

# ec2 배포하는 내용 - 디폴트 vpc 사용 예제.


vi 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

  }

}



//  depends_on    = [aws_default_vpc.default]

디폴트 vpc가 있는지 디펜트온을 걸어 놓는다.


  ami           = data.aws_ami.default.id

// ami은 data에서 가져온다.


  instance_type = var.instance_type

// 인스턴스는 var 변수에서 가져온다.




5

vi variable.tf


variable "instance_type" {

  description = "vm 인스턴스 타입 정의"

  default     = "t2.micro"

}


variable "instance_name" {

  description = "vm 인스턴스 이름 정의"

  default     = "my_ec2"

}



6

vi output.tf


output "private_ip" {

  value = aws_instance.default.private_ip

}





<4> 루트 모듈에서 프로바이더 정의(실습)


1

06-module-traning/multi_provider_for_module/main.tf  과   output.tf 파일 생성


mkdir -p 06-module-traning/multi_provider_for_module/

cd 06-module-traning/multi_provider_for_module/



2

vi 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"

}


// 디폴트는 싱가포르로 생성된다.

// 서울은 프로바이더에  값을 주어,  서버 타입을 t3로 변경한다.




3

vi output.tf


output "module_output_singapore" {

  value = module.ec2_singapore.private_ip

}


output "module_output_seoul" {

  value = module.ec2_seoul.private_ip

}



4

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


cd 06-module-traning/multi_provider_for_module/

terraform init

cat .terraform/modules/modules.json | jq


// key라는 이름으로 싱가포르와 서울이 구분된다.



#

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





<5> 모듈의 반복문 - count  사용



모듈내에서도 반복문을 사용할 수 있다.



1

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



2

mkdir -p 06-module-traning/module_loop_count/



3

vi 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   

}



4

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


cd 06-module-traning/module_loop_count/

terraform init

cat .terraform/modules/modules.json | jq



terraform apply -auto-approve

콘솔에서 ec2 생성 확인



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







<6> 모듈의 반복문 -   for each 문


모듈 묶음에 일관된 구성과 구조로 프로비저닝이 되는 경우라면 count가 간편한 방안이지만, 

동일한 모듈 구성에 필요한 인수 값이 다르다면 for_each를 활용한다.



1

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


cd 06-module-traning/module_loop_count



2

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

  ]

}



3

#

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





<7> 모듈 소스 관리



1

https://developer.hashicorp.com/terraform/language/modules/sources

https://developer.hashicorp.com/terraform/language/modules/sources#terraform-registry

https://registry.terraform.io/?product_intent=terraform


2

테라폼 레지스트리 사용해보자.

테라폼 레지스트리  > 브라우저 모듈 사용할수 있다. 

https://registry.terraform.io/browse/modules

https://registry.terraform.io/modules/terraform-aws-modules/vpc/aws/latest



3

aws vpc 모듈 실습 해보자~

https://registry.terraform.io/modules/terraform-aws-modules/vpc/aws/latest

https://github.com/terraform-aws-modules/terraform-aws-vpc


예제 )

https://github.com/terraform-aws-modules/terraform-aws-vpc/tree/master/examples/simple


mkdir work

cd work


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




4

코드 수정 : 서울 리전 변경, 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

}



5

# 모듈 확인 - 여기 내용을 참고한다.  자식모듈이다.

tree ../../modules



6

terraform init

cat .terraform/modules/modules.json| jq



# 생성된 리소스들 확인해보자!

terraform apply -auto-approve

// vpc가 만들어진다!!!!


terraform output

// 아웃풋이 많다. 재활용하자!!!


terraform state list



7

콘솔에서 VPC 확인하자!!!



# 실습 완료 후 리소스 삭제

terraform destroy -auto-approve




<8> 테라폼 레지스트리에 공개된 모듈을 사용하여 리소스 배포 


1

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

- 6.3에서 사용한 06-module-traning/modules/terraform-aws-ec2/ 를 깃허브에 업로드하는 과정

    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 추가 후 업로드


2

디렉터리 생성 후 main.tf 파일 생성

mkdir module-source-mygithub



3

# 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"

}



4

실행

#

cd module-source-mygithub

terraform init


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

tree .terraform/modules


# 배포

terraform apply -auto-approve

terraform state list


# 실습 완료 후 삭제

terraform destroy -auto-approve








다음과정

https://brunch.co.kr/@topasvga/3395




전체 보기

https://brunch.co.kr/@topasvga/3347


다음은 주말 CloudNet 테라폼 스터디 내용 참고하여  정리한 부분입니다.

https://gasidaseo.notion.site/gasidaseo/CloudNet-Blog-c9dfa44a27ff431dafdd2edacc8a1863  


감사합니다.



브런치는 최신 브라우저에 최적화 되어있습니다. IE chrome safari