Terraform 기본 인프라 구축하기

ImOk·2022년 11월 10일
1

Terraform

목록 보기
1/4
post-thumbnail

시작하기 앞서..

CloudNet@ Terraform Study 101 을 진행하며 학습한 내용을 바탕으로 업무에 활용한 내용을 작성했습니다.
따라서 AWS와 terraform에 대한 기본 개념 내용은 생략한 경우가 많습니다.

Terraform 스터디를 시작한 계기

클라우드 서비스 공급자인 AWS는 고객이 인프라 구성을 간편하게 할 수 있도록 웹 콘솔을 지원하고 있습니다.

성향에 따라 이런 GUI 환경을 좋아하시는 분들이 있지만,
저 같은 경우는 반복적인 작업이 필요한 경우 휴먼 에러를 방지할 수 있고,
생성하고자 하는 리소스를 한눈에 파악하기 쉽기 때문에 콘솔 작업보다 코드의 작업을 더 선호하는 편입니다.

처음 AWS를 접하고 VPC, EC2, SG, ELB과 같은 기본 서비스를 생성하고자 하면 어려울 수 있지만,
한번 익히고 나면 지루한 반복 작업으로 느껴질 수 있습니다.

따라서 저는 간단하지만 자주 사용하는 기본 인프라 구성을 코드형 인프라(Infrastructure as Code, IaC)중 하나인 Terraform으로 작성해 업무에 활용하고자 스터디를 시작하게 되었습니다.

⚠️ 본 내용은 개인적으로 Terraform을 처음 사용하면서 활용한 내용이기 때문에 틀릴 수 있습니다.

TL;DR: 노가다 하기 싫어서 Terraform 시작함 🙂 근데 너무 어렵다.. 그치만 재밌다


인프라 구축 전 체크 사항

A 기업에서 새로운 프로젝트 도입에 앞서 AWS에 테스트 환경을 구축하고자, 주니어 사원 👶🏻 OK에게 다음과 같은 인프라를 생성해줄 것을 요청했다고 가정해봤습니다.

인프라 구축 요구 사항 체크 ✅

  1. 서비스 개발을 위해 사용할 서버는 Private Subnet 환경으로 구성

  2. 서버는 개발(Dev) 환경과 운영(Prod) 환경으로 나눠서 구성 필요

    • 나중에 서버 환경은 추가될 수 있음
    • 운영 서버 환경은 고가용성을 위해 2개의 가용영역(AZ)으로 이중화 구성
  3. SG(Security Group)에 다양한 Inbound Source 정보 등록 필요

    • 환경에 따라 Port 및 Source 정보는 달라질 수 있음
  4. ALB에 HTTPS로 리스너 설정해 SSL Offload 수행

    • ALB는 개발 환경과 서비스별로 구분해서 생성
    • Target Group port는 언제든 변경, 추가될 수 있음

👶🏻 OK는 요구 사항을 확인하고, Terraform 구성 전에 미리 구축 내용을 작성했습니다.

구축 내용 ✅

1. VPC(Virtual Private Cloud)

  • IP 대역 : 10.60.0.0/16
  • On-Premise 환경에서 AWS의 Private Subnet 환경에 접속하기 위해, 인터넷 게이트웨이NAT 게이트웨이로 구성

2. Subnet 구성

  • Region : ap-northeast-2 [Asia Pacific (Seoul)]

    EnvAZSubnetIPv4 CIDR
    Commonap-northeast-2aimok-corp-pub-sub-2a10.60.4.0/24
    Commonap-northeast-2cimok-corp-pub-sub-2c10.60.5.0/24
    Devap-northeast-2aimok-corp-dev-pri-sub-2a10.60.0.0/24
    Devap-northeast-2cimok-corp-dev-pri-sub-2c10.60.1.0/24
    Prodap-northeast-2aimok-corp-prd-pri-sub-2a10.60.2.0/24
    Prodap-northeast-2cimok-corp-prd-pri-sub-2c10.60.3.0/24

3. SG(Security Group)

  • EC2 접속을 위한 On-Premise의 IP Inbound Source 정보 (IP는 보안을 위해 임의로 생성했습니다.)

    CIDRDesc
    18.164.239.93/32ImOK Tower wireless ip
    111.245.120.118/32ImOK Tower lan
    179.245.107.211/32ImOK Corp VPN

