가시다 테라폼 스터디 3주차 정리

강재민·2023년 9월 17일
0

테라폼으로 시작하는 IaC 책을 기준으로 정리하였습니다.


조건문

테라폼에서의 조건식은 3항 연산자 형태를 갖는다. 조건은 true 또는 false로 확인되는 모든 표현식을 사용할 수 있다

예시

# <조건 정의> ? <옳은 경우> : <틀린 경우>
# 조건식 형태 권장 사항 
var.example ? 12 : "hello"            # 비권장
var.example ? "12" : "hello"          # 권장
var.example ? tostring(12) : "hello"  # 권장

[도전과제1] 조건문을 활용하여 (각자 편리한) AWS 리소스를 배포하는 코드를 작성해보자!

main.tf

variable "enable_file" {
  default = true
}

resource "local_file" "foo" {
  count    = var.enable_file ? 1 : 0
  content  = "foo!"
  filename = "${path.module}/foo.bar"
}

# Create a VPC for the region associated with the AZ
resource "aws_vpc" "myvpc" {
  count    = var.enable_file ? 1 : 0
  cidr_block           = "10.10.0.0/16"
  enable_dns_support   = true
  enable_dns_hostnames = true

  tags = {
    Name = "repush"
  }
}

함수

테라폼은 프로그래밍 언어적인 특성을 가지고 있어서, 값의 유형을 변경하거나 조합할 수 있는 내장 함수를 사용 할 수 있다

  • 단, 내장된 함수 외에 사용자가 구현하는 별도의 사용자 정의 함수를 지원하지는 않는다.
  • 함수 종류에는 숫자, 문자열, 컬렉션, 인코딩, 파일 시스템, 날짜/시간, 해시/암호화, IP 네트워크, 유형 변환이 있다.
  • 테라폼 코드에 함수를 적용하면 변수, 리소스 속성, 데이터 소스 속성, 출력 값 표현 시 작업을 동적이고 효과적으로 수행할 수 있다.

[도전과제2] 내장 함수을 활용하여 (각자 편리한) 리소스를 배포하는 코드를 작성해보자!

count + 함수 cidrhost 로 EC2의 ENI 에 10개의 ip를 장착

main.tf

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

resource "aws_vpc" "my_vpc" {
  cidr_block = "172.16.0.0/16"
}

data "aws_availability_zones" "available" {
  state = "available"
}

locals {
  host_ip_count = 10
}

resource "aws_subnet" "my_subnet" {
  vpc_id            = aws_vpc.my_vpc.id
  cidr_block        = "172.16.10.0/24"
  availability_zone = data.aws_availability_zones.available.names[0]
}

resource "aws_network_interface" "foo" {
  count       = local.host_ip_count
  subnet_id   = aws_subnet.my_subnet.id
  private_ips = [cidrhost(aws_subnet.my_subnet.cidr_block, count.index + 100)]
}

resource "aws_instance" "foo" {
  count         = local.host_ip_count
  ami           = "ami-0162b88b98a9265c6"
  instance_type = "t2.micro"

  network_interface {
    network_interface_id = aws_network_interface.foo[count.index].id
    device_index         = 0
  }
}

output "network_private_ips" {
  value = aws_network_interface.foo.*.private_ips
}

output

Outputs:

network_private_ips = [
  toset([
    "172.16.10.100",
  ]),
  toset([
    "172.16.10.101",
  ]),
  toset([
    "172.16.10.102",
  ]),
  toset([
    "172.16.10.103",
  ]),
  toset([
    "172.16.10.104",
  ]),
  toset([
    "172.16.10.105",
  ]),
  toset([
    "172.16.10.106",
  ]),
  toset([
    "172.16.10.107",
  ]),
  toset([
    "172.16.10.108",
  ]),
  toset([
    "172.16.10.109",
  ]),
]

프로비저너

