✏️ 모듈의 목적과 원리를 이해한다!
✏️ 모듈을 생성하고 활용해보자!
각 프로젝트 당 여러 개의 웹서버를 구축해야한다고 가정하자. 1개의 서버를 생성하는데 필요한 리소스는 static ip, vpc, firewall, instance 이다. 지금까지 배운 바에 의하면, 각 웹서버를 생성하기 위해 여러 개의 파일을 생성하여 필요한 리소스들을 생성해야 할 것이다. 웹서버 1 을 위한 static ip 1, firewall 1 ... 이런식이다.
리소스의 설정값(arguments)를 추가 해야한다면? 각 파일마다 들어가 변경사항을 반영해주어야 할 것이다. 100 개의 서버가 있다면 새로운 설정값을 100번 추가해주어야 할 것이다. 😒
반복적인 코드는 의도치 않은 실수를 유발하게 하며 관리 차원에서도 불필요한 일을 만든다. 이러한 문제점을 해결 할 수 는 없을까?
💡 모듈은 리소스를 위한 템플릿이다. 피피티 템플릿을 하나 만들어두면 유용하다. 새로운 피피티를 만들 때 템플릿을 복사해 제목과 내용만 바꿔주면 된다. 모듈도 마찬가지이다. 특정 리소스의 설정값을 사전에 만들어 모듈을 생성한 뒤, 모듈을 이용하여 똑같은 리소스를 쉽게 찍어낼 수 있다.
terraform plan , terraform apply 와 같은 커맨드를 실행하는 폴더를 일컫는다. 아래와 같이 폴더 및 테라폼 파일을 생성하였다고 가정하자. 이 경우 network 모듈과 server 모듈이 생성되었음을 볼 수 있다. network 모듈의 main.tf 에는 네트워크와 관련된 여러 리소스가 포함 될 수 있다. server 모듈도 마찬가지이다.
// dir tree
-- network/
-- main.tf
-- outputs.tf
-- variables.tf
-- server/
-- main.tf
-- outputs.tf
-- variables.tf
1. 코드의 간소화
모든 인프라를 하나의 파일에서 생성하고 관리한다면 어떨까? 적은 리소스는 관리가 가능하겠지만 리소스를 생성하는 코드가 많아진다면 파일의 가독성은 떨어지고 관리는 더욱이 어려워질 것이다.
모듈은 코드를 간소화 시켜 이러한 문제점을 해결한다.
2. 반복 제거
똑같은 코드를 복사해서 붙인 후 값만 변경하는 일을 하지 않아도 된다.
3. 리소스 표준화
미리 리소스 설정값을 생성하면 모든 리소스가 해당 설정값을 가지고 있음을 보장할 수 있다. 항상 디스크를 암호화 해야한다는 조건이 있다고 가정하자. 모듈을 사용하지 않고 모든 디스크에 암호화 설정값을 추가해줄 경우 의도하지 않게 설정값을 누락할 가능성이 있으므로 인프라를 생성하기 전 모든 리소스들이 필요한 설정값을 다 가지고 있는지 확인해야한다.
모듈을 사용하면 이러한 문제점들이 사라진다. 템플릿을 가져와 사용하기 때문에 항상 설정값이 해당 리소스에 적용되어있음을 보증 할 수 있다.
이 밖에도 코드의 재사용성, 가독성, 관리 용이성 등이 향상 된다는 장점이 있다!
우와 편하다 ⛄
앞서 보여준 예제이다. 아래와 같이 폴더 및 파일을 생성하여 network 모듈과 server 모듈을 만들었다고 가정하자. 템플릿은 완성되었다. 이제 이 모듈을 가져다가 사용하면 된다.
// dir tree
-- network/
-- main.tf
-- outputs.tf
-- variables.tf
-- server/
-- main.tf
-- outputs.tf
-- variables.tf
-- main.tf
이와 같이 main.tf 에서 module 을 불러와 해당 모듈을 사용할 수 있다. 예제에 따르면web_server 라는 리소스는 server 모듈을 사용하게 되고 server_network 라는 리소스는 network 모듈을 사용하게 된다.
// main.tf
provider "google" {
region = us-central-1
}
module "web_server" {
source = "./server"
}
module "server_network" {
source = "./network"
}
module <resource name> {
source = <source path>
}
모듈은 이와 같은 구조로 되어있다.
source : 필수 argument 이다. source pathterraform init 커맨드 실행 시, 관련 라이브러리를 다운로드 한다.💁 VPC 모듈을 만들고 이를 사용하여 개발환경용 VPC 와 운영용 VPC 를 생성해보자.
// dir tree
-- network /
-- main.tf
-- outputs.tf
-- variables.tf
--main.tf
// network/main.tf
resource "google_compute_network" "vpc_network" {
name = var.network_name
auto_create_subnetworks = false // 서브넷 자동 생성 금지
}
// network/variables.tf
variable "network_name" {
type = string
description = "name of the network"
}
// main.tf
module "dev_vpc" {
source = "./network"
network_name = "my-network1"
}
module "prod_vpc" {
source = "./network"
network_name = "my-network2"
}
위 예시 코드는 network 모듈을 사용하여 두 개의 VPC 를 만든 예이다.
main.tf
source 를 사용하여 사용할 모듈을 명시하였다.network 모듈에서 필요로 하는 변수인 network_name 을 명시하였다. network 모듈에서 필요로 하는 변수는 /network/variables.tf 에서 찾아 볼 수 있다. network 리소스에 추가적으로 필요한 부분이 발생한다면 network/main.tf 와 network/variables.tf 파일을 수정하면 된다. 💡 모듈을 사용하여 리소스를 생성하였다. 이 방식으로 VPC 를 생성한다면 VPC 를 만들 때마다
network/main.tf에 있는 코드를 복사해서 사용하지 않아도 된다. 공통적인 arguments 나 공통값이 발생할 경우network/main.tf를 사용하면 일괄 반영되므로 리소스를 표준화 할 수 있게 되었다. Good ~
앞선 예제를 통해 모듈을 사용하여 리소스를 생성해보았다. 이제 우리는 리소스에 대한 공통 설정값을 만들고 이를 쉽게 관리 할 수 있게 되었다.
💁 VPC 를 만들었으니 이제 각 VPC 에 적절한 서버를 만들어보자!
// dir tree
-- network /
-- main.tf
-- outputs.tf
-- variables.tf
-- server /
-- main.tf
-- outputs.tf
-- variables.tf
--main.tf
// network/main.tf
resource "google_compute_network" "vpc_network" {
name = var.network_name
auto_create_subnetworks = false
}
// network/variables.tf
variable "network_name" {
type = string
description = "name of the network"
}
// server/main.tf
resource "google_compute_instance" "dev_vm" {
network = <network name> // ...🤔 ?
}
// main.tf
module "dev_vpc" {
source = "./network"
network_name = "my-network1"
}
module "prod_vpc" {
source = "./network"
network_name = "my-network2"
}
module "dev_vm" {
source = "./network"
network = module.dev_vpc.name // ...🤔 ?
}
서버를 만들려고 보니 해당 서버의 <network name> 을 표기해야 한다는 사실을 깨달았다. 이전에 배웠던 방법을 응용하여 module.dev_vpc.name 로 참조 값을 넣어주니 정상 동작하지 않았다. 왜냐하면 모듈화 된 리소스의 값을 참조하기 위해서는 해당값을 outputs.tf 에 표기하여 이를 참조할 수 있도록 해주어야 하기 때문이다.
💡 즉,
network모듈에 있는 값을server모듈에서 사용하려면network/outputs.tf파일에 어떤 값을 외부에 노출 시킬 것인지 명시적으로 작성해주어야 한다.
다음과 같이 코드를 수정해주었다.
// dir tree
-- network /
-- main.tf
-- outputs.tf
-- variables.tf
-- server /
-- main.tf
-- outputs.tf
-- variables.tf
--main.tf
// network/main.tf
resource "google_compute_network" "vpc_network" {
name = var.vpc_name
auto_create_subnetworks = false
}
// network/variables.tf
variable "vpc_name" {
type = string
description = "name of the network"
}
// network/outputs.tf
output "vpc_name_output" {
value = google_compute_network.vpc_network.name
}
// server/main.tf
resource "google_compute_instance" "dev_vm" {
network = var.vpc_name_from_another_module
}
// server/variables.tf <- NEW!!
variables "vpc_name_from_another_module" {
}
// main.tf
module "dev_vpc" {
source = "./network"
vpc_name = "my-network1"
}
module "prod_vpc" {
source = "./network"
vpc_name = "my-network2"
}
module "dev_vm" {
source = "./server"
vpc_name_from_another_module = module.dev_vpc.vpc_name_output
}
💡 같은 리소스를 반복적으로 생성하는 경우 리소스 템플릿인 모듈을 생성하자!
💡 모듈에서 모듈로 값을 전달할 때는outputs.tf파일을 사용하여 해당 모듈에 어떤 값을 외부에 노출 시킬 것인지 명시해야 한다.