Skip to Content
Archive테라폼 기본 3/3

프로비저너

테라폼의 프로비저너는 리소스 생성, 변경 또는 삭제 작업을 보완하기 위해 사용됩니다. 프로비저너는 특정 리소스를 초기 설정, 설정 변경, 시스템에 소프트웨어 설치 등의 작업을 수행합니다.

일반적인 사용 사례

  • 파일 업로드: 서버에 필요한 설정 파일을 업로드
  • 소프트웨어 설치: 패키지 매니저를 사용하여 필요한 소프트웨어를 설치
  • 스크립트 실행: 특정 작업을 수행하기 위해 쉘 스크립트 또는 Ansible 등을 실행

주요 프로비저너 유형

  • local-exec: 로컬 머신에서 명령을 실행
  • remote-exec: 원격 머신에서 명령을 실행
  • file: 로컬 머신에서 원격 머신으로 파일 또는 디렉터리를 복사

주의사항

프로비저너의 사용은 테라폼 코드의 선언적 특성에 어긋날 수 있습니다. 따라서 가능한 프로비저너를 적게 사용하고, 다른 방법으로 리소스를 설정하는 것이 권장됩니다.

사용 예시

AWS EC2 인스턴스에 파일을 업로드하고 명령을 실행하는 예입니다.

resource "aws_instance" "example" { ami = "ami-0c9c942bd7bf113a2" instance_type = "t2.micro" provisioner "file" { source = "local/file/path" destination = "/remote/file/path" } provisioner "remote-exec" { inline = [ "sudo apt-get update", "sudo apt-get install -y nginx" ] } }

유의점

  • 순서: 프로비저너는 리소스의 생명주기 중 특정 시점에 실행됩니다. 예를 들어, 리소스 생성 후에 실행되거나 리소스 삭제 전에 실행될 수 있습니다.
  • 가변성: 프로비저너는 상태를 명시적으로 관리하지 않습니다. 따라서, 프로비저닝 로직이 변경될 경우 이전 리소스에 적용되지 않을 수 있습니다.
  • 에러 핸들링: 프로비저너가 실패하면, 테라폼은 해당 리소스를 “타인트” 상태로 마킹합니다. 이 경우 사용자는 수동으로 상태를 복구해야 할 수 있습니다.

프로비저너는 유용하지만, 가능하면 리소스의 argument 또는 **setting**을 통해 필요한 설정을 하는 것이 더 좋습니다. 프로비저너는 꼭 필요한 경우에만 사용하는 것이 관리 측면에서 좋습니다.


테라폼(Terraform)에서 사용되는 주요 프로비저너(provisioners)와 각각의 특성은 다음과 같습니다.

1. local-exec 프로비저

  • 특성: 로컬 시스템에서 명령어를 실행합니다.
  • 사용 사례: 리소스 생성 후 로컬에서 특정 스크립트를 실행해야 할 때 유용합니다.

2. remote-exec 프로비저너

  • 특성: 원격 서버에서 명령어를 실행합니다.
  • 사용 사례: 서버 생성 후 원격으로 초기 설정이나 패키지 설치가 필요한 경우에 사용됩니다.
