본문 바로가기
공부/IaC

[Terraform on AWS] VPC, Subnet Module 만들어서 사용하기

by haejang 2021. 3. 24.
728x90
728x90

 

GitHub에서 코드 바로 실행해보기

 

 

지난 글에서 테라폼을 사용해 VPC 및 EC2 등의 리소스들을 생성해 보았다

그러나, 저렇게 만들어 놓은 코드들은 재사용하기가 어렵다

테라폼 모듈을 이용해서 재사용하기 쉽게 만들어보자

 

목차

1. 테라폼 모듈 간단 설명

2. 테라폼 모듈 간단 체험 (VPC)

3. VPC, Subnet 모듈화

 


1. 테라폼 모듈 간단 설명

 

 

  • 모듈은 폴더 단위 이다
  • 해당 폴더 내의 모든 구성 파일들이 하나의 모듈이 되는 것
  • 그래서 난 VPC, Public Subnet, Private Subnet, EC2 등등 각각 폴더를 만들어 모듈화를 할 것이다
  • 모듈로 쓸 파일이 수정되었다면 terraform get 명령을 통해 변경사하을 동기화해야 한다
  • 모듈 구문의 기본 문법은 아래와 같다
module "NAME" {
  source = "SOURCE"

  [CONFIG .. (vars)]
}

이 때 SOURCE 부분에 모듈로 쓰려고 리소스를 정의해둔 폴더의 경로를 적어주면 된다

위와 같은 경우는 상대경로를 사용해

module "vpc" {
  source = "../module/vpc"
}

이렇게 사용할 수 있다 (물론 절대경로로 사용해도 무관)

CONFIG 부분엔 해당 리소스 탭에서 사용할 변수 등을 적어주면 된다

 

2. 테라폼 모듈 간단 체험 (VPC)

일단 맛보기로 VPC를 간단하게 모듈화 해보자

 

module/vpc/main.tf

resource "aws_vpc" "vpc" {
  cidr_block           = var.vpc_cidr
  enable_dns_hostnames = true

  tags = {
    "Name" = "vpc"
  }
}

variable "vpc_cidr" {}

 

stage/main.tf

provider "aws" {
  region = "ap-northeast-2"
}

module "vpc" {
  source = "../module/vpc"

  vpc_cidr = var.vpc_cidr
}

variable "vpc_cidr" {
  description = "VPC CIDR BLOCK : x.x.x.x/x"
  type        = string
}

 

여기서 주의할 점

  • provider는 테라폼을 실행할 폴더(이하 stage)에서 정의
  • 입력받을 변수의 경우 stage, 모듈 두군데 다에서 변수 정의

 

 

물론 변수를 입력받지 않고 stage 폴더에서 직접 지정해줄 수도 있다

그러나 재사용성을 극대화하기 위해 앞으로도 상황마다 바뀔 값들은 모두 변수처리를 할 것이며, 당연히 변수 파일도 따로 만들 것이다

 

이제 이 파일들을 가지고 실행을 해보자

terraform init(또는 get) > plan > apply

 

 

VPC CIDR 블럭을 지정해주고 변경사항 확인 후 yes를 입력해주면

 

 

원하던대로 vpc가 생성된걸 확인할 수 있다

 

3. VPC, Subnet 모듈화

1) test8-module/module/vpc/

main.tf

resource "aws_vpc" "vpc" {
  cidr_block           = var.vpc_cidr
  enable_dns_hostnames = true

  tags = {
    Name = "${var.alltag}-vpc"
  }
}

resource "aws_internet_gateway" "igw" {
  vpc_id = aws_vpc.vpc.id

  tags = {
    Name = "${var.alltag}-igw"
  }
}

vars.tf

variable "vpc_cidr" {}
variable "alltag" {}

outputs.tf

output "vpc_id" {
  value = aws_vpc.vpc.id
}

output "igw_id" {
  value = aws_internet_gateway.igw.id
}

vpc 모듈에선 인터넷 게이트웨이까지 한번에 정의해줬다 (모든 vpc마다 igw 하나씩은 꼭 필요할 것이기 때문에...)

변수들은 어차피 전부 입력받을 것이기 때문에 그냥 정의만 해줬고, alltag 변수는 태그에 일관성을 주기 위한 회사 이름 또는 플젝 이름이다

다른 모듈에서 사용해야 할 vpc id와 igw id는 output으로 출력해줬다

 

 

2) test8-module/module/subnet/

main.tf

resource "aws_subnet" "subnet" {
  vpc_id                  = var.vpc_id
  cidr_block              = var.public_subnet_cidr
  availability_zone       = var.public_subnet_az
  map_public_ip_on_launch = var.is_public

  tags = {
    Name = "${var.alltag}-subnet-${var.public_or_private[var.is_public]}"
  }
}

vars.tf

variable "vpc_id" {}
variable "public_subnet_cidr" {}
variable "public_subnet_az" {}
variable "alltag" {}
variable "is_public" {}

variable "public_or_private" {
  type = map(any)
  default = {
    true  = "public"
    false = "private"
  }
}

outputs.tf

output "subnet_id" {
  value = aws_subnet.subnet.id
}

subnet 모듈을 실행할 땐, is_public 이란 bool 형태의 변수를 사용해 public subnet인지 private subnet인지를 구분할 예정이다

