DBMS/활용 사례

Terraform 개념과 설치

(주)비트나인 2023. 8. 30. 13:05

1. 들어가기에 앞서

 

이제 DevOps는 개발에 있어 필수로 자리잡고 있습니다. 그 방법론 중 하나가 Terraform을 사용한 DevOps 개발환경 구현인데요, 이 기사에서는 Terraform이 무엇이고, 기본 설정은 어떻게 하는지 소개하고자 합니다.

 

2.1. Terraform

 

Terraform은 HashiCorp에서 개발된 IaC(Infrastructure as Code), 즉 코드형 인프라의 일종으로, Go Language 로 작성된 오픈소스입니다. 코드를 통해서 Infrastructure를 구성하고 관리하는 데에 쓰이죠.

 

Terraform 구성 순서로는 다음 세 가지 스텝을 준수합니다.

  • Write : Infra를 Code로 작성합니다.
  • Plan : 반영(apply)하기 전에 변경내역을 확인합니다.
  • Apply : 변경한 내용을 시스템에 적용합니다.

 

또한 HCL(HashiCorp Configuration Language) 사용하여 code를 작성할 수도 있습니다.

 

Terraform은 다음과 같은 특징을 지닙니다.

  • Cloud 환경에 제약없이 사용할 수 있습니다.
  • Code 로 관리가 되기 때문에 특정 resource 를 추가하기 위해 적용 전 다른 사람의 리뷰를 받을 수 있습니다.
  • 기존 Code 를 재사용할 수 있어 추가적으로 동일한 resource 를 만들 때 쉽고 빠르게 적용할 수 있습니다.

 

단, 스크립트를 작성하는 초기 단계에서 많은 시간을 요구하며, Cloud의 신규 서비스 대응이 상대적으로 느린 편에 속합니다.

 

2.2. Terraform 관련 용어

  • Provisioning :서비스를 실행하기 위한 준비 단계로, 네트워크나 컴퓨팅 자원을 준비 작업 혹은 준비된 자원에 사이트 패키지나 App 의존성을 준비하는 단계입니다. Terraform은 Terraform은 전자에 가깝습니다.
  • Provider : 외부 서비스를 연결해주는 기능을 하는 모듈(ex. AWS, GCP, Azure)입니다.
  • Resource : Provider가 제공하는 제품의 최소 단위로, Terraform으로 관리할 인프라 자원(ex. EC2, IAM, S3, RDS)을 뜻합니다.
  • HCL(Hashicorp Configuration Language) : Terraform에서 사용하는 언어로, .tf 확장자를 지녔습니다.
  • Plan : .tf 파일의 내용이 실제 적용이 가능한지 확인하는 작업으로, create, update, delete 등의 작업이 Resource에 어떤 영향을 미칠지 보여줍니다.
  • State : Terraform을 통해 생성된 리소스들의 상태로 Terraform apply 명령어를 실행한 결과물입니다.
  • Output : Terraform으로 만든 리소스를 변수 형태로 state에 저장합니다.
  • Module : 공통적으로 활용할 수 있는 모듈을 정의합니다.
  • Remote다른 경로의 state를 참조하는 것을 의미하며, output 변수를 불러올 때 주로 사용합니다.




3. AWS 및 Terraform 설치

기본적인 개념은 위에 소개해 드렸고, 이 장에서는 AWS와 Terraform을 연동하는 방법에 대해 알아보고자 합니다.

 

3.1. Terraform Install 및 AWS 설정

보통 install terraform 으로 설치할 수 있지만, mac OS는 Terraform 버전 관리 도구인 tfenv(Terraform Version Manager)를 설치하여 원하는 version의 Terraform을 설치할 수 있습니다.

 

Terraform Install - Windows

choco install terraform --version 1.0.3


Terraform Install - mac OS

brew install tfenv

tfenv install

tfenv install 1.0.3

tfenv use 1.0.3

상기 과정을 통해 Terraform을 설치한 후에는 AWS를 설정해야 합니다. Terraform은 사용자가 지정한 AWS_ACCESS_KEY_ID 및 AWS_SECRET_ACCESS_KEY를 그대로 사용하기 때문에 Terraform 사용 시 반드시 세팅해야 합니다

 

