카이도스의 Tech Blog

1주차 - 테라폼 기본 사용 1/2 본문

Terraform

1주차 - 테라폼 기본 사용 1/2

카이도스 2023. 7. 5. 17:03
728x90
반응형
CloudNet@-가시다(Gasida)님의 Terraform 스터디와
테라폼으로 시작하는 IaC책을 기준하여 정리하였습니다.

1. Iac와 테라폼

1-1. 테라폼 : Terraform 구성 언어는 선언적입니다. , 인프라에 대해 원하는 최종 상태를 설명합니다.

작동 방식 : 하시코프 Go 언어로 개발한 오픈 소스 도구, OS 마다 바이너리 파일이 존재하는데, Go코드는 하나의 바이너리 파일로 컴파일되며 terraform 명령어로 실행

  • terraform 바이너리가 AWS/GCP 등의 공급자를 대신해 API를 호출하여 리소스를 생성
  • 테라폼은 인프라 정보가 담겨 있는 테라폼 구성 파일을 생성하여 API를 호출

동작

https://developer.hashicorp .com/terraform/tutorials/aws-get-started/infrastructure-as-code?in=terraform%2Faws -get-started

  1. Scope - 프로젝트의 인프라를 식별합니다.
  2. Author - 인프라에 대한 구성을 작성합니다.
  3. Initialize - (초기화/준비) 테라폼이 인프라를 관리하는데 필요한 플러그인을 설치합니다.
    • 지정한 backend에 상태 저장을 위한 .tfstate 파일을 생성합니다. 여기에는 가장 마지막에 적용한 테라폼 내역이 저장됩니다.
    • init 작업을 완료하면, local에는 .tfstate에 정의된 내용을 담은 .terraform 파일이 생성됩니다.
    • 기존에 다른 개발자가 이미 .tfstate에 인프라를 정의해 놓은 것이 있다면, 다른 개발자는 init작업을 통해서 local에 sync를 맞출 수 있습니다.
  4. Plan - (예측) 사용자의 구성과 일치하도록 테라폼이 변경할 내용을 미리 파악합니다.
    • 정의한 코드가 어떤 인프라를 만들게 되는지 미리 예측 결과를 보여줍니다. 단, plan을 한 내용에 에러가 없다고 하더라도, 실제 적용되었을 때는 에러가 발생할 수 있습니다.
    • Plan 명령어는 어떠한 형상에도 변화를 주지 않습니다.
  5. Apply - (생성) 계획된 변경을 수행합니다.
    • 실제로 인프라를 배포하기 위한 명령어입니다. apply를 완료하면, AWS 상에 실제로 해당 인프라가 생성되고 작업 결과가 backend의 .tfstate 파일에 저장됩니다.
    • 해당 결과는 local의 .terraform 파일에도 저장됩니다.

기본 개념

  • resource : 실제로 생성할 인프라 자원을 의미
  • provider : Terraform으로 정의할 Infrastructure Provider를 의미
  • output : 인프라를 프로비저닝 한 후에 생성된 자원을 output 부분으로 뽑을 수 있음. Output으로 추출한 부분은 이후에 remote state에서 활용 가능
  • backend : terraform의 상태를 저장할 공간을 지정하는 부분. backend를 사용하면 현재 배포된 최신 상태를 외부에 저장하기 때문에 다른 사람과의 협업이 가능
  • module : 공통적으로 활용할 수 있는 인프라 코드를 한 곳으로 모아서 정의하는 부분. Module을 사용하면 변수만 바꿔서 동일한 리소스를 손쉽게 생성할 수 있음
  • remote state : remote state를 사용하면 VPC, IAM 등과 같이 여러 서비스가 공통으로 사용하는 것을 사용할 수 있음. tfstate파일이 저장되어 있는 backend 정보를 명시하면, terraform이 해당 backend에서 output 정보들을 가져옴

코드형 인프라 장점

  • 자급식 배포 Self-service : 배포 프로세스를 자동화 할 수 있으며, 개발자는 필요할 때마다 자체적으로 배포를 진행 할 수 있음
  • 속도와 안정성 Speed and safety : 자동, 일관되고 오류 적음
  • 문서화 Documentation : 시스템 관리자 조직만 인프라에 관한 정보를 독점하는 것이 아니라 누구나 읽을 수 있는 소스 파일로 인프라 상태를 나타낼수 있음
  • 버전 관리 Version control : 인프라의 변경 내용이 모두 기록된 코드형 인프라 소스 파일을 저장할 수 있으므로 버전을 쉽게 관리할 수 있음, 문제 발생 시 코드 원복
  • 유효성 검증 Validation : 인프라 상태가 코드로 정의되어 있으면 코드가 변경될 때마다 검증을 수행하고 일련의 자동화된 테스트를 실행할 수 있음
  • 재사용성 Reuse : 인프라를 재사용 가능한 모듈로 패키징할 수 있어 검증된 모듈로 일관되게 배포할 수 있음
  • 행복 Happiness 😊

2. 실행 환경 구성

2.1 테라폼 환경 구성