4. EC2

  • Private EC2에 ssh 접속은 Bastion Host를 통한 Tunneling 방식으로 접속

  • OS : Amazon Linux 2

    구분용도NameCPUMemoryDiskType
    CommonBastion Hostimok-corp-bastion22GiB10GiBt3.small
    DevManagementimok-corp-dev-management-2a216GiB100GiBr5.large
    DevServiceimok-corp-dev-service-2a24GiB50GiBt3.medium
    PrdManagementimok-corp-prd-management-2a416GiB100GiBt3.xlarge
    PrdServiceimok-corp-prd-service-2a416GiB50GiBt3.xlarge
    PrdServiceimok-corp-prd-service-2c416GiB50GiBt3.xlarge

5. ALB(Application Load Balancer)

ALBLB Listener PortTarget Group portTarget Instance
imok-corp-dev-alb-management80 -> 443 Redirect8080imok-corp-dev-management-2a
imok-corp-dev-alb-service80 -> 443 Redirect8085, 8086imok-corp-dev-service-2a
imok-corp-prd-alb-management80 -> 443 Redirect8080imok-corp-prd-management-2a
imok-corp-prd-alb-service80 -> 443 Redirect8085, 8086imok-corp-prd-service-2a
imok-corp-prd-service-2c

6. ACM(AWS Certificate Manager)

  • AWS ACM 발급을 위해 외부 도메인 호스팅 업체에 DNS 검증 과정을 진행
  • 검증 완료 후 Status : ✅ Issued 상태

👶🏻 OK는 다음과 같이 구축하게 될 예상 아키텍처를 그려봤습니다.

구축 예상 아키텍처 ✅


인프라 구축

👶🏻 OK는 향후 비슷한 업무를 맡게되었을 때, 미리 작성해 놓은 Terraform 코드를 재가공해 빠르게 인프라를 구축하고자 하는 목적이 있었습니다. 따라서 다음과 같은 목표를 세웠습니다.

💡 재사용 가능한 코드 작성을 위해 서비스별로 코드를 분리하기
💡 최대한 서비스 구축을 위한 코드는 수정하지않고, 요구사항의 변화에 맞춰 variables 파일만 수정해 사용하기


Terraform 사용 준비

terraform은 다음과 같은 workflow로 동작합니다.


https://learn.hashicorp.com/tutorials/terraform/infrastructure-as-code?in=terraform/aws-get-started

AWS credentials

저는 업무 특성상 여러 AWS 계정을 다뤄야하기 때문에 profileaws configure를 관리하고 있습니다.
따라서 export AWS_PROFILE="imok"로 아래와 같이 profile을 미리 정의하고 시작합니다.

Variables

보통 테라폼 코드를 작성할 때 variables.tf 파일을 만들어 해당 파일에 변수를 저장합니다.
company명, service명, service port 등 인프라 생성 시에 바뀔 수 있는 부분을 변수로 지정해 사용했습니다.

terraform.tfvars

정의한 변수에 값을 주입하기 위해 가장 일반적인 방법은 terraform.tfvars 파일을 생성하는 것입니다. variable = value 형태로 정의합니다.
저 같은 경우는 보안그룹에 추가해야할 Inbound Source 정보를 모두 terraform.tfvars 파일에 정의해 놓고 사용했습니다.

# terraform.tfvars파일 작성 예시
bastion_ingress_rules = [
  {
    from_port = "22",
    to_port   = "22",
    cidr      = "18.164.239.93/32"
    desc      = "ImOK Tower wireless ip"
  },
  {
    from_port = "22",
    to_port   = "22",
    cidr      = "111.245.120.118/32"
    desc      = "ImOK Tower lan"
  }
]

AWS ACM의 경우 보통 기업의 상황에 따라 도메인 호스팅 업체에 등록 후 사용하는 경우가 많기 때문에, Terraform 코드로 생성하는 것이 아닌, AWS Console에서 생성 후 ARN을 tfvars에 등록하는 방법을 택했습니다.

terraform init

terraform init 명령어를 실행하면, local의 현재 디렉터리 아래에 아래 캡처 화면과 같이 미리 선언된 프로바이더(AWS) 플러그인을 설치해 줍니다.