우선 AWS 로그인 후, 우상단 아이디 패널 클릭 → 내 보안 자격 증명 → 액세스 키로 이동하여 새 엑세스 키를 발급받습니다.

로컬에서 aws configure 를 입력한 후, 각 필드에 알맞은 정보를 입력합니다.

  • AWS Access Key ID : 액세스 키 ID
  • AWS Secret Access Key : 시크릿 키 ID
  • Default region name : 지역 정보. 한국(서울)은 ap-northeast-2입니다.
  • Default output format : 출력 포맷 (json, yaml, yaml-stream, text, table)

 

이후 ~/.aws/credentials에서 설정 정보를 확인할 수 있습니다.

 

3.2. VPC 및 관련 요소 설치

AWS VPC(Virtual Private Cloud)는 AWS 전용 가상 네트워크로, 해당 Cloud 사용 시 반드시 필요합니다.

 

VPC의 구성요소는 다음과 같습니다. Terraform script로 직접 구성할 수도 있습니다.

  • Subnet : VPC의 IP 주소 범위.  VPC 내에서도 분리된 독립적인 network area
  • Routing Table : 네트워크 트래픽을 전달할 위치를 결정할 때 사용되는 규칙 집합
  • Internet Gateway : VPC의 리소스와 인터넷 간의 통신을 활성화하기 위해 VPC에 연결하는 게이트웨이
  • NAT Gateway : 네트워크 주소 변환을 통해 프라이빗 서브넷에서 인터넷 또는 기타 AWS 서비스에 연결하는 게이트웨이
  • 보안 그룹 : 인스턴스에 대한 인바운드 및 아웃바운드 트래픽을 제어하는 가상 방화벽 역할을 하는 규칙 집합
  • VPC EndPoint : 인터넷 게이트웨이, NAT 디바이스, VPN 연결 또는 AWS Direct Connect 연결을 필요로 하지 않고 VPC와 AWS 서비스 전용 연결을 할 수 있도록 해주는 가상 장치

먼저 VPC를 생성합시다. 파일 생성 시 확장자는 반드시 tf여야 합니다.

 

provider.tf(혹은 init.tf)
provider "aws" {
 region = "ap-northeast-2"  // aws의 리전(지역) 설정
}

 

vpc.tf(상기 서술된 VPC 구성요소는 이후 이 파일 안에 다 넣습니다.)
resource "aws_vpc" "main" { // VPC 생성 시 "aws_vpc" 리소스 사용. 이런 규칙은 다른 설정에서도 동일 적용
 cidr_block = "10.0.0.0/16" // vpc 생성 시 필수 인자값

 tags = {
  Name = "TERRAFORMVPC_210720" // 여기서 설정된 Name으로 resource 정보가 AWS에 등록됨
 }
}

 

이후 terraform init → terraform plan을 통해 적용될 결과를 확인 후, terraform 

apply 커맨드로 최종 반영합니다.

 

plan 단계에서 문제없다 하더라도 AWS 계정 권한 등의 문제로 apply에서 반영이 안 

될 수 있으니 에러 코드를 보고 문제점을 찾아야 합니다.

 

생성 완료 시 AWS의 VPC 대시보드에서 상기 스크립트로 VPC가 생성되었음을 

확인할 수 있습니다.



 

3.3. Subnet

다음은 subnet 생성입니다. 이 과정에서는 public/private 한 subnet을 설정합니다. subnet 생성 시에는 aws_subnet resource를 사용해야 합니다. 이 때 VPC ID 및 cidr block은 필수이며, subnet의 cidr block은 vpc의 cidr block에 속해야 합니다.

 

resource "aws_subnet" "public_subnet" {
  vpc_id = aws_vpc.main.id
  cidr_block = "10.0.0.0/24"

  availability_zone = "ap-northeast-2a"

  tags = {
   Name = "terraform-vpc-public-subnet"
  }

}