A. wsl 설치 - 링크1, 링크2

 

WSL Windows 10 Ubuntu Linux 20.04 LTS 세팅

*WSL 마이크로소프트 공식 홈페이지 https://learn.microsoft.com/ko-kr/windows/wsl/ 현재 실행 환경에...

blog.naver.com

 

WslRegisterDistribution failed with error: 에러 해결 방법 / 우분투 설치 에러 해결책 정리

우분투 설치 에러 우분투(Ubuntu)를 설치할 때 아래와 같은 에러가 발생했다. Installing, this may take a few minutes... WslRegisterDistribution failed with error: 0xc03a001a Error: 0xc03a001a ?? ??? ??? ???? 해결책 방법 1. 제

parkjh7764.tistory.com


B. Shell 접속 후 기본셋팅

sudo apt update && sudo apt upgrade

C. 테라폼 설치 및 확인 - 링크 (Linux → Ubuntu/Debian 선택)

wget -O- https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list
sudo apt update && sudo apt install terraform

# 테라폼 버전 정보 확인
terraform version
Terraform v1.5.2
on linux_amd64

2.2 IDE 구성

통합 개발 환경 IDE는 비주얼 스튜디오 코드 Visual Studio Code (VS Code)를 설치 후 실습에 사용 - 링크

  • Extentions (확장) 설치
    • HashiCorp HCL : syntax highlighting for HCL files - 링크
    • HashiCorp Terraform : Highlighting syntax from Terraform 등 - 링크
  • VS Code에서 터미널 윈도우 추가


2.3 AWS CLI 설치 및 자격증명

AWS CLI v2 설치 - 링크 및 IAM User 자격 증명 설정 - 링크 환경변수

# CLI 설치
sudo apt install -y awscli

# 버전 확인
aws --version
aws-cli/1.18.69 Python/3.8.10 Linux/4.4.0-22621-Microsoft botocore/1.16.19

# 자격증명
aws configure
AWS Access Key ID [None]: AKIAT....
AWS Secret Access Key [None]: nQbQ....
Default region name [None]: ap-northeast-2
Default output format [None]: json

# 확인
aws s3 ls
2023-02-01 12:34:01 pjh-k8s-s3
  • 실습에 필요한 툴 설치 
sudo apt install -y tree jq

3. 기본 사용

3.1 주요 커멘드

# 실습 디렉터리 생성
mkdir -p workspaces/03.start && cd workspaces/03.start

# 파일생성
vi main.tf
resource "local_file" "abc" {
content  = "abc!"
filename = "${path.module}/abc.txt"
}

# 서브커맨드 help 지원
terraform console -help
terraform init -help

# init 초기화
terraform init
ls -al
tree .terraform
.terraform
└── providers
    └── registry.terraform.io
        └── hashicorp
            └── local
                └── 2.4.0
                    └── linux_amd64
                        └── terraform-provider-local_v2.4.0_x5

Plan & apply

  • terraform plan 명령은 테라폼으로 적용할 인프라의 변경 사항에 관한 실행 계획생성하는 동작. 또한 출력되는 결과를 확인하여 어떤 변경이 적용될지 사용자가 미리 검토하고 이해하는데 도움을 줌.
    • 변경 사항을 실제로 적용하지는 않으므로, 적용 전에 예상한 구성이 맞는지 검토
  • terraform applyplan 계획을 기반으로 작업을 실행.
# plan 실행 : 구성 내용을 바탕으로 어떤 리소스가 생성되는지 상세 내역 출력, 기본값 자동 입력 적용
terraform plan
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # local_file.abc will be created
  + resource "local_file" "abc" {
      + content              = "abc!"
      + content_base64sha256 = (known after apply)
      + content_base64sha512 = (known after apply)
      + content_md5          = (known after apply)
      + content_sha1         = (known after apply)
      + content_sha256       = (known after apply)
      + content_sha512       = (known after apply)
      + directory_permission = "0777"
      + file_permission      = "0777"
      + filename             = "./abc.txt"
      + id                   = (known after apply)
    }

Plan: 1 to add, 0 to change, 0 to destroy. # 하나의 리소스가 추가되고, 변경되거나 삭제되는 것은 없을 예정
  • -detailed-exitcode : plan 추가 옵션으로, 파이프라인 설계에서 활용 가능, exitcode가 환경 변수로 구성됨
  • -auto-approve 자동 승인 기능 부여 옵션
```bash
# plan 결과를 시스템 코드로 출력
terraform **plan** -detailed-exitcode
...

# 코드 확인 : 0(변경 사항이 없는 성공), 1(오류가 있음), 2(변경 사항이 있는 성공)
**echo $?**
2

# (참고) apply 결과에 적용 시도
terraform apply -auto-approve -detailed-exitcode
```
  • apply
# apply 실행 : no 입력
terraform apply
...
Enter a value: no
...

# plan 결과를 지정된 파일(바이너리 형태) 이름으로 생성
terraform plan -out=tfplan
cat tfplan
file tfplan

# apply 실행 : 실행계획이 있으므로 즉시 적용됨
terraform apply tfplan
ls -al abc.txt

