Terraform Study #1

예전 테라폼 스터디에 참여한 적이 있었는데 그때는 배운 뒤 업무에 사용할 일이 없어 많이 응용할 기회가 없었다.

최근 테라폼을 사용해야 할 일이 생겼는데 마침 다시 좋은 기회가 생겨 스터디에 참여하게 됐다. 이번 스터디를 통해 IaC에 대해 조금 더 익숙해지고 테라폼을 익숙하게 사용하는 계기가 되길 바란다.

IaC와 테라폼

IaC (Infrastructure as Code)
IaC는 Infrastructure을 Code로 관리하는 것을 의미한다. 기존 관리 방식은 제공되는 콘솔 등을 통해 설정하는 방식이었다면 IaC를 통해 Code로 표현하고 관리할 수 있게 됐다.
IaC의 특징은 다음과 같다.
버전 관리: 인프라를 코드로 관리하면서 소프트웨어 코드처럼 버전 관리가 가능
복제 용이: 코드를 재사용하여 쉽게 동일한 인프라를 다른 환경에 배포 가능
자동화: 인프라 변경 작업을 자동화하여, 오류 가능성을 줄이고 효율성 상승

Terraform
Terraform은 HashiCorp사에 의해 개발 된 IaC 도구이다. 여러 CSP 또는 기존 On-premise 환경에 걸쳐 인프라를 안전하고 효율적으로 구축하기 위한 도구이다.
Terraform의 특징은 다음과 같다.
선언적 언어: HCL(Hashicorp Configuration Language)라는 선언적 언어를 사용하고 이를 통해서 원하는 결과값을 코드로 작성하면 이를 인프라로 구성해준다.
Provider 지원: 앞서 Terraform에 대한 설명에 나와있듯이 CSP을 포함한 다양한 서비스 제공자에 대한 플러그인이 포함되어 있기 때문에 여러 환경의 인프라를 관리할 수 있다.
상태 관리: Terraform은 관리하는 인프라의 상태를 추적하고 이를 원하는 상태로 만들기 위한 계획을 자동으로 생성한다.

0. 환경 구성

Terraform 실습 환경 구성은 특별할 게 없다. 이미 내 PC에는 실습환경이 구성되어 있기 때문에 스터디에서 제공 받은 내용을 간단하게 공유한다.

# tfenv 설치
brew install tfenv

# 설치 가능 버전 리스트 확인
tfenv list-remote

# 테라폼 1.5.6 버전 설치
tfenv install 1.5.6

# tfenv로 설치한 버전 확인(변경 전)
tfenv list
  1.5.6
* 1.5.1 (set by /usr/local/Cellar/tfenv/3.0.0/version)

# 테라폼 1.5.6 버전 사용 설정 
tfenv use 1.5.6

# tfenv로 설치한 버전 확인(변경 후)
tfenv list
* 1.5.6 (set by /usr/local/Cellar/tfenv/3.0.0/version)
  1.5.1

# 테라폼 버전 정보 확인
terraform version

# 자동완성
terraform -install-autocomplete
## 참고 .zshrc 에 아래 추가됨
cat ~/.zshrc
autoload -U +X bashcompinit && bashcompinit
complete -o nospace -C /usr/local/bin/terraform terraform

1. EC2 배포

Terraform을 사용해서 기본적인 EC2를 배포해보도록 하겠다.
이는 어려운 부분이 아니라 간단하게 스터디 내용을 참고해서 진행했다.

#최신 Amazon Linux2 버전 ami ID 찾기
#aws ec2 describe-images --owners self amazon
aws ec2 describe-images --owners self amazon --query 'Images[*].[ImageId]' --output text

aws ssm get-parameters-by-path --path /aws/service/ami-amazon-linux-latest
aws ssm get-parameters-by-path --path /aws/service/ami-amazon-linux-latest --query "Parameters[].Name"
aws ssm get-parameters-by-path --path /aws/service/ami-amazon-linux-latest --query "Parameters[].Value"

생성 입력을 넣었을 때 생성되는 순간을 확인하기 위해 별도 터미널에서 모니터링을 진행한다.

while true; do aws ec2 describe-instances --query "Reservations[*].Instances[*].{PublicIPAdd:PublicIpAddress,InstanceName:Tags[?Key=='Name']|[0].Value,Status:State.Name}" --filters Name=instance-state-name,Values=running --output text ; echo "------------------------------" ; sleep 1; done

EC2 생성하는 tf 파일을 만든다.

# VS Code 터미널2
cat <<EOT > main.tf
provider "aws" {
  region = "ap-northeast-2"
}

resource "aws_instance" "example" {
  ami           = "ami-084e92d3e117f7692"
  instance_type = "t2.micro"
}
EOT

초기화와 Plan 그리고 배포를 순서대로 진행한다.