resource "aws_subnet" "private_subnet" {
  vpc_id = aws_vpc.main.id
  cidr_block = "10.0.1.0/24"

  availability_zone = "ap-northeast-2a"

  tags = {
   Name = "terraform-vpc-private-subnet"
  }
}

 

설정이 제대로 되었다면, terraform plan → terraform apply 후 VPC dashboard에서 정상적으로 생성됨을 확인할 수 있습니다.

 

3.4. Internet Gateway 및 NAT Gateway

우선 VPC의 instance와 internet을 연결해 줍시다.

 

resource "aws_internet_gateway" "IGW" {
 vpc_id = aws_vpc.main.id

 tags = {
  Name = "terraform-IGW"
 }
}

그리고 NAT  Gateway를 설정합니다. private subnet에서 요청이 외부로 나갈 경우 고정 IP를 사용합니다.(연결은 private subnet과 하지만 public subnet에 위치). 그로 인해 NAT Gateway 생성 시 Elastic IP도 생성해야 하는데, Elastic IP가 생성되면 실제로 IP 사용여부에 관계없이 과금이 진행되므로 주의해야 합니다.

 

스크립트 작성 후 terraform plan/apply 시 시간이 1분 이상 소요됩니다.

 

# Elastic IP 생성
resource "aws_eip" "NAT" {
 vpc = true
 lifecycle {
  create_before_destroy = true
 }


resource "aws_nat_gateway" "NAT_gateway" {
 allocation_id = aws_eip.NAT.id
 subnet_id = aws_subnet.public_subnet.id # public subnet에 설치

 tags = {
  Name = "NAT-GW"
 }
}

 

3.5. Route Table

Traffic을 전달하기 위한 규칙이 적재된 table이며, 여러 subnet에서 동시에 사용할 수 있습니다.

 

resource "aws_route_table" "public"{
 vpc_id = aws_vpc.main.id

 tags = {
   Name = "terraform-rt-public"
 }
}

resource "aws_route_table" "private"{
 vpc_id = aws_vpc.main.id

 tags = {
   Name = "terraform-rt-private"
 }
}

이후, 어떤 subnet을 route table에 연결해야 할지 정하기 위해 association 작업을 수행합니다.

 

resource "aws_route_table_association" "route_table_association_public"{
 subnet_id = aws_subnet.public_subnet.id
 route_table_id = aws_route_table.public.id

}

resource "aws_route_table_association" "route_table_association_private"{
 subnet_id = aws_subnet.private_subnet.id
 route_table_id = aws_route_table.private.id

}

다음으로 route table의 rule을 추가합시다.

 

resource "aws_route" "public_nat" {
  route_table_id              = aws_route_table.public.id
  destination_cidr_block      = "0.0.0.0/0"
  nat_gateway_id              = aws_nat_gateway.NAT_gateway.id
}
resource "aws_route" "private_nat" {
  route_table_id              = aws_route_table.private.id
  destination_cidr_block      = "0.0.0.0/0"
  nat_gateway_id              = aws_nat_gateway.NAT_gateway.id
}
 

3.6. S3

S3는 인터넷용 Storage Service인데요. Amazon은 AWS S3에 대해 다음과 같은 이점을 제시하고 있습니다.

  • 확장성/가용성/내구성 우수
  • 비용 효율적인 storage class
  • 보안/규정 준수/감사 기능
  • 간편한 data 관리 및 엑세스 제어
  • query in place 요청에 따른 처리
  • 많은 지원 제공

 

s3를 제어하기 위해 s3.tf 파일을 생성해 봅시다.

resource "aws_s3_bucket" "s3" { # bucket : S3에 저장되는 객체에 대한 기본 컨테이너
 bucket = "20210721-terraform"
}

제대로 설정했다면, terraform plan → terraform apply가 완료되면 생성된 

버킷을 확인할 수 있습니다.

이렇게 버킷이 생성되면 aws 명령어를 통해 파일을 업로드하거나 다운로드 

할 수 있습니다.

  • 업로드 : aws s3 cp [filename] s3://[bucket name]/[path]
  • 다운로드 : aws s3 cp s3://[bucket name]/[path]

 

3.7.IAM

IAM(Identity and Access Management)는 소스에 대한 액세스를 안전하게 제어할 수 있는 웹 서비스로 IAM을 사용하여 리소스를 사용하도록 인증 및 권한 부여된 대상을 제어할 수 있습니다. 이 제어에 대한 그룹이나 롤, 규칙을 설정할 수 있지요.

 

먼저, 사용자 및 해당 사용자 및 policy, role을 만듭시다.

 

user_test.tf
resource "aws_iam_user" "test" {
 name = "test"
}
# 상기 코드로 user 생성 후, 하단 code는 아래의 group, role apply 후 추가하여 apply
resource "aws_iam_user_policy" "test_policy" {
  name  = "test_policy"
  user  = aws_iam_user.test.name

  policy = <<EOF    # policy는 json type으로 관리하여, json form의 처음과 끝을 EOF로 감싸줌
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "*"
            ],
            "Resource": [
                "*"
            ]
        }
    ]
}
EOF
}



