5 minute read


본문요약

  1. 퍼블릭 서브넷: ALB / 프라이빗 서브넷: EC2 가 위치한 기본 구성
  2. 간단해 보여도, IaC tool(테라폼)으로 구성하려면 여러 리소스에 대한 설정이 필요
    • VPC network
      • VPC & Subnet CIDRs, igw & NAT, Routing tables, etc…
    • ALB
      • Subnet mapping, Listener rule & Target group, etc…
    • EC2
      • AMI, Instance type, Security groups, etc…
  3. 해당 구성을 자주 사용한다면, 어떻게 재사용성을 높일 수 있을까?
    • 필요 변경값만을 정의하고 변수화하여 해당 값 변경으로 새로운 세트 생성
    • 동일한 리소스 파일(=모듈)에 변수값만 다르게하여 빠른 복제 가능
  4. 단일 모듈 대신 다수의 작은 모듈간 연동으로 구성하고자 하였음
    • 일부 모듈만 사용하여 구성할 수 있도록
  5. 모듈간 연동문제 발생 > depend_on으로 해결
    • 모듈간 정보를 data source로 연동 시도
    • 모듈 호출시에는 필요한 data가 생성되지않아 에러 발생
    • depend_on으로 모듈간 의존성을 부여하여 해결 > 좋은 방법은 아님

프로젝트 관련 GIT Repository


!모듈 왜써요? / !모듈 왜 만들어써요?

noreason

출처: [TIG 카툰] 호드 50

공부하는데 이유가 어딨어? 그냥 하는거지

위의 본문요약에도 간단히 적어둔것처럼, 테라폼을 통해 생성 및 관리되는 리소스 중 반복되는 리소스가 많다면 모듈을 사용해 재사용성을 높일 수 있다.

일반적인 모듈의 장점은 Docs다른 블로그들에 잘 나와있고

작성해보면서 느낀 모듈을 써야하는 / 만들어 써야하는 이유는 아래와 같다.

  1. 변경할 부분만 바로 볼 수 있음
    • 몇십~몇백줄의 resource block을 매번 보는것은 부담스러움
    • module block으로 간단하게 관리가능
  2. 공개된 모듈을 사용자 환경에 맞게 개선
    • aws module과 같이 provider가 공식적으로 제공하는 모듈도 존재
    • 모듈로 제공되지 않는 리소스도 일부 존재 -> 모듈로 쓰려면 만들어서 써야함
    • 범용성을 위해 입력값으로 설정된 부분들(vpc/subnet/sg등)을 모듈안에 입력해둘 수 있음