# 초기화
terraform init
ls -al
tree .terraform

# plan 확인
terraform plan

# apply 실행
terraform apply
 Enter a value: yes 입력

# ec2 생성 확인 : aws 웹 관리 콘솔에서도 확인 - 서울 리전 선택
export AWS_PAGER=""
aws ec2 describe-instances --output table

위 사진들을 순서대로 보면 초기화/Plan/배포까지 별 문제없이 잘 진행된 것을 알 수 있다.

EC2 배포를 완료했으니 이제 .tf 파일을 수정해서 EC2의 Tag 정보를 수정해보도록 하겠다.
기존에는 EC2에 Name Tag을 입력하지 않았으나 이번엔 tf 파일에 Name Tag을 추가하였다.

cat <<EOT > main.tf
provider "aws" {
  region = "ap-northeast-2"
}

resource "aws_instance" "example" {
  ami           = "ami-084e92d3e117f7692"
  instance_type = "t2.micro"

  tags = {
    Name = "t101-study"
  }

}
EOT

Name Tag을 추가한 tf 파일의 배포를 진행하기 전에 Plan을 진행했다.
Plan 단계에서 Name Tag가 추가되는 것을 확인할 수 있다. (Patch Group은 무관한 내용이다.)

Plan 내용을 확인하고 Apply을 진행했다. 모니터링 중인 터미널 창에서도 Name Tag을 통해 이름이 등록된 것을 확인할 수 있다.

Terraform 으로 생성한 리소스를 삭제할 때는 생성과 같이 간단하다. 다만 모든 리소스가 삭제 될 수 있으니 이 부분은 조심해야 한다. destroy 명령어를 입력하면 EC2 리소스가 잘 삭제되는 것을 확인할 수 있다. EC2 뿐만 아니라 EC2가 생성되며 같이 생성 되는 ebs도 같이 잘 삭제 된 것을 알 수 있다.

#삭제 명령어
terraform destroy -auto-approve

