메뉴

문서정보

Terraform 0.12.23 버전 기준이다. for문은 0.12 버전부터 지원하기 때문에 0.12 이전 버전에서는 작동하지 않을 것이다.

Terraform 코드 시나리오

아래와 같은 VPC를 Terraform 코드로 개발&배포 하기로 했다. 이 문서는 테라폼 모듈을 이용한 코드의 구조화가 목적이므로 VPC 외에 다른 AWS 자원들은 포함하지 않을 것이다.

 Terraform 테스트 VPC

  1. VPC : 10.20.0.0/16
  2. Internet gateway : VPC 내부와 외부 인터넷 간에 통신을 하기 위한 관문
  3. Public subnet : VPC 안에 만들어진 24bit 서브넷이다. Internet gateway와 연결 (associate)된다.
  4. Private subnet : Internet gateway와 연결되지 않는다. 즉 인터넷에서 접근 할 수 없으며, 인터넷으로 나갈 수도 없다. 나가는 것은 NAT gateway를 전개하면 되는데, 여기에서는 NAT gateway를 사용하지 않는다.
우리는 이 VPC 구조를 레퍼런스로 해서 DEV, STG, PRD 3개의 네트워크를 만들 것이다.
  1. DEV : 개발을 위해서 사용하는 네트워크
  2. STG : 개발이 끝난 애플리케이션을 검증하는 네트워크
  3. PRD : 검증이 끝난 애플리케이션을 고객에게 서비스하는 네트워크
DEV, STG, PRD는 네트워크 대역대만 다르고 다를 뿐 모든 구성이 완전히 동일하다. DEV는 10.20.0.0/16, STG는 10.21.0.0/16, PRD 는 10.22.0.0/16 이다.

확장 가능한 Terraform 코드 구조 만들기 - 1

우리가 만들려고 하는 인프라는 "네트워크 주소"만 변경될 뿐 완전히 동일한 네트워크 구조를 가지는 것을 알 수 있다. 코드를 잘 구조화 하면, 단지 하나의 코드로 DEV, STG, PRD 모두 배포 할 수 있을 것이다.

DEV, STG, PRD를 환경(environment)로 빼내고, 각 환경에 따라서 네트워크 값만 변경해 준다면 하나의 코드로 여러 환경으로의 배포가 가능할 것이다. Terrform의 environment variable을 이용해서 이러한 코드를 만들 수 있다.
variable "environment" {
	type = string
	description = "Options: dev, stg, prd"
}

variable "cidr_ab" {
	type = map
	default = {
		dev = "10.20"
		stg = "10.21"
		dev = "10.22"
	}
}
이 Terraform 코드를 실행 할 때 environment를 설명하면, 변수에 환경에 맞는 값을 설정 할 수 있다. 예를 들어서 plan 을 실행하면 아래와 같이 프롬프트가 뜬다.
# terraform plan
var.environment
  Options: dev, stg, prd

  Enter a value: 
입력한 값은 var.environment 에 저장이 되므로 이 값을 이용해서 환경을 선택 할 수 있다. 예를 들어서 "dev"를 입력했다면, lookup 함수를 이용해서 dev 환경의 vpc cidr 값을 읽을 수 있다.
lookup(var.cidr_ab, var.environment)
# cidr_ab["dev"] 와 같은 효과를 가진다. 
# 따라서 "10.22"가 리턴된다.

environment variable를 이용한 코드를 만들어보자. 이 코드의 파일 구조는 아래와 같다.
tree
.
├── README.md
├── provider.tf
├── main.tf
└── variable.tf

variable.tf
variable "aws_region" {
	description = "test region"
	default = "ap-northeast-2"
}

variable "environment" {
	type = string
	description = "Options: dev, stg, prd"
}

variable "cidr_ab" {
	type = map
	default = {
		dev = "10.20"
		stag = "10.21"
		prod = "10.22"
	}
}

variable "project" {
	type = string
	default = "helloworld"
}

data "aws_availability_zones" "available" {
	state = "available"
}

locals {
	availability_zones = data.aws_availability_zones.available.names
	vpc_cidr = "${lookup(var.cidr_ab, var.environment)}.0.0/16"
}


locals {
	cidr_c_public_subnets  = 1
	cidr_c_private_subnets = 3
   	max_subnets     = 2
}

locals {
	public_subnets = merge({ 
		for az in local.availability_zones: 
         "${lookup(var.cidr_ab, var.environment)}.${local.cidr_c_public_subnets + index(local.availability_zones, az)}.0/24" => az
		if index(local.availability_zones, az) < local.max_subnets
    })
	private_subnets = merge({
		for az in local.availability_zones:
         "${lookup(var.cidr_ab, var.environment)}.${local.cidr_c_private_subnets + index(local.availability_zones, az)}.0/24" => az
		if index(local.availability_zones, az) < local.max_subnets
	})
}

output "vpc" {
	value = local.vpc_cidr
}


output "public_subnet" {
	value = local.public_subnets
}

output "private_subnet" {
	value = local.private_subnets
}

output "aws_availability_zones" {
	value = data.aws_availability_zones.available
}
locals.public_subnets 이 코드가 생소 할 수 있으니 분석을 해보자.
public_subnets = merge({ 
	for az in local.availability_zones: 
        "${lookup(var.cidr_ab, var.environment)}.${local.cidr_c_public_subnets + index(local.availability_zones, az)}.0/24" => az
	if index(local.availability_zones, az) < local.max_subnets
})
... 계속

main.tf
resource "aws_vpc" "default" {
 	cidr_block = local.vpc_cidr
 	enable_dns_hostnames = true
 	tags = {
		Project = var.project 
		Environment = var.environment 
 	}
}

resource "aws_subnet" "public" {
	for_each = local.public_subnets

	vpc_id = aws_vpc.default.id
	cidr_block = each.key
	availability_zone = each.value
	tags = {
		Project = var.project
		Environment = var.environment
	}
}

resource "aws_subnet" "private" {
	for_each = local.private_subnets

	vpc_id = aws_vpc.default.id
	cidr_block = each.key
	availability_zone = each.value
	tags = {
		Project = var.project
		Environment = var.environment
	}
}

resource "aws_internet_gateway" "default" {
	vpc_id = aws_vpc.default.id
	tags = {
		Project = var.project
		Environment = var.environment
	}
}

resource "aws_route_table" "public" {
	vpc_id = aws_vpc.default.id
	route {
		cidr_block = "0.0.0.0/0"
		gateway_id = aws_internet_gateway.default.id
	}
	tags = {
		Project = var.project
		Environment = var.environment
	}
}

resource "aws_route_table_association" "public" {
	for_each = local.public_subnets
	subnet_id = aws_subnet.public[each.key].id
	route_table_id = aws_route_table.public.id
}

... 계속