init 작업을 완료하면, 테라폼의 꽃🌺인 .tfstate 파일과 .tfstate에 정의된 내용을 담은 .terraform 파일이 생성됩니다.
.tfstate 파일은 상태 저장을 위한 파일로, 기존에 다른 개발자가 이미 .tfstate에 인프라를 정의해 놓은 것이 있다면, 다른 개발자는 init작업을 통해서 local에 sync를 맞출 수 있습니다.

.terraform
├── environment
├── providers
│   └── registry.terraform.io
│       └── hashicorp
│           └── aws
│               └── 4.38.0
│                   └── darwin_arm64
│                       └── terraform-provider-aws_v4.38.0_x5
└── terraform.tfstate

이제 인프라 생성을 위한 초기 준비가 완료됐습니다.


Terraform 구성

저는 다음과 같이 10개의 영역으로 구분해 코드를 작성했습니다.

terraform_study
├── alb.tf
├── ami.tf
├── ec2.tf
├── key-pair.tf
├── output.tf
├── provider.tf
├── sg.tf
├── terraform.tfvars
├── variables.tf
└── vpc.tf

Provider

  1. variables.tf 파일 구성
    profile에 쓰일 account와 region을 미리 variables에 정의했습니다.

    variable "account" {
      default     = "imok"
      description = "aws account"
    }
    
    variable "region" {
      type    = string
      default = "ap-northeast-2"
    }
  2. provider.tf 파일 구성

    provider "aws" {
      region  = var.region
      profile = var.account
    }

key-pair Resource

  1. variables.tf 파일 구성
    key에 붙는 이름은 기업명, 프로젝트명에 따라 달라지기 때문에 tags를 미리 variables에 정의했습니다.

    variable "tags" {
      type        = string
      default     = "imok-corp"
      description = "Additional company tags"
    }
  2. key-pair.tf 파일 구성

    # Generates a secure private key and encodes it as PEM
    resource "tls_private_key" "key_pair" {
      algorithm = "RSA"
      rsa_bits  = 4096
    }
    # Create the Key Pair
    resource "aws_key_pair" "key_pair" {
      key_name   = "${var.tags}-key"
      public_key = tls_private_key.key_pair.public_key_openssh
    }
    # Save Pem Key
    resource "local_file" "ssh_key" {
      filename = "${aws_key_pair.key_pair.key_name}.pem"
      content  = tls_private_key.key_pair.private_key_pem
    }

VPC Resouece

  1. variables.tf 파일 구성
    vpc에 사용할 ip 대역대와 subnet 대역대, az 정보를 미리 variables에 정의했습니다.

    variable "aws_az" {
      type    = list(any)
      default = ["ap-northeast-2a", "ap-northeast-2c"]
    }
    
    variable "aws_az_des" {
      type    = list(any)
      default = ["2a", "2c"]
    }
    
    variable "vpc_cidr" {
      type    = string
      default = "10.60.0.0/16"
    }
    
    variable "dev_private_subnet" {
      type    = list(any)
      default = ["10.60.0.0/24", "10.60.1.0/24"]
    }
    
    variable "prd_private_subnet" {
      type    = list(any)
      default = ["10.60.2.0/24", "10.60.3.0/24"]
    }
    
    variable "public_subnet" {
      type    = list(any)
      default = ["10.60.4.0/24", "10.60.5.0/24"]
    }
  1. vpc.tf 파일 구성
    subnet은 보통 az 2개의 쌍으로 구성하기 때문에, 코드의 반복을 줄이고자 count 라는 메타 변수를 사용했습니다.
    variables에서 정의한 aws_az의 개수가 2 이기 때문에, length 함수를 이용해 길이를 계산해 count 변수에서 정의했습니다.
    따라서 count = length(var.aws_az) 로 count = 2가 되어 총 2개의 리소스가 생성됩니다.

    count가 설정된 스탠자(stanza)에서 index 라는 객체를 사용할 수 있기 때문에, variables에서 정의한 public_subnet 변수에 count.index를 사용했습니다.

    💡 저는 terraform 초보이고 생성할 리소스가 2개뿐이어서 count를 사용했지만, 생성할 리소스가 많을 때는 for each 문을 사용하는 것이 더 올바른 방법이라는 의견이 많습니다. 따라서 좀 더 학습 후 for each 문으로 변경해볼 예정입니다.

    2022.12.11 for_each 문으로 변경 완료 😘 for_each 활용해 vpc 생성하기

    ## vpc
    resource "aws_vpc" "vpc" {
      cidr_block           = var.vpc_cidr
      enable_dns_support   = true
      enable_dns_hostnames = true
    
      tags = {
        Name = "${var.tags}-vpc"
      }
    }
    ## Subnet
    # public subnet
    resource "aws_subnet" "pub_sub" {
      count                   = length(var.aws_az)
      vpc_id                  = aws_vpc.vpc.id
      cidr_block              = var.public_subnet[count.index]
      availability_zone       = var.aws_az[count.index]
      map_public_ip_on_launch = false
    
      tags = {
        Name = "${var.tags}-pub-sub-${var.aws_az_des[count.index]}"
      }
    }