# apply 실행 : 어떻게 되나요?
# 테라폼은 선언적 구성 관리를 제공하는 언어로 멱등성 idempotence을 갖고, 상태를 관리하기 때문에 동일한 구성에 대해서는 다시 실행하거나 변경하는 작업을 수행하지 않음
terraform apply
...

terraform state list

[도전과제1] EC2 웹 서버 배포 
목표 : EC2 웹 서버 배포 : Ubuntu 에 apache(httpd) 를 설치하고 index.html 생성(닉네임 출력)하는 userdata 를 작성해서 설정 배포, 포트는 TCP 80 후 curl 접속 - 해당 테라폼 코드(파일)를 작성

  • 코드파일 생성(별도 폴더 생성하여 진행)
# 폴더 생성
cd workspaces
mkdir EC2 && cd EC2

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

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

  user_data = <<-EOF
  #!/bin/bash
  sudo apt update -y
  sudo apt install apache2 -y
  sudo echo "PJH WebSrv" > /var/www/html/index.html
  EOF

  tags = {
    Name = "PJH-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"]
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

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

output "public_ip" {
  value       = aws_instance.example.public_ip
  description = "The public IP of the Instance"
}
EOT
  • 배포 실행 및 확인
# init
terraform init

# 배포 및 확인
terraform plan
terraform apply -auto-approve
....
Apply complete! Resources: 2 added, 0 changed, 0 destroyed.

Outputs:

public_ip = "54.180.25.49"

# 웹 서버 접속 시도 : 터미널2에서 실행
# process 올라오는데 시간이 1~2분 정도 소요되어서, ec2 생성되면 1~2분 정도 후에 curl 접속이 가능하다
PIP=<각자 자신의 EC2 IP>
PIP=54.180.25.49
while true; do curl --connect-timeout 1  http://$PIP/ ; echo "------------------------------"; date; sleep 1; done
------------------------------
Fri Jul  7 12:29:43 KST 2023
PJH WebSrv
------------------------------

# 삭제
terraform destroy -auto-approve


3.2 주요 커멘드

A. HCL HashiCorp configuration language은 하시코프사에서 IaC와 구성 정보를 명시하기 위해 개발된 오픈 소스 도구이다 - 링크

  • IaC는 수동 프로세스가 아닌 코드를 통해 인프라를 관리하고 프로비저닝 하는 것을 말함
  • 테라폼에서 HCL이 코드의 영역을 담당한다. HCL은 쉽게 읽을 수 있고 빠르게 배울 수 있는 언어의 특징을 가진다.
  • 인프라가 코드로 표현되고, 이 코드는 곧 인프라이기 때문에 선언적 특성을 갖게 되고 튜링 완전한 Turing-complete 언어적 특성을 갖는다.
  • 즉, 일반적인 프로그래밍 언어의 조건문 처리 같은 동작이 가능하다. 자동화와 더불어, 쉽게 버저닝해 히스토리를 관리하고 함께 작업 할 수 있는 기반을 제공.

  • HCL에서 변수와 문자열 값을 함께 사용하는 인터폴레이션 interpolation 표현 방식을, JSON을 사용하는 다른 IaC 도구와 비교

  • HCL을 사용하면 동일한 내용을 JSON으로 표현하는 것보다 더 간결하고 읽기 쉽게 작성할 수 있다

HCL 표현식

// 한줄 주석 방법1
# 한줄 주석 방법2

/*
라인
주석
*/

locals {
  key1     = "value1"     # = 를 기준으로 키와 값이 구분되며
  myStr    = "TF ♡ UTF-8" # UTF-8 문자를 지원한다.
  multiStr = <<EOF
  Multi
  Line
  String
  with anytext
EOF

  boolean1    = true   # boolean true
  boolean2    = false  # boolean false를 지원한다.
  deciaml     = 123    # 기본적으로 숫자는 10진수,
  octal       = 0123   # 0으로 시작하는 숫자는 8진수,
  hexadecimal = "0xD5" # 0x 값을 포함하는 스트링은 16진수,
  scientific  = 1e10   # 과학표기 법도 지원한다.

  # funtion 호출 예
  myprojectname = format("%s is myproject name", var.project)

  # 3항 연산자 조건문을 지원한다.
  credentials = var.credentials == "" ? file(var.credentials_file) : var.credentials
}
  • HCL 표현식에서는 코드에서 사용되는 주석 표기부터 변수 정의 등을 포함하고 프로그래밍적인 연산과 구성 편의성을 높이기 위한 function 제공 - 링크
  • 테라폼으로 인프라를 구성하기 위한 선언 블록도 다수 존재 : terraform, resource, data, variable, local, output

3.3 테라폼 블록

A. 테라폼 블록 : 테라폼 구성을 명시하는 데 사용

  • 테라폼 버전이나 프로바이더 버전과 같은 값들은 자동으로 설정되지만, 함께 작업할 때는 버전을 명시적으로 선언하고 필요한 조건을 입력하여 실행 오류를 최소화 할 것을 권장한다.
  • 오늘 실행하던, 3년 후에 실행하던 동일한 결과를 얻을 수 있어야 한다!
