본문 바로가기
Terraform/Terraform 101 Study

4주차 1편 테라폼 프로바이더

by 개발자 영만 2024. 7. 7.

프로바이더

  • 테라폼은 인프라 자동화 도구로, 다양한 클라우드, SaaS, 기타 서비스의 인프라와 리소스를 관리할 수 있습니다.
  • 이를 가능하게 하는 것이 프로바이더입니다. 각 프로바이더는 특정 클라우드 플랫폼이나 서비스의 API와 상호작용하여 리소스를 관리할 수 있도록 합니다.
  • 프로바이더는 테라폼 실행 시, 특정 클라우드나 서비스의 API를 호출하여 리소스를 생성하거나 관리합니다.
  • 각 프로바이더는 테라폼의 고유 문법을 따르면서, 다양한 환경에서 동일한 동작을 수행할 수 있도록 구현됩니다.
  • 각 프로바이더는 테라폼 실행 파일과는 별도로 관리되며, 테라폼 레지스트리에서 관련 문서와 설정 방법을 확인할 수 있습니다.
  • 프로바이더를 사용하기 위해서는 해당 클라우드나 서비스와의 연결 및 인증에 필요한 정보(예: AWS 자격증명)를 제공해야 합니다.
  • 해당 링크 : https://registry.terraform.io/browse/providers
 

Terraform Registry

 

registry.terraform.io

 

프로바이더 구성

  • 테라폼 레지스트리(Terraform Registry)에는 다양한 프로바이더가 제공되며, 이들 프로바이더는 유지 보수 및 게시 권한에 따라 다양한 Tier 정보가 제공됩니다.

https://developer.hashicorp.com/terraform/language/providers#how-to-find-providers

 

프로바이더 요구사항 정의

  • 프로바이더를 명시적으로 정의하고 사용할 때 required_providers 블록을 사용하여 정의합니다.
  • 이 블록을 사용하면 특정 제공자의 다운로드 경로와 버전 제약을 명시할 수 있습니다.
terraform {
  required_providers {
    <프로바이더 로컬 이름> = {
      source = [<호스트 주소>/]<네임스페이스>/<유형>
      version = <버전 제약>
    }
    ...
  }
}
  • 프로바이더 요구사항 정의 블록
    • 호스트 주소 : 프로바이더를 배포하는 기본 주소입니다. 기본적으로 registry.terraform.io로 설정되어 있으며, 이는 공식 Terraform 레지스트리입니다. 다른 레지스트리를 사용하는 경우, 해당 레지스트리의 주소를 사용할 수 있습니다.
    • 네임스페이스 : 네임스페이스는 특정 레지스트리에서 프로바이더를 공개하는 조직을 식별하는 데 사용됩니다. 예를 들어, HashiCorp에서는 hashicorp가 네임스페이스이며, 여기에 각종 프로바이더들이 포함됩니다.
    • 유형 : 프로바이더의 유형은 해당 프로바이더가 관리하는 플랫폼이나 서비스의 이름을 지칭합니다. 일반적으로 이는 접두사와 일치하지만, 특정 예외 사항이 있을 수 있습니다. 예를 들어, AWS 프로바이더의 유형은 aws입니다.
  • 프로바이더의 버전은 프로바이더의 기능이나 조건이 변경될 수 있음을 나타냅니다. 테라폼에서는 버전 호환성을 유지하기 위해 version 설정을 통해 특정 버전을 명시할 수 있습니다.
  • version 속성을 생략하는 경우에는 테라폼이 초기화( terraform init )될 때 사용 가능한 가장 최신 버전의 프로바이더를 선택합니다.

 

프로바이더 설치

  • 테라폼에서는 terraform init 명령을 사용하여 특정 버전의 프로바이더를 설치하고 이를 .terraform 폴더에 저장하게 됩니다.
  • 아래 두 가지 주요 방법을 사용하여 프로바이더 버전을 관리할 수 있습니다.
    • terraform 블록을 사용하여 사용할 프로바이더를 명시적으로 정의
    • .terraform.lock.hcl 파일을 코드 저장소에 공유
  • 만약 required_providers 블록에 프로바이더를 명시하지 않고, 테라폼 구성 파일에 어떤 프로바이더가 사용되면, 테라폼은 이 프로바이더의 최신 버전을 자동으로 추론하여 다운로드합니다.

 

