Terraform Study - w03

wdb·2023년 7월 22일

01. 조건문

소스코드
테라폼에서는 타 프로그래밍 언어의 문법에서 지원하는 삼항연산자를 통해 조건문을 이용할 수 있습니다. 테라폼의 조건문을 사용하여 조건에 따라 두 개의 유저 데이터를 사용하여 EC2를 배포하는 실습을 진행하였습니다. 각 유저 데이터에는 apache 설치 스크립트와 nginx 설치 스크립트가 있습니다.
template 폴더에 유저 데이터로 이용될 두 개의 템플릿 파일을 작성해둡니다.

template
├── apache_install.tpl
└───nginx_install.tpl

로컬 변수로 웹 서버의 유저 데이터(web_serveR_user_data)를 선언합니다. is_apache라는 변수 값을 이용하여 어떤 템플릿 파일을 불러올 것인지 결정합니다. is_apache가 true(=1)이면 apache 설치 스크립트가 있는 템플릿 파일을, is_apache가 false(=0)이면 nginx 설치 스크립트가 있는 템플릿 파일을 불러옵니다.

# local.tf

locals {
  ...
  web_server_user_data = var.is_apache ? templatefile("./template/apache_install.tpl", {
    msg = "apache server"
    }) : templatefile("./template/nginx_install.tpl", {
    msg = "nginx server"
  })
}

ec2를 배포할 때 web_server_user_data 로컬 변수를 이용하면 is_apache 값에 따라 적합한 스크립트를 유저 데이터로 이용할 수 있습니다.

# ec2.tf

resource "aws_instance" "web_server" {
  ...
  user_data = local.web_server_user_data
  ...
}

02. 함수 - merge, format

소스코드

테라폼은 다양한 Build-In 함수를 지원하고 있습니다. 이번 실습에서는 merge와 format 함수를 이용하여 네이밍 룰을 세팅해보았습니다.
merge 함수는 map 또는 객체를 병합하는 역할을 하고, format 함수는 문자열을 지정한 형식에 맞추어 재생성하는 역할을 합니다.
variable.tf에 네이밍 룰로 사용할 prefix, company, environent을 선언합니다.

# variable.tf

# naming
variable "company" {}
variable "environment" {}
variable "project" {}

로컬 블록에서 VPC의 네이밍 룰을 조합할 것입니다. AWS 리소스의 이름은 리소스의 Name 키를 가진 태그로 지정됩니다. 따라서 우선 기본적으로 이용할 태그(default_tags)를 map 형식으로 지정해줍니다.
VPC의 네이밍 룰은 company-environment-project-vpc(ex. google-dev-gmail-vpc) 형식으로 설정할 것입니다. format 함수에 "%s-%s-%s-vpc"이라는 형식을 지정한 다음, 해당하는 값(company, environment, project)들을 가져옵니다."%s"는 문자열 파라미터를 의미합니다.

# local.tf

locals {
  default_tags = {
    env = var.environment
  }  
  
  vpc_tag = merge(local.default_tags, {
    Name = format(
      "%s-%s-%s-vpc",
      var.company,
      var.environment,
      var.project
    )
  })
}

VPC 리소스 블록에서 tags로 로컬의 vpc_tag를 대입해주면 네이밍 룰에 맞게 VPC의 Name 태그 값이 설정된 것을 확인할 수 있습니다.

# vpc.tf

resource "aws_vpc" "vpc" {
  ...
  tags = local.vpc_tag
}

03. remote-exec

소스코드
테라폼은 리소스를 배포하는 데 최적화된 툴이지만, 서비스를 운영하다보면 배포된 리소스 환경 내에서 작업이 필요할 때도 있습니다. Ansible과 같은 구성 자동화 도구를 이용하는 것이 가장 좋겠지만, 간단한 작업들은 테라폼의 Provisioner를 통해서도 가능합니다. 테라폼에 최적화된 기능은 아닌만큼 프로덕션 환경에서의 사용은 권장되지 않습니다.
remote-exec은 원격 환경에서의 커맨드 실행을 위한 테라폼 Provisioner입니다. remote-exec을 이용하여 배포한 EC2 인스턴스의 웹 서버 중지시켜 보았습니다.
remote-exec 인스턴스가 배포된 이후에 remote-exec이 작동해야 합니다. 필요한 리소스 배포와 remote-exec는 무관하게 작동해야 하므로 아무 리소스를 생성하지 않는 null_resource 내에서 remote-exec을 이용합니다.

resource "aws_instance" "web_server" {
  ami                    = data.aws_ami.latest_ubuntu_22_04.id
  instance_type          = var.instance_type
  vpc_security_group_ids = [aws_security_group.web_sg.id]
  key_name               = var.key_name

  user_data = templatefile("./template/apache_install.tpl", {
    msg = "apache server"
  })

  tags = {
    Name = "${var.prefix}-server"
  }
}