terraform {
  required_version = "~> 1.3.0" # 테라폼 버전

  required_providers { # 프로바이더 버전을 나열
    random = {
      version = ">= 3.0.0, < 3.1.0"
    }
    aws = {
      version = "4.2.0"
    }
  }

  cloud { # Cloud/Enterprise 같은 원격 실행을 위한 정보
    organization = "<MY_ORG_NAME>"
    workspaces {
      name = "my-first-workspace"
    }
  }

  backend "local" { # state를 보관하는 위치를 지정
    path = "relative/path/to/terraform.tfstate"
  }
}
  • 테라폼 내에서 버전이 명시되는 terraform, module에서 사용 가능하며 버전에 대한 제약을 둠으로써 테라폼, 프로바이더, 모듈이 항상 의도한 정의대로 실행되는 것을 목적으로 한다.
  • 버전 체계는 시맨틱 버전 관리 Semantic Versioning 방식을 따른다
# version = Major.Minor.Patch
version = 1.3.4
  • 시맨틱 버전 관리 방식
    • Major 버전 : 내부 동작의 API가 변경 또는 삭제되거나 하위 호환이 되지 않는 버전
    • Minor 버전 : 신규 기능이 추가되거나 개선되고 하위 호환이 가능한 버전
    • Patch 버전 : 버그 및 일부 기능이 개선된 하위 호환이 가능한 버전
  • 버전 제약 구문은 다른 프로그램 언어에서의 종속성 관리 시스템과 흡사하다.
    • = 또는 연산자 없음 : 지정된 버전만을 허용하고 다른 조건과 병기할 수 없다.
    • != : 지정된 버전을 제외한다.
    • , >=, <, <= : 지정된 버전과 비교해 조건에 맞는 경우 허용한다.
      • ~> : 지정된 버전에서 가장 자리수가 낮은 구성요소만 증가하는 것을 허용한다.
        • ~> x.y 인 경우 y 버전에 대해서만, ~> x.y.z인 경우 z 버전에 대해서만 보다 큰 버전을 허용한다
  • 테라폼 버전 관리로 비유된 선언 방식의 의미


B. 테라폼 버전 required_version

# 디렉터리 이동
cd ..
cd 03.start

# 테라폼 버전에 대한 변경 내역과 기존 버전 정보 확인
terraform version
Terraform v1.5.2

# 코드 파일 수정 main.tf
terraform {
  required_version = ">= 1.0.0"
}

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

# init 실행
terraform init
Initializing the backend...

Initializing provider plugins...
- Reusing previous version of hashicorp/local from the dependency lock file
- Using previously-installed hashicorp/local v2.4.0

Terraform has been successfully initialized!

You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.

If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.

C. 프로바이더 버전

테라폼 0.13 버전 이전에는 provider 블록에 함께 버전을 명시했지만 해당 버전 이후 프로바이더 버전은 terraform 블록에서 required_providers에 정의

  • 각 프로바이더의 이름에 소스 경로와 버전을 명시하며, 테라폼 레지스트리 공식 페이지에서 원하는 프로바이더를 선택한 다음 화면에서 우측 상단의 [USE PROVIDER]를 클릭하면 테라폼 코드에 해당 버전을 사용하는 샘플 코드가 표기된다 - 링크

https://registry.terraform.io/providers/hashicorp/local/latest

  • 코드 파일 수정 : 프로바이더 버전을 과하게 높게 설정 - 링크
terraform {
  required_version = ">= 1.0.0"
  required_providers {
    local = {
      source  = "hashicorp/local"
      version = ">=10000.0.0"
    }
  }
}

resource "local_file" "abc" {
  content  = "123!"
  filename = "${path.module}/abc.txt"
}
  • init
# 실행 결과는?
terraform init -upgrade
Initializing the backend...

Initializing provider plugins...
- Finding hashicorp/local versions matching ">= 10000.0.0"...
╷
│ Error: Failed to query available provider packages
│ 
│ Could not retrieve the list of available versions for provider hashicorp/local: no available releases match the given constraints >=
│ 10000.0.0
  • 코드 파일 수정 : local 프로바이더 버전을 >= 2.0.0으로 수정
terraform {
  required_version = ">= 1.0.0"
  required_providers {
    local = {
      source  = "hashicorp/local"
      version = ">= 2.0.0"
    }
  }
}

resource "local_file" "abc" {
  content  = "123!"
  filename = "${path.module}/abc.txt"
}
  • init(성공)
# 실행 결과는?
terraform init -upgrade
....
Terraform has been successfully initialized!
....

D. 백엔드블록

  • 백엔드 블록의 구성은 테라폼 실행 시 저장되는 **State(상태 파일)**의 저장 위치를 선언한다.
  • 주의할 점은 하나의 백엔드만 허용한다는 점이다.
  • 테라폼은 State의 데이터를 사용해 코드로 관리된 리소스를 탐색하고 추적한다.
  • 작업자 간의 협업을 고려한다면 테라폼으로 생성한 리소스의 상태 저장 파일을 공유할 수 있는 외부 백엔드 저장소가 필요하다.
  • 그리고 State에는 외부로 노출되면 안 되는 패스워드 또는 인증서 정보 같은 민감한 데이터들이 포함될 수 있으므로 State의 접근 제어가 필요하다.