중점적으로 고민했던 부분들

  • 어디까지 모듈로 만들고 각 모듈마다 어떤 리소스를 배정할것인가?
    • 한두번만 쓰고 말거면 모듈로 구성할 필요가 있는가?
      • Backend(S3 bucket, DynamoDB key)등은 별도 생성해서 복붙사용
    • 몇개의 모듈을 만들어서 사용할 것인가?
      • VPC, ALB, EC2 3개의 모듈 생성 및 연동
        • VPC-ALB-EC2 모두 묶은 1개의 모듈로도 구성 가능. But,
          • 기 VPC에 ALB-EC2 세트만 추가생성 할수도 있고
          • ALB 없이 퍼블릭에 EC2만 생성하고 싶을 수도 있음
    • 각 모듈마다 어떤 리소스가 배정되야하고 어느부분이 자주 변경되는가?
      • All: project_name, env_name, etc…
      • VPC: CIDRs(VPC,Subnet), etc…
      • ALB: sec_grps(ingress),target_grps, etc…
      • EC2: instance_type, amount, etc…
  • 해당 고민을 하고난 뒤 기존의 짜여진 모듈들을 보고 컨닝함.
  • 모듈간의 산출믈을 서로 어떻게 넘겨줄것인가
    1. Resource.local_file 사용
      • local_file 리소스를 사용해 별도 산출물 파일을 생성해 다른 모듈의 경로에 던져주기
      • ex)

          /modules/00network/nw-output.tf
        
          resource "local_file" "example {
            content = <<EOF
          variable "vpc_id" = "${aws_vpc.vpc.id}"
          이하 생략
          EOF
            filename = "../02ec2/nw_output.tf"
          }
        
        
    2. Module Output 사용
      • Accessing Module Output Values 모듈간 참조할 값을 output으로 뺴놓고, output을 타 모듈의 input으로 넣어주기
      • ex)

          /modules/00network/nw-output.tf
        
          output "vpc_id" {value = aws_vpc.vpc.id}
          이하 생략
        
          /dev/aaa/aaa-main.tf
        
          module "aaa-ec2" {
            vpc_id = module.aaa-network.vpc_id
            이하 생략
          }
        
        
    3. Tag 기반 Data source 사용
      • Dependency Inversion 모듈간 참조할 값을 개별 module마다 data source로 받아와서 사용
      • ex)

          /modules/02ec2/ec2-variable.tf
        
          data "aws_vpc" "this" {tags={Name="dev"}
        
          /modules/02ec2/ec2-template.tf
        
          resource "aws_instance" "this" {
            vpc_id = data.aws_vpc.this.id
          }
        
        
  • So?
    • 3번 채택
      • 개별 모듈안에는 template & variable만 남겨두고 싶음
      • 최상위 input에는 수동입력이 필요한것만 남기고 나머지는 다 눈에 안보이게 치워두고 싶음
      • 추가로 연동할 부분이 생긴다면, output 기반은 해당 리소스id/arn을 추가로 설정해 줘야하지만 data 기반으로하면 별도로 뺴낼필요 없이 기존의 data를 활용해서 사용할 수 있을것이라고 생각함.
  • 최선입니까? 확실해요?
    • 의존성 문제 발생 > depends_on으로 의존성 부여하여 해결
    • 모듈이 시작되는 시점에서 동시에 data를 긁어오다보니 모듈간 결과를 못받아오는 경우 발생
    1. apply를 여러번 적용
      • 주석 처리해놓고, 하나씩 주석해제해가면서 terraform apply -auto-approve && terraform apply -auto-approve && terraform apply -auto-approve …
      • 근본적인 해결책은 아닌듯함..
    2. 모듈 구조를 잘못 짠것에 대해서 반성하기
      • 어쩐지 예제들에서 모듈간 연계하기보다는 한모듈안에 때려넣더라
    3. vpc-alb-ec2 순으로 생성되도록 순서부여
      • 한 모듈 생성을 마치고 다음 모듈이 생성될 수 있도록
      • 전체적인 실행시간은 느려지겠지만 apply 한번에 생성되는 목적은 달성

구축 Architecture

221113

ALB 1개, EC2 2개로 구성된 간단한 구조

Project Structure

.
├── README.md
├── dev
│   ├── aaa
│   │   ├── aaa-main.tf
│   │   ├── aaa-variable.tf
│   │   └── aaa.auto.tfvars
│   └── bbb
│       └── WIP
├── prd
│   └── WIP
└── _modules
    ├── 00network
    │   ├── nw-variable.tf
    │   └── nw-template.tf
    ├── 01alb
    │   ├── alb-template.tf
    │   └── alb-variable.tf
    └── 02ec2
        ├── ec2-template.tf
        └── ec2-variable.tf

디렉토리별 상세 설명

/

최상위 디렉토리

각 환경들이 상호 영향을 주지않기 위해 디렉토리 단위로 구분되어 있음

/dev/aaa

aaa.auto.tfvars / aaa-variable.tf / aaa-main.tf 3개의 파일이 존재,
순서대로 리소스 생성에 필요한 값들을 전달하고/전달받으며 전체 모듈을 실행함

  1. aaa.auto.tfvars
    • 이후 파일들이 참조할 수 있도록 사용자 입력이 필요한 값들을 입력
    • 특정 파일명을 설정하면 Terraform은 자동으로 해당 파일 내부의 값을 변수로 입력
    • ex)
     # ------ 프로젝트 공통 설정 ------
     name_prefix = "aaa" // 프로젝트 명 입력
     env         = "dev" // 생성환경 입력
    
     # ------ Network 설정 ------
     vpc_cidr = "10.0.0.0/16" // VPC CIDR
     subnet_cidrs = {         // Subnets CIDR
       pub1 = "10.0.10.0/24"
       pub2 = "10.0.20.0/24"
       pub3 = "10.0.30.0/24"
       pri1 = "10.0.40.0/24"
       pri2 = "10.0.50.0/24"
     }
    
     #  ------ ALB 설정 ------
    
     # ALB의 Security group 설정
     alb_ports           = [80, 443]     // ALB ingress에 허용될 ports 입력
     alb_cidr_blocks     = ["0.0.0.0/0"] // ALB ingress에 허용될 cidr_block 입력
     alb_security_groups = []            // ALB ingress에 허용될 security_groups 입력
    
     # ------ EC2 설정 ------
    
     # EC2 관련 설정
     ec2_amount    = ["1", "2"] // 생성될 EC2 수량
     instance_type = "t2.micro" // 필요한 인스턴스 유형 입력
     volume_size   = "8"        // 필요한 루트 볼륨 사이즈 입력 (최소8)
    
  2. aaa-variable.tf
    • aaa.auto.tfvars의 변수 정의 값들을 받아 aaa-main.tf로 전달하는 중간 파일
    • default 등을 설정하여 기본값을 설정할 수 있음
    • ex)
     # ------ 프로젝트 공통 설정 ------
     variable "name_prefix" {}
     variable "env" {}
    
     # ------ Network 설정 ------
     variable "vpc_cidr" {}
     variable "subnet_cidrs" {}
    
     #  ------ ALB 설정 ------
     variable "alb_ports" {}
     variable "alb_cidr_blocks" {}
     variable "alb_security_groups" {}
    
     # ------ EC2 설정 ------
     variable "ec2_amount" {}
     variable "instance_type" {}
     variable "volume_size" {}
    
  3. aaa-main.tf
    • Child module을 가동하는 Root module
    • provider, backend 및 output 설정을 해두었음
    • ex)
     # aaa의 네트워크 설정
     module "aaa-network" {
       source       = "../../_modules/00network"
       name_prefix  = var.name_prefix
       env          = var.env
       vpc_cidr     = var.vpc_cidr
       subnet_cidrs = var.subnet_cidrs
     }
    
     # aaa의 ALB 설정
     module "aaa-alb" {
       source              = "../../_modules/01alb"
       name_prefix         = var.name_prefix
       env                 = var.env
       alb_ports           = var.alb_ports
       alb_cidr_blocks     = var.alb_cidr_blocks
       alb_security_groups = var.alb_security_groups
       depends_on          = [module.aaa-network]
     }
    
     # aaa의 EC2 설정
     module "aaa-ec2" {
       source        = "../../_modules/02ec2"
       name_prefix   = var.name_prefix
       env           = var.env
       ec2_amount    = var.ec2_amount
       instance_type = var.instance_type
       volume_size   = var.volume_size
       depends_on    = [module.aaa-alb]
     }
    
     # outputs
    
     output "alb_dns_name" { value = module.aaa-alb.alb_dns_name }
     output "ec2_private_ips" { value = module.aaa-ec2.ec2_private_ips }
    