resource "aws_eip" "web_server_eip" {
  instance = aws_instance.web_server.id
}

resource "null_resource" "code_deploy_agent_install" {
  provisioner "remote-exec" {
    connection {
      host = aws_eip.web_server_eip.public_ip
      type = "ssh"
      user = "ubuntu"
      private_key = file("./keypair/test-key.pem")
    }
    script = "./tmp/stop_server.sh"
  } 
}

connection을 통해 원격 환경에 접속합니다. 접속에는 일반 ssh 접속과 동일한 정보(IP, 프로토콜, 유저 이름, 키 파일)이 필요합니다. 실행할 스크립트는 로컬 환경에서 불러옵니다.
테라폼 apply를 통해 리소스 배포 후 remote-exec이 있는 null_resource 블록을 배포하였습니다(같은 타이밍에 배포하면 아파치 웹 서버가 세팅되기 전이므로 에러가 발생합니다). 그 결과 apache 서버가 inactive로 전환된 것을 확인할 수 있었습니다.

04. trigger_replace

소스코드
테라폼은 로컬 코드와 상태 파일을 체크한 다음 상태의 변경이 없을 경우 아무 동작도 하지 않습니다. trigger_replace를 이용하면 현재의 state와 비교하여 변화가 없는 블록이라도 특정 조건에 따라 코드를 실행시킬 수 있습니다. terraform_data 블록은 null_resource와 같이 아무 리소스도 생성하지 않는 블록이지만, trigger_replace를 통해 필요한 명령을 실행시키는 것이 가능합니다. 이 기능은 필요한 리소스가 생성된 이후에 리소스에 특정 요청을 해야하는 경우 유용합니다.
아래 예시와 같이 trigger_replace에 EC2 인스턴스의 id를 넣어주면, 이 id가 변경될 때마다 해당 블록이 실행됩니다.

# ec2.tf
...
resource "terraform_data" "on_id_change" {
  triggers_replace = [
    aws_instance.web_server.id
  ] 
  
  input = aws_instance.web_server.id
}

output "terraform_data_output" {
  value = terraform_data.on_id_change.output 
}

05. moved

소스코드
테라폼에서 State에 기록되는 리소스 주소의 이름이 변경될 경우, 기존 리소스는 삭제되고 새로운 리소스가 생성됩니다. 하지만 리소스가 삭제되어서는 안되는 경우가 발생할 수 있습니다. 리소스의 이름을 변경하고 싶지만 리소스를 유지하고 싶을 때는 moved 블록을 사용할 수 있습니다.

# main.tf

resource "local_file" "a" {
    content = "moved test"
    filename = "${path.module}/test.txt"
}

moved {
     from = local_file.a
     to = local_file.b
 }

output "file_content" {
    value = local_file.b.content
}

a 리소스를 생성한 이후에 b라는 이름으로 변경하고 싶으면 moved 블록에 a를 b로 변경하겠다고 명시하면 됩니다.

06. 두 개의 리전에 S3 버킷 배포

소스코드
하나의 리소스를 두 개의 리전에 배포해야하는 상황에서는 어떻게 해야 할까요? 이 경우 두 개의 프로바이더를 이용할 수 있습니다.

# provider.tf

terraform {
  required_version = "~> 1.5.2"
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = ">= 5.0, <=5.6.2"
    }
  }
...
provider "aws" {
  alias   = "region_1"
  region  = var.region_1
  profile = var.account_profile
}

provider "aws" {
  alias   = "region_2"
  region  = var.region_2
  profile = var.account_profile
}

provider에 alias를 지정하면 같은 aws 프로바이더 두 개를 구분하여 사용할 수 있습니다. provider를 선언할 때 사용할 리전을 구분해줍니다.
코드의 재활용을 통해 모듈을 이용했습니다. S3 버킷을 배포하는 코드를 moduels/s3 폴더에 넣고, main.tf에서 이 경로의 모듈을 불러옵니다.

module "kr_bucket" {
  source = "./modules/s3"

  providers = {
    aws = aws.region_1
  }

  bucket_prefix            = var.bucket_prefix
  bucket_postfix           = "${var.bucket_postfix}-kr"
  versioning_configuration = var.versioning_configuration

  tags = var.tags
}

module "jp_bucket" {
  source = "./modules/s3"

  providers = {
    aws = aws.region_2
  }

  bucket_prefix            = var.bucket_prefix
  bucket_postfix           = "${var.bucket_postfix}-jp"
  versioning_configuration = var.versioning_configuration

  tags = var.tags
}

모듈을 불러올 때 프로바이더의 alias를 이용하여 각각 다른 프로바이더를 선언하면 이 모듈을 다른 두 개의 리전에 배포할 수 있습니다.

1개의 댓글

comment-user-thumbnail
2023년 7월 22일

많은 도움이 되었습니다, 감사합니다.

답글 달기