devops_group.tf
resource "aws_iam_group" "devops_group"{
 name = "devops"
}
# 상단 코드만 apply하여 group 생성 후, devops라는 group에 iam user 등록.
resource "aws_iam_group_membership" "devops" {
 name = aws_iam_group.devops_group.name

 users = [
   aws_iam_user.test.name
 ]

 group = aws_iam_group.devops_group.name
}



iam_role.tf
resource "aws_iam_role" "test-role" {
 name = "test-role"
 path = "/"
 assume_role_policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "",
      "Effect": "Allow",
      "Principal": {
        "Service": "ec2.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
EOF
}
# 상기 코드까지만 수행 시 권한은 부여되지 않으므로, 아래 추가 코드를 기입하여 해당 role에 대한 권한 추가
resource "aws_iam_instance_profile" "test-profile" {
  name = "test-profile"
  role   = aws_iam_role.test-role.id
}

 

3.8. Backend

마지막 단계입니다. Terraform Backend를 통해 작업의 수행 location, 방법, snapshot 저장 공간 등을 정의합시다.

 

provider.tf(혹은 init.tf)
provider "aws"{
 access_key = [your access key]
 secret_key = [your secret key]
 reigon = "ap-northeast-2"
}


resource "aws_s3_bucket" "tfstate"{
 bucket = "202107-apnorthease2-tfstate"

 versioning {
  enabled = true
 }
}

resource "aws_dynamodb_table" "terraform_state_lock" { #상태에 대한 관리를 위해 dynamodb 생성
 name= "TerraformStateLock"
 hash_key = "LockID"
 billing_mode = "PAY_PER_REQUEST"

 attribute{
  name = "LockID"
  type = "S" # Lock 속성 지정. S: String, N : Number, B : Binary
 }
}

init → plan → apply를 통해 backend s3 및 dynamoDB Table을 생성한 후, iam을 생성했을 때의 state 파일을 관리하기 위해 backend로 옮깁시다.

 

terraform.tf
terraform {
 backend "s3" {
  bucket = "202107-apnorthease2-tfstate"
  key = "terraform/IAM/terraform.tfstate"
  region = "ap-northeast-2"
  encrypt = true
  dynamodb_table = "TerraformStateLock"
  }
}

 

4. DevOps를 위한 Terraform 숙지

여기까지가 Terraform 설정의 ‘기본’ 과정입니다. 앞서 말씀드렸듯이 스크립트를 작성하는 초기 단계에서 많은 시간을 요구한다는 것이 괜한 말이 아니었죠. 러닝 커브가 제법 있습니다. 설정 막바지에 와서 하나라도 잘못되면 지금까지 작성한 코드를 다 훑어봐야 할 수도 있습니다.

 

하지만 하이 리턴은 확실히 보장하며, 제대로 사용할 수 있으면 인프라 자동화에 대한 고성능, 고효율을 보장하기에 Terraform을 활용하고자 한다면 반드시 숙지해야 합니다. 순간의 시간 투자로 인해 나도 편하고 팀도 편한, 생산성을 보장하는 Terraform을 익혀 보시는 건 어떨까요?

 

 

 

 

글 : 윤재영 팀장 ( 비트나인 R&D Lead팀 )