State 잠금 동작

  • 기본적으로 활성화되는 백엔드local이다.
  • 상태를 작업자의 로컬 환경저장하고 관리하는 방식이다.
  • 이 밖의 다른 백엔드 구성은 동시에 여러 작업자가 접근해 사용할 수 있도록 공유 스토리지 같은 개념을 갖는다.
  • 공유되는 백엔드에 State가 관리되면 테라폼이 실행되는 동안 .terraform.tfstate.lock.info 파일이 생성되면서 해당 State를 동시에 사용하지 못하도록 잠금 처리를 한다.
  • 파일 생성을 확인하고 싶다면 terraform apply를 실행하고 생성되는 잠금 파일을 확인해보자. 잠금 파일 내의 정보는 다음과 같다.
ID:        30773305-a78d-49f1-8f3f-5604d742b66f
Operation: OperationTypeApply                      # 어떤 동작으로 인해 해당 잠금 파일이 생성되었는지 명기
Info:
Who:       gasida@seojonghoui-MacBookPro.local     # 작업자 정보
Version:   1.5.1                                   # 실행한 테라폼 버전
Created:   2023-06-28 12:23:59.113608 +0000 UTC
Path:      state/terraform.tfstate                 # 잠긴 state 파일의 위치
  • 잠금 파일이 테라폼 실행에 어떤 영향을 주는지 확인해보자.
  • 코드 파일 수정 : content 내용을 수정!
resource "local_file" "abc" {
  content  = "123456!"
  filename = "${path.module}/abc.txt"
}
  • 터미널 나누기로 2개 창 띄우기
# 터미널1
terraform apply
...
Enter a value: 대기

# 터미널2
terraform apply
...
ls -a
  • apply 적용
# 터미널1 취소 후 apply
terraform apply -auto-approve

백엔드 설정 변경

  • 현재 State 파일 정보 확인
ls terraform.tfstate*
terraform.tfstate        terraform.tfstate.backup

# 리소스 확인
terraform state list

# State 파일 정보 확인
cat terraform.tfstate
  • 백엔드가 설정되면 다시 init 명령을 수행해 State의 위치를 재설정해야 한다.
  • 백엔드 블록에 local을 정의해 terraform init을 수행해본다.
terraform {
  backend "local" {
    path = "state/terraform.tfstate"
  }
}

resource "local_file" "abc" {
  content  = "123456!"
  filename = "${path.module}/abc.txt"
}
  • init
# init 시 백엔드 변경에 따른 마이그레이션 안내
terraform init
...
Enter a value: yes
...

#
ls terraform.tfstate*
tree state
cat state/terraform.tfstate | jq
cat state/terraform.tfstate | jq -r .serial

# 
terraform apply -auto-approve
  • 코드 파일 수정 : content 내용을 수정!
terraform {
  backend "local" {
    path = "state/terraform.tfstate"
  }
}

resource "local_file" "abc" {
  content  = "123456789!"
  filename = "${path.module}/abc.txt"
}
  • apply
#
terraform plan
terraform apply -auto-approve

#
cat abc.txt
ls terraform.tfstate*
tree state
cat state/terraform.tfstate | jq
cat state/terraform.tfstate | jq -r .serial

# 이전 사용 state 파일 삭제 후 확인
rm -rf terraform.tfstate*
ls -al

# 
terraform plan
terraform apply -auto-approve
cat abc.txt
  • 추가 옵션1 (이전 구성 유지) : -migrate-state는 terraform.tfstate의 이전 구성에서 최신의 state 스냅샷을 읽고 기록된 정보를 새 구성으로 전환한다.
  • 추가 옵션2 (새로 초기화) : -reconfigure는 init을 실행하기 전에 terraform.tfstate 파일을 삭제해 테라폼을 처음 사용할 때처럼 이 작업 공간(디렉터리)을 초기화 하는 동작이다.

3.4 리소스

A. 리소스 구성

  • 실습을 위해서 03.end 디렉터리를 신규 생성 후 열기 → main.tf 파일 생성
cd ..
rm -rf 03.start
mkdir 03.end
cd 03.end
  • 리소스 블록은 resource로 시작한다. 이후 리소스 블록이 생성할 ‘리소스 유형’을 정의한다.
  • 리소스 선언 : 리소스 유형(프로바이더이름_제공리소스유형), 동일한 유형에 대한 식별자 역할로 고유한 이름, 구성 인수들이 이름 뒤에 중괄호 내에 선언됨
resource "<리소스 유형>" "<이름>" {
  <인수> = <값>
}

resource "local_file" "abc" {
  content  = "123"
  filename = "${path.module}/abc.txt"
}
  • 리소스에서 사용되는 유형들은 프로바이더에 종속성을 갖는다. 특정 프로바이더의 유형만 추가해도 init 수행 시 해당 프로바이더를 설치한다.
