본문 바로가기
  • lakescript
스터디 이야기/Terraform

[T101] 1-2. Terraform - 기본 사용법(init, plan, apply, destory)

by lakescript 2024. 6. 13.

 

더보기

이 스터디는 CloudNet@에서 진행하는 T101 스터디를 참여하면서 공부하는 내용을 기록하는 블로그 포스팅입니다.

CloudNet@에서 제공해주는 자료들과 테라폼으로 시작하는 IaC 를 바탕으로 작성되었습니다.

 

주요 커맨드

테라폼 버전이 변경되면서 추가되거나 삭제될 수 있으므로 커맨드 목록을 확인하고 사용하는 법을 익히는 것이 좋습니다. 

 

terraform command 실행 시 기본 출력 내용

$ terraform

Usage: terraform [global options] <subcommand> [args]

The available commands for execution are listed below.
The primary workflow commands are given first, followed by
less common or more advanced commands.

Main commands:
  init          Prepare your working directory for other commands
  validate      Check whether the configuration is valid
  plan          Show changes required by the current configuration
  apply         Create or update infrastructure
  destroy       Destroy previously-created infrastructure

All other commands:
  console       Try Terraform expressions at an interactive command prompt
  fmt           Reformat your configuration in the standard style
  force-unlock  Release a stuck lock on the current workspace
  get           Install or upgrade remote Terraform modules
  graph         Generate a Graphviz graph of the steps in an operation
  import        Associate existing infrastructure with a Terraform resource
  login         Obtain and save credentials for a remote host
  logout        Remove locally-stored credentials for a remote host
  metadata      Metadata related commands
  output        Show output values from your root module
  providers     Show the providers required for this configuration
  refresh       Update the state to match remote systems
  show          Show the current state or a saved plan
  state         Advanced state management
  taint         Mark a resource instance as not fully functional
  test          Execute integration tests for Terraform modules
  untaint       Remove the 'tainted' state from a resource instance
  version       Show the current Terraform version
  workspace     Workspace management

Global options (use these before the subcommand, if any):
  -chdir=DIR    Switch to a different working directory before executing the
                given subcommand.
  -help         Show this help output, or the help for a specified subcommand.
  -version      An alias for the "version" subcommand.

 

terraform 실습 - 1

커맨드 사용 방법을 익히기 위해 Directory 하나를 생성하고 main.tf. 파일을 하나 생성합니다.

#01-command/main.tf

resource "local_file" "abc"{
  content = "abc!"
  filename = "${path.module}/abc.txt"
}

 

- "local_file"은 terraform local 프로바이더로 파일을 프로비저닝하는 데 사용됩니다.

- ${path.module}은 실행되는 terraform 모듈의 파일 시스템 경로이며, 여기서는 디렉토리 경로입니다.

 

init

기본 사용법 : terraform [global options] init [options]

 

terraform init 명령은 terraform 구성 파일이 있는 작업 디렉토리를 초기화하는 데 사용됩니다. 이 작업을 실행하는 디렉토리를 root module이라고 부릅니다. 또한, init 명령어는 terraform을 시작하는 데 사용되는 첫 번째 명령어로 terraform에서 사용되는 프로바이더, 모듈 등의 지정된 버전에 맞춰 root module을 구성하는 역할을 수행합니다. 자동화 구성을 위한 파이프라인 설계 시 terraform을 실행하는 시점에 필수적으로 요청되는 명령어이기도 합니다. (자바의 pom.xml, Node.js의 package.json, python의 requirements.txt 처럼 애플리케이션 구성에 필요한 의존성을 읽고 가져와 최소 실행 시 실행에 필요한 artifact나 library등을 다운로드하고 준비하는 역할과 비슷합니다.)

 

바로 plan을 한다면?
terraform init 을 수행하지 않고 바로 terraform plan을 하면 어떻게 될까? 

 

위의 오류 메시지는 작성한 코드에서 local이라는 종속성에 대한 구성을 요구하지만 초기화되지 않아 문제가 발생했다는 내용이고, terraform init을 실행하라는 내용입니다. 

 

terraform init 명령어를 실행하니 terraform 코드에서 사용된 구문을 기반으로 필요한 프로바이더 플러그인을 찾고 설치하는 과정이 진행되었습니다. 추가로 프로바이더, 모듈, 백엔드 구성이 설정되고 변경되는 경우에도 이 명령을 수행해야 한다고 알려줍니다.

 

 