로컬 이름과 프로바이더 지정

  • 프로바이더의 경우 required_providers 블록을 사용하여 정의하고 여기서 사용되는 로컬 이름은 모듈 내에서 고유해야 합니다.
  • 로컬 이름과 리소스 접두사는 독립적으로 선언됩니다. 프로바이더의 소스 경로가 지정되면 프로바이더의 고유 접두사도 제공됩니다.
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 3.0"
    }
  }
}

resource "aws_s3_bucket" "my_bucket" {
  bucket = "my-unique-bucket-name"
  acl    = "private"
}
  • 동일한 접두사를 사용하는 프로바이더를 선언할 때, 로컬 이름을 다르게 지정하여 테라폼 구성 파일에서 명시적으로 프로바이더를 구분하고 사용할 수 있습니다.
  • 이를 통해 테라폼 리소스와 데이터 소스가 어떤 프로바이더 인스턴스를 사용할지 provider 인수를 사용하여 명확히 지정할 수 있습니다.
  • 동일한 source에 대해 다수의 정의는 불가능합니다.
  • 동일한 http 접두사를 사용하는 다수의 프로바이더 정의 및 사용
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"
  }
}

 

단일 프로바이더의 다중 정의

  • 동일한 프로바이더를 사용하지만 각 리소스가 다른 설정을 갖는 경우 여러 개의 프로바이더를 선언하여 각각의 설정을 다르게 할 수 있습니다.
  • AWS의 경우 IAM 권한이나 리전 등에 따라 리소스를 관리해야 할 경우가 많습니다.
  • provider 블록에서 alias 를 명시하여 프로바이더를 구분합니다.
provider "aws" {
  alias = "seoul"
  region = "ap-northeast-2"
}
  • 리소스와 데이터 소스에서 provider 메타 인수를 사용하여 특정 프로바이더를 지정할 수 있습니다.
resource "aws_instance" "app_server2" {
  provider      = aws.seoul
  ami           = "ami-0ea4d4b8dc1e46212"
  instance_type = "t2.micro"
}
  • provider 메타 인수가 지정되지 않은 경우 alias가 없는 프로바이더가 기본 프로바이더로 동작합니다.
provider "aws" {
  region = "ap-southeast-1"
}

resource "aws_instance" "app_server1" {
  ami           = "ami-06b79cf2aee0d5c92"
  instance_type = "t2.micro"
}
  • 리전을 다르게 구성한 프로바이더를 aws_instance에 지정하는 실습
provider "aws" {
  region = "ap-southeast-1"
}

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"
}
  • 실행 및 확인
# 리소스 배포
terraform init && terraform plan
terraform apply -auto-approve

# 리소스의 상태 목록 확인
terraform state list

# 콘솔을 통해 인스턴스의 퍼블릭 IP 주소 조회
echo "aws_instance.app_server1.public_ip" | terraform console
echo "aws_instance.app_server2.public_ip" | terraform console

# AWS CLI를 통해 EC2 인스턴스 상태 확인
aws ec2 describe-instances --filters Name=instance-state-name,Values=running --output table
aws ec2 describe-instances --filters Name=instance-state-name,Values=running --output table --region ap-southeast-1

# 다음 실습을 위해 리소스 삭제
terraform destroy -auto-approve

 

프로바이더 간 전환 여부

  • 테라폼은 여러 클라우드 서비스를 지원하기 위해 각각의 클라우드에 대한 프로바이더를 개발하고 유지합니다. 각 프로바이더는 해당 클라우드 서비스의 API와 통신하여 인프라 자원을 관리합니다.
  • 테라폼 코드는 특정 클라우드 프로바이더와 관련된 리소스 정의를 포함하고 있습니다.
  • 예를 들어, AWS에서 EC2 인스턴스를 생성하거나 GCP에서 VM 인스턴스를 생성하는 등의 작업은 각기 다른 프로바이더를 사용하여 정의됩니다.
  • 테라폼 코드를 다른 클라우드 환경으로 전환하려면, 해당 클라우드의 프로바이더를 사용하여 리소스 정의를 수정해야 합니다.
  • 예를 들어, AWS에서 GCP로 전환할 때는 AWS 프로바이더를 GCP 프로바이더로 변경해야 하며, 각 클라우드의 API에 맞게 리소스를 재정의해야 합니다.

 