resource "local_file" "abc" {
  content  = "123"
  filename = "${path.module}/abc.txt"
}

resource "aws_instance" "web" {
  ami = "ami-a1b2c3d4"
  instance_type = "t2.micro"  
}
  • init
# init 시 프로바이더 선언 없이도 리소스 추가로 자동 인식된 프로바이더 요구사항과 초기화
terraform init
tree .terraform
.terraform
└── providers
    └── registry.terraform.io
        └── hashicorp
            ├── aws
            │   └── 5.7.0
            │       └── linux_amd64
            │           └── terraform-provider-aws_v5.7.0_x5
            └── local
                └── 2.4.0
                    └── linux_amd64
                        └── terraform-provider-local_v2.4.0_x5
  • 리소스 동작 보조 추가 메타인수를 정의 할 수 있다 → 뒤에서 자세히 설명
    • depends_on : 종속성을 선언하며, 선언된 구성요소와의 생성 시점에 대해 정의
    • count : 선언된 개수에 따라 여러 리소스를 생성
    • for_each : map 또는 set 타입의 데이터 배열의 값을 기준으로 여러 리소스를 생성
    • provider : 동일한 프로바이더가 다수 정의되어 있는 경우 지정
    • lifecycle : 리소스의 수명주기 관리
    • provisioner : 리소스 생성 후 추가 작업 정의
    • timeouts : 프로바이더에서 정의한 일부 리소스 유형에서는 create, update, delete에 대한 허용 시간 정의 가능

B. 종속성

  • 테라폼 종속성은 resource, module 선언으로 프로비저인되는 각 요소의 생성 순서를 구분짓는다.
  • 기본적으로 다른 리소스에서 값을 참조해 불러올 경우 생성 선후 관계에 따라 작업자가 의도하지는 않았지만 자동으로 연관 관계가 정의되는 암시적 종속성을 갖게 되고, 강제로 리소스 간 명시적 종속성을 부여할 경우에는 메타인수인 depends_on을 활용한다.

  • 코드 파일 수정
resource "local_file" "abc" {
  content  = "123!"
  filename = "${path.module}/abc.txt"
}

resource "local_file" "def" {
  content  = "456!"
  filename = "${path.module}/def.txt"
}
  • apply 실행 : 서로 선후 관계가 없는 동일한 수준으로, 병렬(동시) 실행
  • VS Code 확장 graphviz 설치
#
terraform apply -auto-approve
...
Plan: 2 to add, 0 to change, 0 to destroy.
local_file.def: Creating...
local_file.abc: Creating...
local_file.abc: Creation complete after 0s [id=5f30576af23a25b7f44fa7f5fdf70325ee389155]
local_file.def: Creation complete after 0s [id=b9fbde4d33ab9c450a7ce303fb4788c9d2db9aed]

# 리소스 확인
ls *.txt
abc.txt  def.txt

terraform state list
local_file.abc
local_file.def

# graph 확인 > graph-1.dot 파일 선택 후 오른쪽 상단 DOT 클릭
terraform graph
terraform graph > graph-1.dot

# 모든 리소스 제거
terraform destroy -auto-approve
ls *.txt
terraform state list
  • 리소스 참조값을 설정해 두 개의 리소스 간 암시적 종속성 부여
resource "local_file" "abc" {
  content  = "123!"
  filename = "${path.module}/abc.txt"
}

resource "local_file" "def" {
  content  = local_file.abc.content
  filename = "${path.module}/def.txt"
}
  • apply : 커맨드 생성에 순서가 발생한 종속성 있는 두 개의 리소스
#
terraform apply -auto-approve
...
Plan: 2 to add, 0 to change, 0 to destroy.
local_file.abc: Creating... <- 먼저 만들고
local_file.abc: Creation complete after 0s [id=5f30576af23a25b7f44fa7f5fdf70325ee389155]
local_file.def: Creating... <- 그 다음 만듬
local_file.def: Creation complete after 0s [id=5f30576af23a25b7f44fa7f5fdf70325ee389155]

ls *.txt
terraform state list
cat abc.txt
cat def.txt
diff abc.txt def.txt

# graph 확인 > graph-2.dot 파일 선택 후 오른쪽 상단 DOT 클릭
terraform graph
terraform graph > graph-2.dot
  • 리소스의 속성을 주입하지 않아도 두 리소스 간에 종속성이 필요한 경우에, depends_on 선언으로 적용 가능
resource "local_file" "abc" {
  content  = "123!"
  filename = "${path.module}/abc.txt"
}

resource "local_file" "def" {
  depends_on = [
    local_file.abc
  ]

  content  = "456!"
  filename = "${path.module}/def.txt"
}
  • apply
#
terraform destroy -auto-approve
terraform apply -auto-approve
...

# graph 확인 > graph-3.dot 파일 선택 후 오른쪽 상단 DOT 클릭
terraform graph
terraform graph > graph-3.dot