EC2 AMI data 정의

OS 요구에 따라 다양한 EC2 AMI를 활용하기 위해 최신 AMI 정보를 미리 구성했습니다.

1. Amazon Linux2

# amazon linux2 ami latest
data "aws_ami" "amazon-linux-2" {
  most_recent = true
  owners      = ["amazon"]

  filter {
    name = "name"
    # values = ["amzn2-ami-hvm*"]
    values = ["amzn2-ami-hvm-*-gp2"]
  }
  filter {
    name   = "root-device-type"
    values = ["ebs"]
  }
  filter {
    name   = "architecture"
    values = ["x86_64"]
  }
  filter {
    name   = "virtualization-type"
    values = ["hvm"]
  }
}

2. Amazon Linux2 kernel-5

# amazon linux2 ami latest kernel-5
data "aws_ami" "amazon-linux-2-kernel-5" {
  most_recent = true
  owners      = ["amazon"]

  filter {
    name   = "name"
    values = ["amzn2-ami-kernel-5*"]
  }
}

3. Ubuntu

# ubuntu ami latest
data "aws_ami" "ubuntu_latest" {
  most_recent = true
  owners      = ["099720109477"] # Canonical(owner account id)

  filter {
    name   = "name"
    values = ["ubuntu/images/hvm-ssd/ubuntu-xenial-16.04-amd64-server-*"]
    # values = ["ubuntu/images/hvm-ssd/ubuntu-xenial-18.04-amd64-server-*"]
    # values = ["ubuntu/images/hvm-ssd/ubuntu-xenial-22.04-arm64-server-*"]
  }
  filter {
    name   = "virtualization-type"
    values = ["hvm"]
  }
}

EC2 Resouece

EC2의 인스턴스 타입을 결정할 때 보통 cpu 와 memory 스펙을 정하고 워크로드에 적합한 타입을 선정합니다.
저는 아래 명령어를 통해 인스턴스 타입을 결정하는 편입니다.

aws ec2 describe-instance-types --filters \
"Name=current-generation,Values=true" \
"Name=memory-info.size-in-mib,Values=4096" \
"Name=vcpu-info.default-vcpus,Values=2" \
--query "InstanceTypes[*].[InstanceType]" --output text | sort

  1. ec2.tf 파일 구성
    요구사항에 맞춰 미리 위에서 정의한 ami 중 최신 amazon linux2의 이미지를 선택했고, 인스턴스 타입, 볼륨 등을 설정했습니다.

    ## EC2
    # bastion
    resource "aws_instance" "bastion" {
      ami                    = data.aws_ami.amazon_linux2_kernel_5.id
      instance_type          = "t3.small"
      key_name               = aws_key_pair.key_pair.key_name
      subnet_id              = element(aws_subnet.pub_sub.*.id, 0) # public-subnet-2a
      vpc_security_group_ids = [aws_security_group.bastion_sg.id]
    
      root_block_device {
        volume_type = "gp3"
        volume_size = "10"
      }
    
      tags = {
        Name = "${var.tags}-bastion"
      }
    }
    
    # dev-management
    resource "aws_instance" "dev_management_2a" {
      ami                    = data.aws_ami.amazon_linux2_kernel_5.id
      instance_type          = "r5.large"
      key_name               = aws_key_pair.key_pair.key_name
      subnet_id              = element(aws_subnet.dev_pri_sub.*.id, 0) # dev-private-subnet-2a
      vpc_security_group_ids = [aws_security_group.dev_management_sg.id]
    
      root_block_device {
        volume_type = "gp3"
        volume_size = "100"
      }
    
      tags = {
        Name = "${var.tags}-dev-management-2a"
      }
    }