[option] - upgrade
0.14 버전 이후부터 프로바이더 종속성을 고정시키는 .terraform.lock.hcl이 추가되었습니다. 작업자가 로컬에서 init으로 받은 프로바이더, 모듈 버전으로 수행한 이후 다른 작업자나 원격(remote) 환경에서 init을 수행하는 경우, 지속적으로 업그레이드되는 각 프로바이더와 모듈로 인해 변경된 버전으로 설치될 가능성이 있습니다. 그로인해 작업 당시의 버전 정보를 기입하고, .terraform.lock.hcl 파일이 있으면 해당 파일에 명시된 버전으로 terraform init 명령을 실행합니다. 이후 작업자가 의도적으로 버전을 변경하거나 코드에 명시된 다른 버전으로 변경하려면 terraform init -upgrade 옵션이 붙은 명령어를 실행해야 합니다.

 

 

vaildate

기본 사용법 : terraform [global options] validate [options]

 

terraform validate는 커맨드 단어 의미대로 디렉토리에 있는 terraform 구성 파일의 유효성을 확인하는 명령어입니다. 이때 대상이 되는 인프라와 서비스의 상태를 확인하기 위한 원격 작업이나 API 작업은 발생하지 않고, 코드적인 유효성만 확인합니다. 즉, 작성된 구성의 문법, 종속성, 속성 이름이나 연결된 값의 정확성에 대한 확인을 진행합니다.

#01-command/main.tf

resource "local_file" "abc"{
  content = "abc!"
 #filename = "${path.module}/abc.txt"
}


유효성을 확인해보기 위해 위에서 작성한 코드에서 주석을 추가해보겠습니다.

 

local_file 리소스 정의에는 filename이 필수 인수이지만 주석 처리되었기 때문에 해당 정의가 없다는 메시지가 출력됩니다.

 