C. 리소스 속성 참조

  • 리소스 구성에서 참조 가능한 값은 인수속성이다
    • 인수 : 리소스 생성 시 사용자가 선언하는 값
    • 속성 : 사용자가 설정하는 것은 불가능하지만 리소스 생성 이후 획득 가능한 리소스 고유 값
  • 리소스 인수의 선언과 참조 가능한 인수 및 속성 패턴
# Terraform Code
resource "<리소스 유형>" "<이름>" {
  <인수> = <값>
}

# 리소스 참조
<리소스 유형>.<이름>.<인수>
<리소스 유형>.<이름>.<속성>

 

  • 아래 코드는 쿠버네티스 프로바이더의 Namespace 리소스를 생성하고 이후 Secret을 해당 Namespace에 생성하는 종속성을 리소스 인수 값 값으로 생성하는 예이다. Namespace의 이름만 변경해도, 해당 Namespace를 참조하는 모든 리소스가 업데이트되어 영향을 받는다.
resource "kubernetes_namespace" "example" {
  metadata {
    annotations = {
      name = "example-annotation"
    }
    name = "terraform-example-namespace"
  }
}

resource "kubernetes_secret" "example" {
  metadata {
    namespace = kubernetes_namespace.example.metadata.0.name # namespace 리소스 인수 참조
    name      = "terraform-example"
  }
  data = {
    password = "P4ssw0rd"
  }
}
  • 리소스가 생성될 때, 사용자가 입력한 ‘인수’를 받아 실제 리소스가 생성되면 일부 리소스는 자동으로 기본값이나 추가되는 ‘속성’이 부여된다.
  • 각 리소스마다 문서를 확인해보면 인수Arguments로 표현되어 있으며, 리소스 생성 후 추가되는 속성 값으로 Attributes에 안내되어 있다 - 링크
  • 리소스 속성을 참조하는 다른 리소스 또는 구성요소에서는 생성 후의 속성 값들도 인수로 가져올 수 있다.

D. 수명주기

  • lifecycle은 리소스의 기본 수명주기를 작업자가 의도적으로 변경하는 메타인수다. 메타인수 내에는 아래 선언이 가능.
    • create_before_destroy (bool): 리소스 수정 시 신규 리소스를 우선 생성하고 기존 리소스를 삭제
    • prevent_destroy (bool): 해당 리소스를 삭제 Destroy 하려 할 때 명시적으로 거부
    • ignore_changes (list): 리소스 요소에 선언된 인수의 변경 사항을 테라폼 실행 시 무시
    • precondition: 리소스 요소에 선언해 인수의 조건을 검증
    • postcondition: Plan과 Apply 이후의 결과를 속성 값으로 검증

create_before_destroy (bool): 리소스 수정 시 신규 리소스를 우선 생성하고 기존 리소스를 삭제

  • 예를 들어 image 변경 되는 경우 해당 VM 리소스를 삭제하고 다시 생성
  • 테라폼의 기본 수명주기삭제 후 생성이기 때문에 작업자가 의도적으로 수정된 리소스를 먼저 생성하기를 원할 수 있다.
  • 이 경우 create_before_destroytrue로 선언되면 의도한 생성을 실행한 후 삭제로 동작한다.
  • 하지만 생성되는 리소스가 기존 리소스로 인해 생성이 실패되거나 삭제 시 함께 삭제될 수 있으니 주의해야 한다.
    • 잘못된 사례 1 : 리소스의 명시적 구분이 사용자가 지정한 특정 이름이나 ID인 경우 기존 리소스에 할당되어 있기 때문에 생성 실패
    • 잘못된 사례 2 : 생성 후 삭제 시 동일한 리소스에 대한 삭제 명령이 수행되어 리소스가 모두 삭제

[실습] 잘못된 사례 2

  • 실습을 위해서 03.lifecycle 디렉터리를 신규 생성 후 열기 → main.tf 파일 생성
cd ..
rm -rf 03.end
mkdir 03.lifecycle
cd 03.lifecycle
resource "local_file" "abc" {
  content  = "lifecycle - step 1"
  filename = "${path.module}/abc.txt"

  lifecycle {
    create_before_destroy = false
  }
}
  • 처음 apply 수행
# 
terraform init && terraform plan && terraform apply -auto-approve
terraform state list

# 파일 내용 확인
cat abc.txt
  • content 내용 수정 저장
resource "local_file" "abc" {
  content  = "lifecycle - step 1111111111111111"
  filename = "${path.module}/abc.txt"

  lifecycle {
    create_before_destroy = false
  }
}
  • create_before_destroy = false 동작 확인
#
terraform plan && terraform apply -auto-approve
...
-/+ destroy and then create replacement

Terraform will perform the following actions:

  # local_file.abc must be replaced