SG Resouece

  1. variables.tf 파일 구성
    SG에서 사용하기 위한 ingress rules을 미리 variables에 정의했습니다.
    default를 비워둔 이유는 위에서 언급한 terraform.tfvars 파일에 정의한 변수를 사용하기 위함입니다.

    variable "bastion_ingress_rules" {
      type        = list(map(string))
      default     = []
      description = "bastion sg rule"
    }
    
    variable "service_ingress_rules" {
      type        = list(map(string))
      default     = []
      description = "service sg rule"
    }
    
    variable "management_ingress_rules" {
      type        = list(map(string))
      default     = []
      description = "management sg rule"
    }
  2. sg.tf 파일 구성
    aws_security_group 리소스의 내부에 ingress block을 생성하는 데 dynamic block을 활용했습니다.
    for_each에서 block을 생성할 정보를 담은 for 표현식을 사용한 collection 을 전달받고, 이 collection 의 item 수만큼 block이 생성됩니다.

    for 표현식 : [for <ITEM> in <LIST> : <OUTPUT>]

    content는 실제로 정의하려는 block 안에 전달되는 값들을 명시하는 곳입니다. 따라서 원래 ingress block을 생성할 때 필요한 값을 넣어줍니다.

    ## Security group
    # bastion-sg
    resource "aws_security_group" "bastion_sg" {
      # count = length(var.bastion_ip)
      name        = "${var.tags}-bastion-sg"
      description = "${var.tags}-bastion-sg"
      vpc_id      = aws_vpc.vpc.id
    
      # inbound rule
      dynamic "ingress" {
        for_each = [for s in var.bastion_ingress_rules : {
          from_port = s.from_port
          to_port   = s.to_port
          desc      = s.desc
          cidrs     = [s.cidr]
        }]
        content {
          from_port   = ingress.value.from_port
          to_port     = ingress.value.to_port
          cidr_blocks = ingress.value.cidrs
          protocol    = "tcp"
          description = ingress.value.desc
        }
      }
    
      # outbound rule
      egress {
        from_port   = 0
        to_port     = 0
        protocol    = "-1"
        cidr_blocks = ["0.0.0.0/0"]
      }
    
      tags = {
        Name = "${var.tags}-bastion-sg"
      }
    }

ALB Resouece

ALB 구성이 개인적으로 제일 힘들었습니다. 처음엔 ALB의 Listener rule의 조건을 코드로 모두 작성하려고 했습니다. 하지만 아래 캡처와 같이 생각해야 할 분기 조건들이 너무 많았고, 처음부터 코드를 간결히 작성하는 데에만 집중해 count와 list로 해결하려다 보니 의존성에 문제가 생겨 오류가 계속 발생했습니다. 결국 룰 분기 조건은 콘솔창에서 작성하는 게 더 빠를 것 같다는 판단을 내렸습니다.