2. EC2 1대 배포 & 웹 서버 설정(도전과제 #1 병행)

간단하게 EC2를 1대 배포/수정/삭제해보았다면 이제 해당 EC2에 Web Server 설정을 진행하는 부분까지 진행해보도록 하겠다. 기본적인 EC2 생성은 동일하게 진행하지만 웹서버로 작동시키기 위해 보안그룹을 생성하고 연결하는 과정이 추가 될 예정이다.

우선 웹서버 역할을 할 Ubuntu 22.04 Image을 사용하여 EC2를 배포해준다.
이때 user_data라는 구문을 사용하여 bash 쉘을 통해 내 이름을 출력하는 index.html 생성 및 httpd 설치와 80 Port Open을 진행해준다. 이후 배포 된 EC2의 Public IP에 접속 테스트를 다른 터미널에서 진행해주었다.

cat <<EOT > main.tf
provider "aws" {
  region = "ap-northeast-2"
}

resource "aws_instance" "example" {
  ami                    = "ami-0c9c942bd7bf113a2"
  instance_type          = "t2.micro"

  user_data = <<-EOF
              #!/bin/bash
              echo "I'm Boseung" > index.html
              nohup busybox httpd -f -p 80 &
              EOF

  tags = {
    Name = "terraform-Study-101"
  }
}
EOT

# init
terraform init

# plan
terraform plan

# apply 실행
terraform apply -auto-approve

# [터미널3] 변수 지정
PIP=<EC2 Public IP>
while true; do curl --connect-timeout 1  http://$PIP:80/ ; echo "------------------------------"; date; sleep 1; done

어쩌면 당연한 결과겠지만 제대로 연결되지 않는 것을 확인할 수 있다. 서두에서 말한 것처럼 보안그룹의 설정이 필요하다.

8080을 허용해주는 보안그룹을 생성하고 EC2에 해당 보안그룹을 연결하는 구문을 추가하였다. 그리고 output 명령어를 통해 배포되는 EC2의 Public IP을 확인한다.

cat <<EOT > main.tf
provider "aws" {
  region = "ap-northeast-2"
}

resource "aws_instance" "example" {
  ami                    = "ami-0c9c942bd7bf113a2"
  instance_type          = "t2.micro"
  vpc_security_group_ids = [aws_security_group.instance.id]

  user_data = <<-EOF
              #!/bin/bash
              echo "I'm Boseung" > index.html
              nohup busybox httpd -f -p 80 &
              EOF

  tags = {
    Name = "Single-WebSrv"
  }
}

resource "aws_security_group" "instance" {
  name = var.security_group_name

  ingress {
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

variable "security_group_name" {
  description = "The name of the security group"
  type        = string
  default     = "terraform-example-instance"
}

output "public_ip" {
  value       = aws_instance.example.public_ip
  description = "The public IP of the Instance"
}
EOT

# plan/apply
terraform plan
terraform apply -auto-approve

보안 그룹이 생성되었기 때문에 웹서버에 정상적으로 접근이 되는 것을 확인할 수 있다. 또한 Output을 통해 Public IP가 호출되는 것도 확인할 수 있다.

생성을 완료하고 리소스 다이어그램을 살펴보기 위해 graphviz을 통해 다이어그램을 실행시켜봤다.

#graph 다이어그램 실행
terraform graph > websrv.dot

websrv.dot 파일 생성 후 해당 파일 선택한 다음 MAC OS 기준 CMD+Shift+V을 눌러 다이어그램을 띄워봤다.
Terraform이 어떤 식으로 작동되는지를 간단하게나마 확인할 수 있었다.

3. S3 Backend 활용 리소스 배포(도전과제 #2)

Terraform에서 S3을 Backend을 사용해 리소스를 배포하는 도전과제를 진행해볼 예정이다.
그에 앞서, Terraform에서의 Backend는 어떤 의미인지 알아보자.

Terraform에서 Backend는 크게 2가지의 기능을 수행한다.

  1. 상태 파일 관리: Terraform은 .tfstate라는 상태 파일을 사용하여 인프라의 현재 상태를 추적한다. 이 파일은 로컬 파일 시스템에 저장할 수도 있고, 원격 스토리지 서비스(AWS S3, Azure Blob Storage 등)에 저장할 수도 있다. Backend 설정을 통해 이러한 원격 저장소를 지정할 수 있고, 여러 팀원이 상태 파일을 공유할 수 있게 된다. 따라서 협업을 진행할 때 해당 상태 파일을 통해 내용을 확인할 수 있다.
  2. Execution Lock: 여러 사람이나 시스템이 동시에 같은 Terraform 구성에 대한 변경을 수행하는 것을 막기 위해 사용한다다. 원격 Backend를 사용하면, Terraform은 자동으로 실행 중인 작업을 잠금 처리(Lock)하여 동시 변경을 방지한다. 이를 통해 협업을 진행할 때 동시에 작업이 수행되어 문제가 발생하는 것을 방지할 수 있다.

위와 같은 기능을 갖고 있는 Terraform Backend을 AWS S3에 저장하여 진행할 예정이다.

S3 Bucket을 미리 생성하고 해당 S3 Bucket을 Backend로 사용하는 내용으로 진행한다.

# backend.tf 파일 생성
cat <<EOT > backend.tf
terraform {
  backend "s3" {
    bucket = "ybs-tf-backend"
    key    = "backend/terraform.tfstate"
    region = "ap-northeast-2"
  }
}
EOT

# terraform init 진행
terraform init

기존 만들어둔 S3을 사용해서 backend.tf을 만들고 terraform init을 진행했다.
터미널 창에 S3을 사용한 backend 구성이 진행된 것을 확인할 수 있다.

# main.tf 생성
cat <<EOT > main.tf
provider "aws" {
  region = "ap-northeast-2"
}

resource "aws_instance" "ybs-websrv-ec2" {
  ami                    = "ami-0c9c942bd7bf113a2"
  instance_type          = "t2.micro"
  vpc_security_group_ids = [aws_security_group.ybs-websrv-sg.id]

  user_data = <<-EOF
              #!/bin/bash
              echo "I'm Boseung" > index.html
              nohup busybox httpd -f -p 80 &
              EOF

  tags = {
    Name = "Single-WebSrv"
  }
}

resource "aws_security_group" "ybs-websrv-sg" {
  name = var.security_group_name

  ingress {
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

variable "security_group_name" {
  description = "The name of the security group"
  type        = string
  default     = "ybs-websrv-sg"
}

output "public_ip" {
  value       = aws_instance.ybs-websrv-ec2.public_ip
  description = "The public IP of the Instance"
}
EOT

# Plan/Appliy 진행
terraform plan
terraform apply -auto-approve

EC2 배포는 당연히 문제없이 잘 되었다.

하지만 우리가 궁금한 건 EC2 배포가 아닌 Backend 설정이었으니 Backend로 할당한 S3을 확인해보도록 한다.

Bucket에 들어가보면 tfstate 파일이 생성된 것을 볼 수 있다.

파일을 다운로드 받아 열어보면 아래와 같은 내용도 확인 가능하다.

tfstate 파일은 중요한 내용들을 담고있을 수 있으니 파일에 대한 권한 설정도 확실히 해야 한다. 실제 협업을 할 때는 접근해야 하는 사용자에 한해서만 tfstate 파일에 접근 권한을 줘야 할 것 같다.

4. 정리

Terraform을 오랜만에 다시 접하니 처음 접한 마음처럼 느껴졌다. 조금은 더 익숙해질 수 있도록 이것저것 다뤄봐야 할 것 같다.