resource "aws_instance" "web" { # ... # Establishes connection to be used by all # generic remote provisioners (i.e. file/remote-exec) connection { type = "ssh" user = "root" password = var.root_password host = self.public_ip } provisioner "file" { source = "script.sh" destination = "/tmp/script.sh" } provisioner "remote-exec" { inline = [ "chmod +x /tmp/script.sh", "/tmp/script.sh args", ] } }

3. file 프로비저너

  • 특성: 로컬 파일을 원격 서버로 복사합니다.
  • 사용 사례: 설정 파일이나 스크립트를 원격 서버에 전달해야 할 때 사용됩니다.
resource "null_resource" "foo" { # myapp.conf 파일이 /etc/myapp.conf 로 업로드 provisioner "file" { source = "conf/myapp.conf" destination = "/etc/myapp.conf" } # content의 내용이 /tmp/file.log 파일로 생성 provisioner "file" { content = "ami used: ${self.ami}" destination = "/tmp/file.log" }

null resource와 terraform data

null_resource

특성

  • 아무런 작업도 수행하지 않는 리소스.
  • 별도의 프로바이더 구성이 필요합니다.
  • 주로 프로비저닝 로직이나 의존성 관리를 수행하기 위해 사용됩니다.

사용 시나리오

  1. 프로비저닝 수행: **local-exec**나 **remote-exec**과 같은 프로비저너를 활용해 명령어를 실행합니다.
  2. 의존성 관리: 다른 리소스의 생성을 조건부로 관리합니다.
  3. 데이터 처리: 출력을 위한 데이터를 가공하거나, 로컬 변수와 함께 사용합니다.

terraform_data

특성

  • 자체적으로 아무런 작업도 수행하지 않습니다.
  • 별도의 프로바이더 구성이 필요 없이 테라폼 자체의 기본 수명주기 관리자가 제공됩니다.
  • **trigger_replace**와 input 인수, 그리고 **output 속성**이 제공됩니다.

사용 시나리오

  • 기본적으로 **null_resource**와 동일한 사용 시나리오가 적용됩니다.
  • 강제 재실행을 위한 trigger_replace 사용이 가능합니다.
  • 상태 저장을 위한 **input 인수**와 그에 따른 **output 속성**을 활용할 수 있습니다.

추가 기능

  1. triggers_replace: tuple 형태로 기존 map 형태보다 간단하게 값들을 정의할 수 있습니다.
  2. input: 상태 저장을 위해 사용됩니다.
  3. output: **input**에 저장된 값을 출력합니다.

예제

resource "terraform_data" "foo" { triggers_replace = [ aws_instance.foo.id, aws_instance.bar.id ] input = "world" } output "terraform_data_output" { value = terraform_data.foo.output # 출력 결과는 "world" }

moved 블록

기본 개념

  • 테라폼에서 리소스의 주소나 이름이 변경되면 기본적으로 그 리소스는 삭제되고 새로운 리소스가 생성됩니다.
  • 테라폼 1.1 버전부터는 리소스 이름이나 주소를 변경하더라도, 기존 리소스 상태를 유지하면서 이를 반영할 수 있는 moved 블록이 도입되었습니다.

주요 사용 케이스

  1. 리소스 이름 변경: 코딩 규칙이나 명명 규칙의 변화로 리소스 이름을 변경해야 할 때.
  2. 반복문 변경: **count**을 사용하던 것을 **for_each**로 변경할 때.
  3. 모듈 이동: 리소스가 다른 모듈로 이동하여 참조 주소가 변경될 때.

기존 방식 vs Moved 블록

  • 기존 방식: 이전에는 terraform state mv 명령어를 사용해서 수동으로 테라폼 상태 파일을 수정해야 했습니다. 이는 State 파일에 직접 접근해야 하므로 권한이나 복잡성 문제가 발생할 수 있습니다.
  • Moved 블록: State 파일에 직접 접근할 필요가 없습니다. 이전 주소와 새 주소를 moved 블록 내에서 선언함으로써 리소스의 이동을 안전하고 간편하게 관리할 수 있습니다.

핵심 기능

  • 리소스가 “옮겨졌다(moved)“는 사실과 그에 따른 이전 주소와 새로운 주소를 테라폼 State에 알려주는 역할을 합니다.

이러한 특징을 고려하면, moved 블록은 리소스 구성이 변경되는 다양한 상황에서 유용하게 사용될 수 있습니다. 이를 통해 테라폼 코드의 유연성과 안정성이 향상될 수 있습니다.

다음 예시 코드를 실행해봅다.

cat <<'EOT' > main.tf resource "local_file" "a" { content = "foo!" filename = "${path.module}/foo.bar" } output "file_content" { value = local_file.a.content } EOT

Untitled

이제 다음 main.tf 파일을 변경합니다다.

아래의 local_file의 이름을 a→ b 로 변경합니다

cat <<'EOT' > main.tf resource "local_file" "b" { content = "foo!" filename = "${path.module}/foo.bar" } moved { from = local_file.a to = local_file.b } output "file_content" { value = local_file.b.content } EOT

Untitled

위의 결과는 moved 블록을 사용하지않고 plan 했을경우 나오는 경과문이며, moved 블록을 이용해 plan 할경우 이상이 없음을 확인할 수 있습니다.

CLI를 위한 시스템 환경변수

테라폼은 환경변수를 이용해 실행방식, 출력내용에 대한 옵션을 조정이 가능합니다

일반적인 사용

  • 테라폼 CLI 환경 변수 설정을 통해 로컬 또는 다른 서버 환경에서 특정 옵션을 지정할 수 있습니다.
  • 이 설정은 시스템 환경 변수로, 일반적으로 영구적으로 로컬 환경에 적용됩니다.

설정 방법

Mac/리눅스/유닉스: export <환경 변수 이름>=<> Windows CMD: set <환경 변수 이름>=<> Windows PowerShell: $Env:<환경 변수 이름>='<>'

주요 환경 변수

  1. TF_LOG: 테라폼의 로깅 레벨을 설정합니다. (trace, debug, info, warn, error, off)
  2. TF_LOG_PATH: 로그를 저장할 파일의 위치를 지정합니다.
  3. TF_LOG_CORE: 테라폼 코어 자체의 로깅 레벨을 설정합니다.
  4. TF_LOG_PROVIDER: 테라폼의 프로바이더에 대한 로깅 레벨을 설정합니다.

사용 예시

  • **TF_LOG**를 **info**로 설정하고 **terraform plan**을 실행하면, 관련 로그가 표시됩니다.

    bashCopy code TF_LOG=info terraform plan

이를 통해 테라폼 작업을 보다 세밀하게 제어하고 디버깅할 수 있습니다.

프로바이더

테라폼의 프로바이더(Provider)는 특정 클라우드 또는 서비스의 리소스를 관리하기 위한 플러그인입니다. 각 프로바이더는 해당 서비스에 특화된 리소스 생성, 수정, 관리 및 삭제 동작을 정의합니다. 예를 들어, AWS 프로바이더를 사용하면 AWS의 EC2 인스턴스, S3 버킷, RDS 데이터베이스 등을 테라폼 코드로 관리할 수 있습니다.

프로바이더는 테라폼 코드에서 provider 블록을 통해 설정됩니다. 이 블록에서는 프로바이더의 버전, 인증 정보, 지역 등을 지정할 수 있습니다.

구성

Untitled

출처 : Cloudnet 스터디

Untitled

대표적인 프로바이더 종류입니다.

terraform { required_providers { architech-http = { source = "architect-team/http" version = "~> 3.0" } http = { source = "hashicorp/http" } aws-http = { source = "terraform-aws-modules/http" } } } data "http" "example" { provider = aws-http url = "https://checkpoint-api.hashicorp.com/v1/check/terraform" request_headers = { Accept = "application/json" } }

다음과 같이 다수의 프로바이더를 선택이 가능합니다.

단일 프로바이더를 다중정의 할 경우

# region 1 provider "aws" { region = "ap-southeast-1" } # region 2 provider "aws" { alias = "seoul" region = "ap-northeast-2" } resource "aws_instance" "app_server1" { ami = "ami-06b79cf2aee0d5c92" instance_type = "t2.micro" } resource "aws_instance" "app_server2" { provider = aws.seoul ami = "ami-0ea4d4b8dc1e46212" instance_type = "t2.micro" }

다음과 같은 형식으로 region에 따라 aws provider를 aws_instance에 지정하는 방식입니다.

확인 / 삭제

terraform init && terraform plan terraform apply -auto-approve terraform state list terraform destroy -auto-approve

프로바이더 경험해보기

Kubernetes

Kubernetes 실습

Kubernetes 프로바이더를 사용한 Nginx Deployment + Service 배포 - [참고: Docs]

  • 실습을 위해서 4.4 디렉터리를 신규 생성 후 열기
cd ../ mkdir 4.4 && cd 4.4
  • main.tf 파일 생성 : kubernetes 프로바이더 정의 및 KUBECONFIG 파일경로 정의
terraform { required_providers { kubernetes = { source = "hashicorp/kubernetes" } } } provider "kubernetes" { config_path = "~/.kube/config" }
  • kubernetes.tf 파일 생성 : Deployment, Service 리소스 정의 - [참고: K8s YAML to HCL]
resource "kubernetes_deployment" "nginx" { metadata { name = "nginx-example" labels = { App = "images/T101-nginx" } } spec { replicas = 2 selector { match_labels = { App = "images/T101-nginx" } } template { metadata { labels = { App = "images/T101-nginx" } } spec { container { image = "nginx:1.7.8" name = "example" port { container_port = 80 } } } } } } resource "kubernetes_service" "nginx" { metadata { name = "nginx-example" } spec { selector = { App = kubernetes_deployment.nginx.spec.0.template.0.metadata[0].labels.App } port { node_port = 30080 port = 80 target_port = 80 } type = "NodePort" } }
  • 실행
# [터미널1] terraform init & plan & apply **terraform init && terraform plan && terraform apply -auto-approve terraform state list** # [터미널1] terraform destroy **terraform destroy -auto-approve**

watch 명령어로 생성을 같이 살펴봅니다

watch -n1 -d kubectl get pods,svc

기존에 올라가있는 리소스들을 제외하고, 4m 13s 로 생성된 nginx-example 리소스를 조회 할 수 있습니다.

Untitled

helm 실습

  • 실습을 위해서 4.5 디렉터리를 신규 생성 후 열기
# 4.4 디렉토리에서 작업을 진행했다면 상위 디렉토리로 이동 cd ../ mkdir 4.5 && cd 4.5
  • main.tf
terraform { required_providers { helm = { source = "hashicorp/helm" version = "2.11.0" } } } provider "helm" { kubernetes { config_path = "~/.kube/config" } }
  • helm.tf
resource "helm_release" "nginx" { name = "nginx" repository = "https://charts.bitnami.com/bitnami" chart = "nginx" values = [ file("${path.module}/nginx-values.yaml") ] }
  • nginx-values.yaml
replicaCount: 1 service: type: NodePort
  • 실행
# [터미널1] terraform init & plan & apply **terraform init && terraform plan && terraform apply -auto-approve** # [터미널2]helm 배포확인 helm list | grep nginx # [터미널1] terraform destroy **terraform destroy -auto-approve**

Untitled

정상적으로 helm 프로비저닝이 된것을 확인 할 수 있다.