제 개인적인 생각으로는 ALB는 수고스럽지만, 코드가 반복되더라도 하나하나 작성하는 게 더 좋지않았을까라는 생각을 합니다.
(아직 제가 좋은 방법이 있지만 찾지 못해서 그럴 수도..더 좋은 방법을 찾으면 개선하겠습니다. 🙇🏻‍♀️)

  1. variables.tf 파일 구성
    ALB에서 Target Group에 사용하기 위한 서비스 포트 및 certificate arn을 미리 variables에 정의했습니다.

    variable "env" {
      type        = list(any)
      default     = ["dev", "prd"]
      description = "Additional env tags"
    }
    
    variable "service_server_port" {
      type    = list(any)
      default = [8085, 8086]
    }
    
    variable "management_server_port" {
      type    = list(any)
      default = [8080]
    }
    
    variable "acm_certi" {
    type    = string
    default = ""
    }
  2. alb.tf 파일 구성

  • alb는 개발 환경에 따라 구성돼야하기 때문에 variables에 정의한 환경 변수의 개수만큼 생성되도록 했습니다.

    # ALB 생성
    resource "aws_lb" "service_alb" {
      count              = length(var.env)
      name               = "${var.tags}-${var.env[count.index]}-alb-service"
      load_balancer_type = "application"
      security_groups    = [aws_security_group.service_alb_sg.id]
      subnets            = [element(aws_subnet.pub_sub.*.id, 0), element(aws_subnet.pub_sub.*.id, 1)]
    
      tags = {
        Name = "${var.tags}-${var.env[count.index]}-alb-service"
      }
    }
  • alb의 target group을 생성하고, target인 인스턴스를 붙입니다.

    ## ALB target group & attachment
    # dev service_tg
    resource "aws_lb_target_group" "dev_service_tg" {
      count    = length(var.service_server_port)
      name     = "${var.tags}-${var.env[0]}-service-tg-${var.service_server_port[count.index]}"
      port     = var.service_server_port[count.index]
      protocol = "HTTP"
      vpc_id   = aws_vpc.vpc.id
    }
    
    resource "aws_alb_target_group_attachment" "dev_service_tg_attach_2a" {
      count            = length(aws_lb_target_group.dev_service_tg)
      target_group_arn = element(aws_lb_target_group.dev_service_tg.*.arn, count.index)
      target_id        = aws_instance.dev_service_2a.id
      port             = var.service_server_port[count.index]
    }
  • ALB listener를 http와 https로 나눠서 생성 후, https의 리스너 설정에서 terraform.tfvars에 정의한 AWS ACM ARN을 사용했습니다.

    ## ALB listener Redirect Action
    # service alb listener_http
    resource "aws_lb_listener" "service_alb_listener_http" {
      count             = length(var.env)
      load_balancer_arn = element(aws_lb.service_alb.*.arn, count.index)
      port              = 80
      protocol          = "HTTP"
    
      default_action {
        type = "redirect"
        redirect {
          port        = "443"
          protocol    = "HTTPS"
          status_code = "HTTP_301"
        }
      }
    }
    
    # service alb listener_https
    resource "aws_lb_listener" "service_alb_listener_https" {
      count             = length(var.env)
      load_balancer_arn = element(aws_lb.service_alb.*.arn, count.index)
      port              = 443
      protocol          = "HTTPS"
      ssl_policy        = "ELBSecurityPolicy-2016-08"
      certificate_arn   = var.acm_certi
    
      default_action {
        type = "fixed-response"
    
        fixed_response {
          content_type = "text/plain"
          message_body = "Fixed response content"
          status_code  = "503"
        }
      }
    }

Terraform 실행

terraform plan

앞에 작성한 리소스들이 실제 AWS에서 생성할 수 있는지 terraform plan 명령어를 통해 확인합니다.
plan 명령어를 사용하면 현재 정의되어있는 리소스들을 실제 프로바이더에 적용했을 때, 테라폼이 어떤 작업을 수행할지 계획을 보여줍니다.
개인적으로 apply를 실행하기 전에 미리 내가 작성한 코드에 문제가 없는지 알 수 있어서 꼭 필요한 명령어라고 생각했습니다. (AWS 리소스 함부로 생성했다간 돌이킬 수 없는 일이 생길 수도 있기 때문이죠 😢)

아래 명령어 수행 결과를 보면, 총 71개의 리소스가 추가될 예정임을 알 수 있습니다.

terraform apply

plan을 통해 확인한 내용을, 실제로 AWS 프로바이더에 terraform apply 명령어를 통해 적용합니다.
"정말 적용해도 괜찮으시겠어요?" 라는 마치 이 명령어에 책임을 질 수 있겠냐는 질문에 yes를 치고서야 최종적으로 리소스들을 생성할 수 있습니다.
(물론, 생략하고 바로 생성할 수 있는 terraform apply -auto-approve 라는 명령어가 있습니다.)

아래 캡처 화면과 같이 Creating.. 이라는 문구가 계속 올라가면서 리소스들이 생성되는 과정을 확인할 수 있습니다. 마지막으로 Apply complete!라는 문구가 나오면, 이제 AWS에 콘솔에서 리소스들이 정상적으로 생성되었음을 확인할 수 있습니다.

  1. VPC

  2. EC2

  1. SG
  • 가장 반복 작업이라고 생각하는 inboud rule 한 번에 생성!🥳
  1. ALB