-/+ resource "local_file" "abc" {
      ~ content              = "lifecycle - step 1" -> "lifecycle - step 1111111111111111" # forces replacement
...

# 파일 내용 확인
cat abc.txt
  • content 내용 수정 저장
resource "local_file" "abc" {
  content  = "lifecycle - step 2"
  filename = "${path.module}/abc.txt"

  lifecycle {
    create_before_destroy = true
  }
}
  • create_before_destroy = true 동작 확인
#
terraform plan && terraform apply -auto-approve
...
+/- create replacement and then destroy

Terraform will perform the following actions:

  # local_file.abc must be replaced
+/- resource "local_file" "abc" {
      ~ content              = "lifecycle - step 11" -> "lifecycle - step 2" # forces replacement
...

# 파일 내용 확인
terraform state list
cat abc.txt

# 파일 내용 확인
terraform plan && terraform apply -auto-approve
cat abc.txt
terraform state list

[도전과제2] AWS S3/DynamoDB 백엔드

# 디렉터리 생성
mkdir  s3_db
cd s3_db

# 환경변수설정
cat <<EOT > var.tf
variable "region" {
  description = "nomad server region"
  default     = "ap-northeast-2"
}
variable "nickname" {
  description = "my nick name"
  default     = "pjh"
}
EOT

 

  • backend.tf 생성
provider "aws" {
  region = var.region
}

# Terraform의 State를 저장 할 S3
resource "aws_s3_bucket" "mys3bucket" {
  bucket = "${var.nickname}-t101study-tfstate"
}

# State Versioning을 위하여 만들어진 S3를 Versioning 설정 추가
resource "aws_s3_bucket_versioning" "mys3bucket_versioning" {
  bucket = aws_s3_bucket.mys3bucket.id
  versioning_configuration {
    status = "Enabled"
  }
}

# RDS 
resource "aws_dynamodb_table" "mydynamodbtable" {
  name         = "terraform-locks"
  billing_mode = "PAY_PER_REQUEST" # 사용량에 따른 과금 모드
  hash_key     = "LockID" # DynamoDB 테이블에서 사용 될 해시 키의 이름

  attribute {
    name = "LockID" 
    type = "S" #
  }
}

output "s3_bucket_arn" {
  value       = aws_s3_bucket.mys3bucket.arn
  description = "The ARN of the S3 bucket"
}

output "dynamodb_table_name" {
  value       = aws_dynamodb_table.mydynamodbtable.name
  description = "The name of the DynamoDB table"
}
  • 배포
# init
terraform init

# 배포
terraform plan && terraform apply -auto-approve
....
dynamodb_table_name = "terraform-locks"
s3_bucket_arn = "arn:aws:s3:::pjh-t101study-tfstate"

# 확인
terraform state list
aws_dynamodb_table.mydynamodbtable
aws_s3_bucket.mys3bucket
aws_s3_bucket_versioning.mys3bucket_versioning

# DynamoDB 테이블 생성 확인
aws dynamodb list-tables --output text
TABLENAMES      terraform-locks

aws dynamodb describe-table --table-name terraform-locks | jq
aws dynamodb describe-table --table-name terraform-locks --output table
----------------------------------------------------------------------------------------------
|                                        DescribeTable                                       |
+--------------------------------------------------------------------------------------------+
||                                           Table                                          ||
|+------------------+-----------------------------------------------------------------------+|
||  CreationDateTime|  1688786348.954                                                       ||
||  ItemCount       |  0                                                                    ||
||  TableArn        |  arn:aws:dynamodb:ap-northeast-2:비공:table/terraform-locks           ||
||  TableId         |  비공                                                                 ||
||  TableName       |  terraform-locks                                                      ||
||  TableSizeBytes  |  0                                                                    ||
||  TableStatus     |  ACTIVE                                                               ||
|+------------------+-----------------------------------------------------------------------+|
|||                                  AttributeDefinitions                                  |||
||+-------------------------------------------------------+--------------------------------+||
|||  AttributeName                                        |  LockID                        |||
|||  AttributeType                                        |  S                             |||
||+-------------------------------------------------------+--------------------------------+||
|||                                   BillingModeSummary                                   |||
||+---------------------------------------------------------+------------------------------+||
|||  BillingMode                                            |  PAY_PER_REQUEST             |||
|||  LastUpdateToPayPerRequestDateTime                      |  1688786348.954              |||
||+---------------------------------------------------------+------------------------------+||
|||                                        KeySchema                                       |||
||+-------------------------------------------------------+--------------------------------+||
|||  AttributeName                                        |  LockID                        |||
|||  KeyType                                              |  HASH                          |||
||+-------------------------------------------------------+--------------------------------+||
|||                                  ProvisionedThroughput                                 |||
||+-------------------------------------------------------------------------+--------------+||
|||  NumberOfDecreasesToday                                                 |  0           |||
|||  ReadCapacityUnits                                                      |  0           |||
|||  WriteCapacityUnits                                                     |  0           |||
||+-------------------------------------------------------------------------+--------------+||
  • 삭제
terraform destroy -auto-approve
728x90
반응형

'Terraform' 카테고리의 다른 글

3주차 - 기본 사용 3/3 & 프로바이더  (0) 2023.07.21
2주차 - 테라폼 기본 사용 2/3  (0) 2023.07.12
[CHAPTER 2] ASG/ELB  (0) 2023.06.01
[CHAPTER 1&2] 기초  (0) 2023.05.23
Comments