/modules

modules 디렉토리
Root module의 설정에 따라 필요한 모듈들이 실행됨
현재 network, alb, ec2가 있고, 각 모듈별 템플릿과 Root에서 넘어온 변수값을 받는 변수 항목들이 설정됨

또한 아래의 예시처럼 앞선 모듈의 생성결과를 받아오기 위해서,
Name 태그 기반으로 필터링하여 필요한 데이터를 수신함

/modules/ec2/ec2-variable.tf

# 생성된 EC2가 지정될 ALB 대상그룹 조회
data "aws_lb_target_group" "alb" {
  name = "${var.env}-${var.name_prefix}-target-grp"
}

/modules/ec2/ec2-template.tf

resource "aws_lb_target_group_attachment" "ec2" {
  for_each         = toset(var.ec2_amount)
  target_group_arn = data.aws_lb_target_group.alb.arn
  target_id        = aws_instance.ec2[each.key].id
  port             = 80
}


생성 결과

trf-result

전체생성까지 약 8분이 소요
output으로 출력된 ALB로 CURL하면 EC2들이 응답하는것을 볼 수 있음


개선해야할 부분

  1. 디렉토리 구조 개선
    • 기본적으로 반복되는 부분을 모듈화 해야함
    • VPC/서브넷의 경우 S3버킷처럼 자주 생성되지 않기 때문에 별도로 분리 예정
  2. Data source 걷어내고 output 사용
    • ~.auto.tfvars로 필요값만 수정할 것이기에 ~-main.tf 안에 다른 모듈 output을 넣어도 눈에 안띔
    • 모듈들이 동시에 작동하면서 생성시간도 빨라질 것이라 기대
  3. 반복 생성되는 부분의 코드 단축
    • 스터디 초기에 짜놨던 서브넷의 경우 필요한 만큼 반복하면서 생성
    • 추후 count, for_each, 등 반복문 숙달되고 정리
  4. 모듈 내 더 많은 리소스 포함
    • 단순히 ALB, EC2 생성 및 연동만 구축하였음
    • Route53 Record 등록, 80>443 HTTPS 적용, EC2 추가볼륨 생성 및 마운트 등 모듈 내 더 많은 리소스 자동화 가능

Categories:

Updated:

Leave a comment