👶🏻 OK는 3분 만에 구축 요구 사항에 맞는 인프라 구성을 완료했고, 룰루랄라 놀 수 새로운 일을 시작할 수 있었습니다. 😂

완성 코드는 제 깃허브에 올려놨습니다 참고 부탁드립니다 :)
https://github.com/euneun316/terraform-study/tree/main/Default_Infra


마치며

처음 테라폼으로 인프라 구축을 시작했을 때 받았던 느낌은 '와 이걸 왜 지금 알았지..?' 였습니다.
명령어 몇 개로 쉽게 인프라가 뚝딱 만들어졌기 때문이죠. 하지만 계속 스터디하면서 느끼게 된 점은 쓰면 쓸수록 어렵고, 정말 '잘' 써야겠다는 생각입니다. 아무것도 없는 상태에서 기본 인프라 구축을 하는 데 사용하기에는 정말 좋습니다.

하지만 저는 위의 기본 인프라를 생성하는데 테라폼을 사용하면서도 많은 에러를 겪었습니다. subnet에서 리스트로 생성하다 꼬여서 6개가 만들어져야 하는데, 12개씩 생성되기도 하고, 만들어 놓은 ALB가 삭제가 안 되기도 하고, 잘 만들어진 EC2였는데, 글자 몇 개 수정했더니 삭제되고 다시 생성되기도 하고.. 그런데 이 리소스가 운영 중이던 서비스였다면..? 생각만 해도 아찔합니다.

따라서 앞으로는 .tfstate (상태 저장 파일)을 활용하는 부분, 테라폼을 사용해 협업하는 부분 에 중점을 두고 스터디할 예정입니다.
테라폼 고수가 되어 다시 이 글을 고칠 그날까지... 🏃🏻‍♀️


사담..

이때의 느낌을 잊지 않기 위한 기록.

처음 CloudNet@ Terraform Study 101 모집 공고를 보고 스터디 신청을 한 후, 운영진 가시다님과의 전화 인터뷰에서 "스터디 탈락 인원이 꽤 많은데, 스터디 완주 가능하실까요?" 라는 질문을 받았습니다.
"물론이죠!!" 라고 당차게 외친지 불과 첫 주 만에 자신감이 와르르 무너졌습니다.😢

분명 101 스터디인데.. 심화 과정이 아닌가 싶을 만큼 스터디 내용이 방대했고, 스터디원분들은 이미 고수들뿐인 것 같았습니다. 다들 이미 경지에 오르신 분들인데도 이렇게 열심히 노력한다는 사실에 엄청난 자극을 얻었고, 따라가려면 다른 분들이 걸을 때 나는 뛰어야겠다는 라는 생각으로 주말 밤을 새워 스터디 과제를 매주 제출했습니다. 그랬더니 조금 이해가 되는 것 같아서 점점 재밌어졌습니다. 😆

또 매주 테라폼 장인님들이 테라폼을 사용하면서 얻은 경험을 공유해주시는데, 모두 알차고 박수가 절로 나왔습니다. 그래서 꼭 스터디 완주를 해야겠다 다시 다짐했죠.

그런데..! (두둥 효과음) 중간과제 : 스터디 내용 혹은 테라폼 내용을 정리하여 글 작성 후 외부 공개!
중간과제 내용을 듣고 전 엄청난 부담감에 사로잡혔습니다. 다른 분들께 도움이 되는 내용을 과연 내가 작성할 수 있을지 자신이 없었습니다. 😢

하지만 그렇다고 포기할 순 없었기에, 할 수 있는 범위내에서 최선을 다하자고 결론 내리고 이 글을 작성하게 되었습니다.
저와 같이 처음 Terraform 스터디 중이신 분들이나, 기본 인프라 구성을 코드로 자동화하고 싶어 하시는 분들에게 꼭 도움이 됐으면 좋겠습니다. 제발(?) 🙏

profile
ImOk👌

2개의 댓글

comment-user-thumbnail
2022년 11월 23일

스터디 모임장 가시다입니다.
스터디 내용 잘 정리해주시고, 잘 따라와주셔서 감사드립니다.
그럼 졸업 완주까지 잘 부탁드립니다!

1개의 답글