프로비저너는 프로바이더와 비슷하게 제공자로 해석되나, 프로바이더로 실행되지 않는 커맨드파일 복사 같은 역할을 수행

  • 프로비저너로 실행된 결과는 테라폼의 상태 파일과 동기화되지 않으므로 프로비저닝에 대한 결과가 항상 같다고 보장할 수 없다 ⇒ 선언적 보장 안됨
  • 프로비저너의 종류에는 파일 복사와 명령어 실행을 위한 file, local-exec, remote-exec가 있다.
  • 여러 종류가 있지만 디펙토로 사용되는 것은 terraform-provider-ansible이다.

[도전과제3] AWS EC2 배포 시 remote-exec/file 프로비저너 혹은 terraform-provider-ansible를 활용하는 코드를 작성해보자!

main.tf

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

resource "aws_vpc" "my_vpc" {
  cidr_block = "172.16.0.0/16"
}

data "aws_availability_zones" "available" {
  state = "available"
}

locals {
  host_ip_count = 10
}

resource "aws_subnet" "my_subnet" {
  vpc_id            = aws_vpc.my_vpc.id
  cidr_block        = "172.16.10.0/24"
  availability_zone = data.aws_availability_zones.available.names[0]
}

resource "aws_network_interface" "foo" {
  count       = local.host_ip_count
  subnet_id   = aws_subnet.my_subnet.id
  private_ips = [cidrhost(aws_subnet.my_subnet.cidr_block, count.index + 100)]
}

resource "aws_instance" "foo" {
  count         = local.host_ip_count
  ami           = "ami-0162b88b98a9265c6"
  instance_type = "t2.micro"

  network_interface {
    network_interface_id = aws_network_interface.foo[count.index].id
    device_index         = 0
  }
}

output "network_private_ips" {
  value = aws_network_interface.foo.*.private_ips
}

null_resource와 terraform_data

null_resource

아무 작업도 수행하지 않는 리소스를 구현

  • 이런 리소스가 필요한 이유는 테라폼 프로비저닝 동작을 설계하면서 사용자가 의도적으로 프로비저닝하는 동작을 조율해야 하는 상황이 발생하여, 프로바이더가 제공하는 리소스 수명주기 관리만으로는 이를 해결하기 어렵기 때문이다.
  • 주로 사용되는 시나리오
    • 프로비저닝 수행 과정에서 명령어 실행
    • 프로비저너와 함께 사용
    • 모듈, 반복문, 데이터 소스, 로컬 변수와 함께 사용
    • 출력을 위한 데이터 가공
  • 예를 들어 다음의 상황을 가정
    • AWS EC2 인스턴스를 프로비저닝하면서 웹서비스를 실행시키고 싶다
    • 웹서비스 설정에는 노출되어야 하는 고정된 외부 IP가 포함된 구성이 필요하다. 따라서 aws_eip 리소스를 생성해야 한다.
  • AWS EC2 인스턴스를 프로비저닝하기 위해 aws_instance 리소스 구성 시 앞서 확인한 프로비저너를 활용하여 웹서비스를 실행하고자 한다

terraform_data

  • 이 리소스 또한 자체적으로 아무것도 수행하지 않지만 null_resource는 별도의 프로바이더 구성이 필요하다는 점과 비교하여 추가 프로바이더 없이 테라폼 자체에 포함된 기본 수명주기 관리자가 제공된다는 것이 장점이다.
  • 사용 시나리오는 기본 null_resource와 동일하며 강제 재실행을 위한 trigger_replace와 상태 저장을 위한 input 인수와 input에 저장된 값을 출력하는 output 속성이 제공된다.
  • triggers_replace에 정의되는 값이 기존 map 형태에서 tuple로 변경되어 쓰임이 더 간단해졌다