[option] -no-color
로컬이 아닌 외부 실행 환경(jenkins, terraform cloud, github action등)을 사용하는 경우 <-[0m <-[1m와 같이 색상 표기 문자가 출력될 수 있습니다. 이때 -no-color 옵션을 주어 명령어를 실행하면 색상 표기 문자 없이 출력합니다.

[option] -json
-json 옵션을 사용할 수 있는 명령어는 실행 결과를 JSON 형식으로 출력할 수 있습니다. 프로비저닝 파이프라인을 설계하는 경우 결과에 대한 쿼리가 필요할 수 있는데, 이때 JSON 형태의 출력 데이터를 이용하면 프로비저닝 과정의 조건 및 데이터로 사용 가능합니다.

 

 

plan & apply

plan 기본 사용법 : terraform [global options] plan [option] 
apply 기본 사용법 : terraform [global options] apply [option] [plan]

 

terraform plan 명령은 terraform으로 적용할 인프라의 변경 사항에 관한 실행 계획을 생성성하는 명령어입니다. 출력되는 결과를 확인해서 어떤 사항이 변경될지, 적용될지, 생성될지 등 사용자가 미리 검토하고 확인하여 이해하는 데 도움을 줍니다.

terraform apply는 terraform plan 명령을 기반으로 실행됩니다. 이 두 명령어는 작업을 계획하고 적용하는 것이 terraform 사용의 주 목적입니다.

 

plan 명령은 변경 사항을 실제로 적용하지 않고, 적용 전에 예상한 구성이 맞는지 검토하는 데 이용합니다.

  • 테라폼 실행 이전의 상태와 비교해 현재 상태가 최신화되었는지 확인합니다.
  • 적용하고자 하는 구성을 현재 상태와 비교하고 변경점을 확인합니다.
  • 구성이 적용되는 경우 대상이 테라폼 구성에 어떻게 반영되는지 확인합니다.

 

apply 명령은 plan에서 작성된 적용 내용을 토대로 작업을 실행합니다. terraform plan 명령으로 생성되는 계획이 필요하지만, 만약 없다면 새 실행 계획을 자동으로 생성하고 해당 계획을 승인할 것인지 묻는 메시지가 표시됩니다.

 

#01-command/main.tf

resource "local_file" "abc"{
  content = "abc!"
  filename = "${path.module}/abc.txt"
}

 

위에서 작성한 코드에서 terraform plan 명령을 실행합니다.

 

현재는 기존에 생성된 리소스가 없으므로 앞선 구성 파일 내용을 바탕으로 새로 생성해야 되기 때문에 + 기호만 있습니다.

 실행계획을 살펴보면 코드 구성에서는 local_file 리소스를 정의할 때 필수 요소인 content와 filename만 선언헀지만, 리소스에 정의 가능한 다른 옵션의 내용과 기본값이 자동 입력되어 적용되는 것을 확인하실 수 있습니다. 이를 통해 해당 구성이 특정 리소스에 대해 생성될 때 어떤 값이 기본값으로 설정되는지 확인하실 수 있습니다. 그리고 마지막 줄의 의미는 이 구성을 적용할 경우 하나의 리소스가 추가되고, 변경되거나 삭제되는 것은 없다고 알려줍니다.

 

[option] -detailed-exitcode
plan 명령과 함께 사용하기에 좋은 옵션으로, 옵션이 없던 때와 결과는 같지만 exitcode가 환경 변수로 구성됩니다. 0, 1, 2로 보여주며 각 숫자 코드는 자동화된 동작을 설게할 때 그 뒤 작업을 수행할 지, 사용자에게 알릴 지, 정지할 지등을 나타냅니다.
(0: 변경 사항이 없는 성공, 1: 오류가 있음, 2: 변경 사항이 있는 성공)

 

 

terraform apply 명령을 실행하면 위에서 봤던 terraform plan의 실행 결과처럼 실행 계획을 먼저 보여주고 적용 여부를 묻는 입력 모드가 활성화되었습니다.(yes가 아니면 모두 취소하는 것으로 간주합니다.)

 

terraform plan -out=tfplan

 

-out=<filename> 옵션을 주어 실행하면 filename으로 plan 결과가 생성되며 이는 바이너리 형태이기 때문에 내용을 확인할 수는 없습니다.

 

해당 파일을 terraform apply 명령에 붙여 실행해보겠습니다.

 

앞서 plan 없이 apply를 하게 되었을 때와 다르게 즉시 적용하는 것을 확인하실 수 있습니다.

 

그렇다면 적용이 완료된 지금 terraform apply를 진행해보겠습니다.

terraform은 기본적으로 선언적 구성 관리를 제공하는 언어로 멱등성(idempotent)을 갖고, 이후에 추가로 설명될 상태를 관리하기 때문에 동일한 구성에 대해서는 다시 실행하거나 변경하는 작업을 수행하지 않습니다. 따라서 더이상 변경이 없는 구성에서는 plan 단계에서 변경 사항이 없기 때문에 출려되는 메시지 내용 (No changes. Your infrastructure matches the configuration.) 처럼 프로비저닝 동작이 실행되지 않습니다.

 

이번에는 기존 main.tf에 새로운 리소스를 추가해보겠습니다.

resource "local_file" "abc"{
  content = "abc!"
  filename = "${path.module}/abc.txt"
}

resource "local_file" "def"{
  content = "def?"
  filename = "${path.module}/def.txt"
}

 

그 후 바로 terraform apply 명령을 실행합니다.

 

이미 프로비저닝된 구성인 resource "local_file" "abc" {}에 대해서는 변경사항이 없지만 새로 추가된 리소스 resource "local_file" "def" {} 에 대해서는 새로 생성하겠다는 실행 계획을 보여줍니다. 

 

 

이제 def 리소스를 주석 처리해보겠습니다.

resource "local_file" "abc"{
  content = "abc!"
  filename = "${path.module}/abc.txt"
}

#resource "local_file" "def"{
#  content = "def?"
#  filename = "${path.module}/def.txt"
#}

 

그 후 terraform apply 명령을 실행합니다.

 

 

terraform은 선언적으로 동작하기 때문에 현재의 코드 상태와 적용할 상태를 비교하여 일치시키는 동작을 수행합니다. 따라서 제거된 리소스에는 삭제 동작(destory)을 수행합니다.

이처럼 terraform plan과 apply는 생성 예정인 리소스의 생성 계획과 실행을 수행한다는 점에서 주로 같이 사용됩니다. 일반적으로 로컬에서 혼자 테스트하고 실핼할 때 terraform plan은 코드 작성 중 검증의 용도로 주로 활용되지만, terraform apply 명령으로만 리소스를 생성하는 경우가 대부분입니다. 하지만 인프라에 대한 외부 실행 환경 구성 시 앞서 확인한 terraform validate와 terraform plan을 먼저 실행해 변경 사항 적용 전에 검증하고 승인하는 단계를 추가할 수 있으므로 두 동작을 분리하는 것을 추천합니다.

destory

기본 사용법 : terraform [global options] destory [options]

 

terraform destory 명령은 테라폼 구성에서 관리하는 모든 리소스를 제거하는 명령어입니다. 만약 terraform 코드로 구성된 리소스의 일부만 제거하기 위해서는 terraform의 선언적 특성에 따라 삭제하려는 항목을 코드에서 제거하고, 다시 terraform apply를 하는 방법이 있습니다. 

 

 

terraform apply 동작과 마찬가지로 실행 계획을 보여준 후 삭제 여부를 묻는 입력이 활성화되었습니다. yes를 입력하여 리소스 삭제 작업을 진행합니다.

 

위에서 생성했었던 txt 파일들이 모두 삭제된 것을 확인하실 수 있습니다.

 

 

Terraform으로 AWS EC2 배포해보기

배포

directory 생성 및 이동

mkdir ec2-test
cd ec2-test

먼저 ec2-test라는 디렉토리를 생성한 후 이동합니다.

 

최신 Ubuntu AMI 확인

aws ec2 describe-images --owners 099720109477 \
    --filters "Name=name,Values=ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*" "Name=state,Values=available" \
    --query 'Images|sort_by(@, &CreationDate)[-1].[ImageId, Name]' --output text
    
    
#출력 결과
ami-0c7bc8b06b0c6fafb   ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-20240607


#환경변수 등록
UBUNTUID=ami-0c7bc8b06b0c6fafb

 

 

Terraform 

코드 작성

provider "aws" {
  region = "ap-northeast-2"
}

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

  user_data = <<-EOF
              #!/bin/bash
              echo "Hello, T101 Study" > index.html
              nohup busybox httpd -f -p 8080 &
              EOF

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

 

terraform 코드를 작성하는데 user_data에 EC2가 배포되면서 실행될 명령어를 작성합니다.

자세히 보자면 Hello, T101 Study 라는 내용이 적인 index.html 파일을 생성하고 8080 port에 연결합니다.

 

배포

terraform init

배포하기전에 가장 먼저 terraform init을 통해 프로바이더 및 root module을 초기화합니다.

 

init되고 나면 .terraform 디렉토리에 코드에 구성되어있던 providers 설정값들이 불러와집니다.=

 

terraform plan

 

plan을 통해 위와같이 어떤 리소스가 추가될 것인지, 수정될 것인지 확인 가능합니다. 

 

 

terraform apply

terraform apply 명령어를 통해 plan의 결과를 확인하고 적용합니다. 

 

그 후 aws managed console에서 ec2에 접근하신 뒤 확인해보시면 아래와 같이 EC2 인스턴스가 생성된 것을 확인하실 수 있습니다.

 

 

접속 확인!

while true; do curl --connect-timeout 1  http://${publicIP}:8080/ ; echo "------------------------------"; date; sleep 1; done

 

위에서 user_data 설정을 보시면 8080 port를 통해 통신이 가능하게끔 설정해놨습니다. EC2의 public IP를 통해 8080 port로 통신을 진행해보겠습니다.

 

 

접속이 되지 않아요!

 

위의 사진을 보시면  Failed to connect 인것을 확인하실 수 있습니다.

 

왜 접속이 되지 않을까요? 기본적으로 EC2에 접근하기 위해서는 보안 그룹에서 inboud 정책을 허용해주셔야 합니다.

 

terraform code 수정

...

resource "aws_instance" "example" {
  ami                    = "ami-0c7bc8b06b0c6fafb"
...

vpc_security_group_ids = [aws_security_group.instance.id]
...
}
resource "aws_security_group" "instance" {
  name = var.security_group_name

  ingress {
    from_port   = 8080
    to_port     = 8080
    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"
}

 

위에서 작성했던 main.tf 파일에 aws_security_group.instance{} 리소스를 작성합니다. ingress 설정에 8080 port에 대한 접근 설정을 작성하고 output을 통해 생성된 EC2의 public IP를 보여줍니다.

 

terraform plan & apply

 

그럼 위와 같이 aws_securoty_group.instance{} 리소스가 생성되고 output으로 public_ip를 보여주는 것을 확인하실 수 있습니다.

 

 

그 후 AWS Managed Console에서 EC2로 접근하신 뒤 생성한 EC2 인스턴스의 보안탭에서 설정된 보안그룹을 확인해보시면 위의 사진과 같이 terraform으로 생성한 8080 port에 대한 any inboud 규칙이 잘 설정된 것을 확인하실 수 있습니다.

 

접속 확인!

while true; do curl --connect-timeout 1  http://${publicIP}:8080/ ; echo "------------------------------"; date; sleep 1; done

 

정상적으로 통신이 되는 것을 확인하실 수 있습니다!

 

 

모든 실습 후 terraform destory 잊지 마세요!