프로바이더 에코시스템

 

프로바이더 경험해보기

[실습] 2개의 리전에 Ubuntu EC2 배포

  • provider_data.tf 파일 생성 - 리전별로 AMI ID 값이 다르므로, 데이터 소스를 사용하여 AMI ID 값을 동적으로 가져옵니다.
provider "aws" {
  region = "ap-northeast-2"
  alias  = "region_1"
}

provider "aws" {
  region = "ap-southeast-1"
  alias  = "region_2"
}

data "aws_region" "region_1" {
  provider = aws.region_1
}

data "aws_region" "region_2" {
  provider = aws.region_2
}

data "aws_ami" "ubuntu_region_1" {
  provider = aws.region_1

  most_recent = true
  owners      = ["099720109477"] # Canonical

  filter {
    name   = "name"
    values = ["ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"]
  }
}

data "aws_ami" "ubuntu_region_2" {
  provider = aws.region_2

  most_recent = true
  owners      = ["099720109477"] # Canonical

  filter {
    name   = "name"
    values = ["ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"]
  }
}
  • ec2.tf 파일 생성 - 리전과 ami를 참조합니다.
resource "aws_instance" "region_1" {
  provider = aws.region_1

  ami           = data.aws_ami.ubuntu_region_1.id
  instance_type = "t2.micro"
}

resource "aws_instance" "region_2" {
  provider = aws.region_2

  ami           = data.aws_ami.ubuntu_region_2.id
  instance_type = "t2.micro"
}
  • 실행 및 확인
# [터미널1] ap-northeast-2
while true; do aws ec2 describe-instances --region ap-northeast-2 --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

# [터미널2] ap-southeast-1
while true; do aws ec2 describe-instances --region ap-southeast-1 --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

# 리소스 배포
terraform init && terraform plan && terraform apply -auto-approve

# 리소스의 상태 목록 확인
terraform state list

# AWS CLI를 통해 EC2 인스턴스 상태 확인
aws ec2 describe-instances --region ap-northeast-2 --query "Reservations[*].Instances[*].{PublicIPAdd:PublicIpAddress,InstanceName:Tags[?Key=='Name']|[0].Value,Status:State.Name}" --filters Name=instance-state-name,Values=running --output text
aws ec2 describe-instances --region ap-southeast-1 --query "Reservations[*].Instances[*].{PublicIPAdd:PublicIpAddress,InstanceName:Tags[?Key=='Name']|[0].Value,Status:State.Name}" --filters Name=instance-state-name,Values=running --output text

# 다음 실습을 위해 리소스 삭제
terraform destroy -auto-approve

 

2가지 주의 사항

  • Warning 1 : Multiregion is hard  프로덕션 수준의 멀티 리전 서비스를 구현하는 것은 쉽지 않다.
    • Active-Active 멀티 리전 서비스를 구현하려면 지연 시간, 고유 ID 관리, 최종 일관성 유지 등 여러 기술적 도전 과제들이 존재합니다.
    • 서로 다른 지역 간의 데이터 동기화와 일관성 유지를 위한 복잡한 메커니즘이 필요합니다.
  • Warning 2: Use aliases sparingly alias을 빈번하게 사용하는 것은 지양해야 합니다.
    • 프로덕션 환경에서는 별칭을 사용하여 두 리전에 동시에 배포하는 경우, 한 리전에서 문제가 발생할 경우 plan 및 apply 단계에서 실패할 수 있습니다.
    • 별칭을 사용하는 것보다는 각 환경을 완전히 격리하여 문제 발생 시 영향도를 최소화해야 합니다.