• AWS 테라폼[Terraform] 모듈(Module) 형식 구조 기초

    2025. 2. 22.

    by. Daramu

    윈도우에서 Terraform 및 aws cli에 대한 명령어 설정을 하고 싶다면 아래의 포스팅 참고

    https://daramu.tistory.com/68

     

    윈도우11 명령어 설치 - 테라폼[Terraform], AWS CLI, AWS IAM Authenticator

    본격적으로 Argo CD를 사용하기 전에, 몇가지 필요한 준비물이 있다. 0. AWS 계정1. Terraform2. AWS CLI3. AWS IAM Authenticator4. 코드 편집을 위한 편집기( VSCode, WEBStorm etc....) 윈도우 도커 데스크탑의 쿠버네

    daramu.tistory.com

     

    Terraform?

    간단하게 말하자면 코드를 통한 인프라 관리를 위한 서비스이다.

    기존의 콘솔에서 클릭을 통해 이루어지던 것을 IaC(Infra as Code)를 통해 코드로 관리를 하겠다는 것이다.

     

    예시로는 쿠버네티스의 yaml이 있겠다.

    쿠버네티스에서 kubectl run test --image=nginx.. 등을 통해 인프라를 올릴 수 있지만, 이것을 "코드"로 남길 수 있다.

    yaml 파일에 apiVersion, kind, metadata 등의 작업을 통해 yaml 로 만든 파일을 kubectl apply -f {name} 으로 올릴 수 있듯이, 일종의 선언처럼 동작하던 인프라(ex. aws, ncp, gcp....)를 코드를 통해 관리하겠다는 취지이다.

     

    Terraform(이하 테라폼)은 HCL이라는 언어로 동작한다.

    굳이  yaml이나 json과 같이 기계도, 사람도 이해하기 편한 언어를 사용하지 않고 HCL이라는 별도의 언어를 사용하는 것은 구분을 위해서이다. 가령 json 으로 인프라를 관리하게 되면 중괄호({})가 엄청 사용될 것인데, 별도의 언어를 통해 이것을 사용자 친화적으로 구성할 수 있는 것이다.

     

    확장자는 .tf를 사용하며, 윈도우 환경의 경우 위의 포스팅에 있듯이 terraform.exe 파일을 통해 동작시킬 수 있다.

    Linux의 경우 훨신 간단하게 install 을 통해 가능하다.

     

    그런 테라폼은 구성하는 방식이 몇가지가 있다.

    우선 가장 간단한 모든 구성요소를 하나의 파일로 두는 것이다.

    provider "aws" {
      region = "us-west-2"  # 원하는 리전을 입력하세요.
    }
    
    resource "aws_vpc" "main_vpc" {
      cidr_block = "10.0.0.0/16"
      enable_dns_support = true
      enable_dns_hostnames = true
      tags = {
        Name = "main_vpc"
      }
    }
    
    resource "aws_subnet" "main_subnet" {
      vpc_id     = aws_vpc.main_vpc.id
      cidr_block = "10.0.1.0/24"
      availability_zone = "us-west-2a"  # 원하는 가용 영역을 입력하세요.
      tags = {
        Name = "main_subnet"
      }
    }
    
    resource "aws_internet_gateway" "main_igw" {
      vpc_id = aws_vpc.main_vpc.id
    ....

     

    이 처럼 모든 인프라 구성을 하나의 파일로 관리한다는 것은 좋은 취지이지만,

    이 같은 구성은 인프라의 규모가 커지면 커질수록 관리가 어려워진다는 단점이 있다.

    반대로 관리해야하는 파일은 단 하나이기 때문에 테라폼 관리자(보통 인프라 팀에서 한다)의 입장에서는 편할 수 있다.

     

    하지만 말했듯이 인프라의 규모가 커지거나, 혹은 사용하는 서비스의 갯수가 많아지면 찾는 것도 일이 된다.

    요컨데 모든 코드가 하나로 몰려있는 상황과 유사한 것이다.

     

    일반적으로 코드에서도 각 역할별로 클래스를 나누듯이 테라폼또한 각 역할별로 각각의 모듈(module)로 나눠 구성할 수 있다.

    여기서는 간단하게 vpc와 subnet하나씩을 만들어 볼 것이다.

    구성은 전체적으로 비슷하니 AWS의 테라폼 공식 사이트를 통해 추가하거나 삭제하는 등의 작업이 문제 없을 것으로 기대한다.

     

    우선 provider.tf를 구성한다.

    ap-northeast-2는 아시아-서울 리전이다.

    #provider.tf
    
    terraform {
      required_providers {
        aws = {
          source  = "hashicorp/aws"
          version = "~> 5.0"
        }
      }
    }
    
    # AWS Provider 설정
    provider "aws" {
      region = "ap-northeast-2"
    }

     

    그리고 구성을 해야하는데, 모듈 형식은 구성 방법이 여러가지이다.

    여기서는 modules폴더 안에 각 vpc, subnet의 폴더를 만들고, 해당 폴더 안에 {name}.tf를 구성하는 방식을 택한다.

    모듈 형식 eks와 key는 무시한다.

    마찬가지로 모듈에 대한 이해가 있다면 굳이 따라할 필요가 없다.

    심지어 모듈안의 파일조차도 해당 포스트에서는 {name}.tf이지만, main.tf를 두고 모두 하나의 파일에서 관리해도 상관없다.

     

    아무튼 provider.tf를 생성하였다면, 이제는 각 모듈을 만들 차례이다.

    원하는 리소스의 폴더를 생성한다. 여기서는 vpc와 subnet폴더를 modules 폴더 안에 생성한다.

     

    modules

    ㄴvpc

    ㄴsubnet

     

    그렇게 진행한 이후에, 안에 파일을 생성한다.

    우선 vpc먼저 생성한다.

    #test-vpc.tf
    
    resource "aws_vpc" "test_vpc" {
      cidr_block           = var.test_vpc_cidr
      enable_dns_support   = true
      enable_dns_hostnames = true
    
      tags = {
        Name        = "test-vpc"
      }
    }
    
    output "test_vpc_id" {
      description = "The ID of test-vpc"
      value       = aws_vpc.test_vpc.id
    }

    output을 별도의 파일로 만들지 않고 resourde 리소스로 함께 둔 이유는 어차피 각각 개별의 resource 파일이 생길 것이기 때문에 안에 내용물이 많지 않을 것이라는 짐작 때문이다.

    이것이 싫다면 말했듯이 정해진 규격은 없으니 별도의 output.tf 로 빼도 좋고, 아니면 아예 vpc 폴더안에 test-vpc 라는 폴더를 만들고, 그 안에 main.tf, variabel.tf, output.tf etc.. 등등의 파일을 넣어도 좋다.

     

    아무튼 test-vpc.tf 파일을 만들었다면 이제는 variable.tf 파일을 생성한다.

    #variable.tf
    
    variable "test_vpc_cidr" {
      description = "CIDR block for test-vpc"
      type        = string
    }

     

    여기서 vpc의 cidr값이 들어가지 않는다.

    넣어도 되지만 최대한 변수를 중앙에서 관리하고, 각 모듈은 중앙에서 정해진 변수를 "받아"사용하는 형식을 사용했다.

    여기까지 생성했다면 아래의 구성이 되어있을 것이다.

     

    이제 앞에서 말했듯이 "중앙에서 관리하는 변수"를 실행하기 위해 최상위 폴더(provider.tf 가 존재하는)에 root.tf 파일을 만든다.

    그리고 아래의 내용을 채워 넣는다.

    module "vpc" {
      source = "./modules/vpc"
      test_vpc_cidr = "10.0.0.0/16"
    }

     

    보면 module vpc의 source는 ./modules/vpc로 되어있다. 폴더명이나 위치를 다르게 했다면 맞춰준다.

    그리고 root.tf에서 test_vpc_cidr을 정해준다.

     

    그럼 흐름은 아래처럼 진행된다.

     

    1. root.tf에서 vpc모듈로 test_vpc_cidr(10.0.0./16)을 전달한다.

    2. vpc 모듈 안에 variable.tf 에서 test_vpc_cidr을 받는다.

    3. 해당 variable.tf에서 받은 정보를 test-vpc.tf에 집어 넣는다.

     

    그렇기에 root.tf에서 입력된 test_vpc_cidr 변수가 variable.tf에 똑같이 있는 모습을 확인할 수 있다.

    그렇게 받은 변수는 test-vpc.tf파일에서 var.test_vpc_cidr 이라는 값으로 사용되고 있다.

    즉, 위 처럼 했다면 "cidr_block  = "10.0.0.0/16"" 이 입력된 것과 동일한 상황인 것이다.

     

    이렇게 되었다면 vpc는 되었다.

    이제 subnet을 생성할 차례이다.

     

    subnet은 알아야 할 것이, vpc_id가 들어간다.

    바로 위에서 생성한 vpc의 고유한 값(id)인 것이다.

    위의 흐름은 root.tf에서 생성된(혹은 입력된)값이 모듈로 가는 구성을 나타낸다.

     

    하지만 이번에는 반대이다. vpc에서 생성된 값이 subnet에 들어가야한다.

    아쉽게도 테라폼에는 vpc에서 생성된 값을 바로  subnet에 넣지 않는다. 지원도 안할 뿐 더러, 그렇게 된다면 vpc와 subnet이 "단단한 결합" 상태가 되기에 피해야하는 상황이다.

     

    자바 개발자라면 느슨한 결합, 단단한 결합의 차이가 무엇일지 쉽게 알 수 있을 것이다.

     

    아무튼 vpc에서 생성된 id값을 subnet에 넣는다면,

    root.tf에서 subnet에 변수로 넣는 작업을 진행한다. 아까도 말했지만, 중요한 것은 중앙에서 관리하는 구성이다.

     

    기초 준비는 되어있다.

    vpc 모듈의 test-vpc.tf 파일에 output으로 test_vpc_id가 있는 것을 볼 수 있다.

    그럼 이 값을 root.tf 에서 받으면 된다.

     

    #root.tf
    
    module "vpc" {
      source = "./modules/vpc"
      test_vpc_cidr = "10.0.0.0/16"
    }
    
    module "subnet" {
      source = "./modules/subnet"
    
      test_vpc_id       = module.vpc.test_vpc_id      # test-vpc에서 출력된 vpc1_id를 전달
      test_subnet_cidr = "10.0.1.0/24"
      test_subnet_az   = "ap-northeast-2a"
    }

     

    순서는 아래와 같다.

    1. test-vpc에서 output을 통해 최상위 root.tf로 test_vpc_id를 노출

    2. root.tf에서 노출된 값을 받아 module.vpc.test_vpc_id라는 이름으로 test_vpc_id 변수에 할당

     

    이렇게 되었다면 다시 논의는 최초 root.tf -> 모듈로 내려가는 것이 된다.

    여기서는 root.tf에서 subnet의 ip와 az까지 설정하였다.

     

    그렇게 설정한 값을 모듈에서 variable.tf로 받는다.

    #modules/subnet/variable.tf
    
    variable "test_vpc_id" {
      description = "ID of test-vpc"
      type        = string
    }
    
    variable "test_subnet_cidr" {
      description = "CIDR block for test-vpc's Subnet IP"
      type        = string
    }
    
    variable "test_subnet_az" {
      description = "Availability Zone for test-vpc's Subnet"
      type        = string
    }

     

    그리고 이 변수를 test-subnet.tf에 넣는다.

    #test-subnet.tf
    
    resource "aws_subnet" "test_subnet" {
      vpc_id            = var.test_vpc_id
      cidr_block        = var.test_subnet_cidr
      availability_zone = var.test_subnet_az
    
      tags = {
        Name        = "test-subnet"
      }
    }
    
    output "vpc1_subnet1_id" {
      description = "The ID of test-vpc's Subnet"
      value       = aws_subnet.test_subnet.id
    }

     

    위처럼 진행했다면, 최종적으로 아래의 구성이 된다.

     

    다시 말하지만, eks와 key는 무시한다.

    준비는 되었다.

    그럼 이제 terraform이 설치 되었다면 윈도우 기준 PowerShell에서 "terraform init"을 실행한다.

     

    여기서 막힌다면 "aws configure"을 통해 연결을 진행한다.

    굳이 거기까지는 적지 않겠다. 그냥 key를 만들고 넣으면 된다. 싫다면 IAM을 사용하든 그것은 자유이다.

     

     

    정상적으로 되었다면, 안내 되어있듯이 "terraform plan"을 실행한다.

     

     

    정상적으로 되었다면, 위처럼 2개의 리소스(vpc, subnet) 이 추가되는 것인데, 실행을 위해 "terraform apply"를 입력하라 한다.

    따라 쳐준다.

     

     

    진짜 맞냐고 물어본다.

    yes를 입력해준다.

    그럼 이렇게 하나씩 실행시켜주는 모습을 볼 수있다.

     

    그리고 콘솔로 확인을 한다면?

     

    vpc와 subnet이 생성되었다!

    이제는 위의 코드를 응용하여 여러가지 서비스를 넣어 모듈 형식으로 구현하면 된다!

     

     

    댓글