is_public이 true로 들어온다면, public subnet이므로 map_public_ip_on_launch도 true가 된다

또한 map 타입 변수를 활용해 is_public 값에 따라 tagging도 public 또는 private으로 작성되도록 했다

 

 

 

3) test8-module/module/rtb_igw/

main.tf

resource "aws_route_table" "rtb" {
  vpc_id = var.vpc_id

  tags = {
    Name = "${var.alltag}-rtb-public"
  }
}

resource "aws_route" "rtb" {
  route_table_id         = aws_route_table.rtb.id
  destination_cidr_block = "0.0.0.0/0"
  gateway_id             = var.igw_id
}

resource "aws_route_table_association" "rtb" {
  subnet_id      = var.subnet_id
  route_table_id = aws_route_table.rtb.id
}

vars.tf

variable "alltag" {}
variable "vpc_id" {}
variable "igw_id" {}
variable "subnet_id" {}

public subnet을 생성하면, igw를 붙인 route table이 생성되기 위한 모듈이다

 

 

 

4) test8-module/stage/

스테이징 환경에서 실제로 실행할 파일들이다

main.tf

provider "aws" {
  region = "ap-northeast-2"
}

module "vpc" {
  source = "../module/vpc"

  vpc_cidr = var.vpc_cidr
  alltag   = var.alltag
}

module "public_subnet" {
  source = "../module/subnet"

  vpc_id             = module.vpc.vpc_id
  public_subnet_cidr = var.public_subnet_cidr
  public_subnet_az   = data.aws_availability_zones.available.names["${var.public_subnet_az}"]
  is_public          = true
  alltag             = var.alltag
}

module "public_subnet_rtb_igw" {
  source = "../module/rtb_igw"

  vpc_id    = module.vpc.vpc_id
  igw_id    = module.vpc.igw_id
  subnet_id = module.public_subnet.subnet_id
  alltag    = var.alltag
}

module "private_subnet" {
  source = "../module/subnet"

  vpc_id             = module.vpc.vpc_id
  public_subnet_cidr = var.private_subnet_cidr
  public_subnet_az   = data.aws_availability_zones.available.names["${var.private_subnet_az}"]
  is_public          = false
  alltag             = var.alltag
}

vars.tf

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

variable "alltag" {
  description = "company name"
  //default     = "honglab"
}

variable "vpc_cidr" {
  description = "VPC CIDR BLOCK : x.x.x.x/x"
  //default     = "10.0.0.0/16"
}

variable "public_subnet_cidr" {
  description = "Public Subnet CIDR BLOCK : x.x.x.x/x"
  //default     = "10.0.0.0/24"
}

variable "public_subnet_az" {
  description = "Public Subnet AZ : 0(A)~3(D)"
  //default     = 0
}

variable "private_subnet_cidr" {
  description = "Private Subnet CIDR BLOCK : x.x.x.x/x"
  //default     = "10.0.10.0/24"
}

variable "private_subnet_az" {
  description = "Private Subnet AZ : 0(A)~3(D)"
  //default     = 2
}

실제로 입력을 하면서 테라폼을 실행시키려면 default값들은 다 없어야 하지만 실습용으로 빨리빨리 진행하고 싶으면 주석을 해제하고 실행하자

aws_available_zones data는 list 형식이기 때문에 0~3까지의 숫자를 입력받아서 가용 영역으로 정해주었다

(a~d를 입력받으려면 map 변수를 사용하면 된다)

 

 

폴더 구조는 왼쪽 아래와 같고, 이대로 테라폼을 실행해보면

 

 

변수들의 입력을 받은 다음 실행이 된다

콘솔에 가보면

 

 

의도한대로 리소스들이 잘 생성된걸 확인할 수 있다

destroy할 땐 yes 입력할 때 빼고 다 엔터만 눌러줘도 된다


더 구현해보고 싶은 것

숫자를 입력받아서 그 숫자만큼 vpc와 서브넷들을 만든다거나, ok를 했을 때만 특정 리소스를 생성한다던가..

-> 테라폼의 반복문과 조건문을 사용하면 된다고 한다 (다음 글에서 써 볼 예정)

 

또한 매번 달라지는 상황에 맞게 모듈을 만들었다곤 하지만, 실행할 때 마다 CIDR이나 변수들을 일일이 적어줘야 하는 일도 더 간편화시키고 싶다

-> 지금까지 생각한 것 중엔 csv 파일에다가 만들어야 할 리소스들의 정보들을 표로 만들어둔 후 그 파일을 받아와서 테라폼을 실행하는 것...인데 가능할지는 모르겠다

 

그리고 EC2는 어차피 매번 만들 때 마다 사용할 ami가 달라지기 때문에 굳이 모듈화를 하지 않았다

사용할 ami id만 따와서 실행시키는것도 생각해봤지만, aws managed가 아닌 marketplace의 ami들은 ec2를 직접 실행시키기 전까지 id를 확인할 수가 없다 (내가 못찾는거면 누군가 알려주길...)

-> CLI로는 확인할 수 있다는거같긴 한데, 뭔가 복잡해보인다 => 그거 찾아보고 변수 설정들 해서 테라폼 실행시킬 시간에 그냥 콘솔에서 만들어버리는게 훨씬 빠를 것 같아서 포기했다

 

 

728x90
728x90

댓글