moved 블록

  • 테라폼의 State에 기록되는 리소스 주소의 이름이 변경되면 기존 리소스는 삭제되고 새로운 리소스가 생성됨을 앞서 설명에서 확인했다.
  • 하지만 테라폼 리소스를 선언하다 보면 이름을 변경해야 하는 상황이 발생하기도 하는데, 예를 들면 다음과 같다.
    • 리소스 이름을 변경
    • count로 처리하던 반복문을 for_each로 변경
    • 리소스가 모듈로 이동하여 참조되는 주소가 변경
  • 리소스의 이름은 변경되지만 이미 테라폼으로 프로비저닝된 환경을 그대로 유지하고자 하는 경우 테라폼 1.1 버전부터 moved 블록을 사용할 수 있다.
  • ‘moved’라는 단어가 의미하는 것처럼 테라폼 State에서 옮겨진 대상의 이전 주소와 새 주소를 알리는 역할을 수행한다.
  • moved 블록 이전에는 State를 직접 편집하는 terraform state mv 명령을 사용하여 State를 건드려야 하는 부담이 있었다면, moved 블록은 State에 접근 권한이 없는 사용자라도 변경되는 주소를 리소스 영향 없이 반영할 수 있다.

[도전과제5] moved 블록을 사용한 테라폼 코드 리팩터링을 수행해보세요

동작 순서
1. 인프라 프로비저닝
2. moved 블록을 활용해 원하는 변경사항 동작
3. moved 블록을 해제하고 바뀐 변경사항만 상태파일과 동기화

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
}

CLI를 위한 시스템 환경 변수

테라폼은 환경 변수를 통해 실행 방식출력 내용에 대한 옵션을 조절할 수 있다.

  • 시스템 환경 변수 설정으로, 영구적으로 로컬 환경에 적용되는 옵션이나 별도 서버 환경에서 실행하기 위한 옵션을 부여 가능.
  • 이를 통한 로컬 작업 환경과 다른 환경 구성에서만 사용될 특정 옵션을 적용.
Mac/리눅스/유닉스: export <환경 변수 이름>=<>
Windows CMD: set <환경 변수 이름>=<>
Windows PowerShell: $Env:<환경 변수 이름>='<>'

TF_LOG : 테라폼의 stderr 로그에 대한 레벨을 정의
TF_INPUT : 값을 false 또는 0으로 설정하면 테라폼 실행 시 인수에 -input=false 를 추가한 것과 동일한 수행 결과를 확인
TF_CLI_ARGS / TF_CLI_ARGS_subcommand : 테라폼 실행 시 추가할 인수를 정의
TF_DATA_DIR : State 저장 백엔드 설정과 같은 작업 디렉터리별 데이터를 보관하는 위치를 지정


프로바이더

  • 테라폼은 terraform 바이너리 파일을 시작으로 로컬 환경에나 배포 서버와 같은 원격 환경에서 원하는 대상을 호출하는 방식으로 실행된다.
    이때 ‘원하는 대상’은 호출하는 방식이 서로 다르지만 대상의 공급자, 즉 프로바이더가 제공하는 API를 호출해 상호작용을 한다. 여기서 테라폼이 대상과의 상호작용을 할 수 있도록 하는 것이 ‘프로바이더’다.
  • 각 프로바이더의 API구현은 서로 다르지만 테라폼의 고유 문법으로 동일한 동작을 수행하도록 구현되어 있다. 프로바이더는 플러그인 형태로 테라폼에 결합되어 대상이 되는 클라우드, SaaS, 기타 서비스 API를 사용해 동작을 수행한다. 각 프로바이더는 테라폼이 관리하는 리소스 유형과 데이터 소스를 사용할 수 있도록 연결한다.
  • 즉, 테라폼은 프로바이더 없이는 어떤 종류의 인프라와 서비스도 관리할 수 없다는 의미다. 대부분의 프로바이더는 대상 인프라 환경이나 서비스 환경에 대한 리소스를 관리하므로, 프로바이더를 구성할 때는 대상과의 연결과 인증에 필요한 정보가 제공되어야 한다.
  • 각 프로바이더는 테라폼 실행 파일과는 별도로 자체 관리되고 게시된다. 테라폼 레지스트리 사이트에서 주요 프로바이더와 관련 문서를 확인 가능

[도전과제8] kubernetes, helm 프로바이더를 사용해서 리소스를 배포하고 확인해보세요! (wordpress, nginx, tomcat 등) -

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: 3
service:
  type: LoadBalancer

0개의 댓글