본문 바로가기
  • lakescript
스터디 이야기/Terraform

[T101] 3-7. Terraform - null_resource와 terraform_data

by lakescript 2024. 6. 28.

 

더보기

이 스터디는 CloudNet@에서 진행하는 T101 스터디를 참여하면서 공부하는 내용을 기록하는 블로그 포스팅입니다.

CloudNet@에서 제공해주는 자료들과 테라폼으로 시작하는 IaC 를 바탕으로 작성되었습니다.

 

 

null_resource와 terraform_data

테라폼 1.4버전이 릴리즈되면서 기존 null_resource 리소스를 대체하는 terraform_data 리소스가 추가되었습니다.

 

null_resource

null_resource는 그 이름 그대로 아무 작업도 수행하지 않는 리소스를 구현합니다. 이 리소스가 필요한 이유는 테라폼 프로비저닝 동작을 설계하면서 사용자가 의도적으로 프로비저닝하는 동작을 조율해야 하는 상황이 발생하는데, 프로바이더가 제공하는 리소스 수명주기 관리만으로는 이를 해결하기 어렵기 때문입니다. 

사용 시나리오

  • 프로비저닝 수행 과정에서 명령어 실행
  • 프로비저너와 함께 사용
  • 모듈, 반복문, 데이터 소스, 로컬 변수와 함께 사용
  • 출력을 위한 데이터 가공

예시 상황

  • AWS EC2 인스턴스를 프로비저닝하면서 웹 서비스를 실행시키고 싶다.
  • 웹 서비스 설정에는 노출되어야 하는 고정된 외부IP가 포함된 구성이 필요하다. 따라서 aws_eip 리소스를 생성해야 한다.

 

aws_instance에서 aws_eip의 public ip를 사용하려는 예시

AWS EC2 인스턴스를 프로비저닝하기 위해 aws_instance 리소스 구성 시 앞서 확인한 프로비저너를 활용하여 웹 서비스를 실행하는 코드를 작성해보겠습니다.

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

resource "aws_security_group" "instance" {
  name = "t101sg"

  ingress {
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  ingress {
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

}

resource "aws_instance" "example" {
  ami                    = "ami-0c9c942bd7bf113a2"
  instance_type          = "t2.micro"
  subnet_id              = "subnet-0bc6aaf486e24b341"  # 각자 default VPC에 subnet ID 아무거나
  private_ip             = "172.31.1.100"
  vpc_security_group_ids = [aws_security_group.instance.id]

  user_data = <<-EOF
              #!/bin/bash
              echo "Hello, T101 Study" > index.html
              nohup busybox httpd -f -p 80 &
              EOF

  tags = {
    Name = "Single-WebSrv"
  }

  provisioner "remote-exec" {
    inline = [
      "echo ${aws_eip.myeip.public_ip}"
     ]
  }
}

resource "aws_eip" "myeip" {
  #vpc = true
  instance = aws_instance.example.id
  associate_with_private_ip = "172.31.1.100"
}

output "public_ip" {
  value       = aws_instance.example.public_ip
  description = "The public IP of the Instance"
}

 

aws_eip가 생성하는 고정된 IP를 할당하기 위해서는 대상인 aws_instance의 id 값이 필요합니다. 또한, aws_instance의 프로비저너 동작에서는 aws_eip가 생성하는 속성 값인 public_ip가 필요합니다.

 

테라폼 구성 정의에서 두 리소스의 종속성이 상호 참조가 발생하는 상황으로, 실제 실행되는 코드를 작성하여 terraform plan을 실행하면 아래와 같은 에러가 발생합니다.

위 에러는 aws ec2 instance에 elastic ip를 사용하게 하려면 aws EC2 instance가 생성되고 나서 elastic ip를 설정하는 것이라 상호 참조되는 종속성을 끊기 위해서는 둘 중 하나의 실행 시점을 한 단계 뒤로 미뤄야 합니다. 이런 경우 실행에 간격을 추가하여 실제 리소스와는 무관한 동작을 수행하기 위해 null_resource를 활용합니다.

	...
resource "aws_instance" "example" {
  	...
    #provisioner "remote-exec" {
    #	inline = [
    #  		"echo ${aws_eip.myeip.public_ip}"
    # 	]
  	#}
}

resource "null_resource" "echomyeip" {
  provisioner "remote-exec" {
    connection {
      host = aws_eip.myeip.public_ip
      type = "ssh"
      user = "ubuntu"
      private_key =  file("/Users/gasida/.ssh/kp-lake.pem") # 각자 자신의 EC2 SSH Keypair 파일 위치 지정
      #password = "qwe123"
    }
    inline = [
      "echo ${aws_eip.myeip.public_ip}"
      ]
  }
}

resource "aws_eip" "myeip" {
     ...
 }
    ...

 

null_resource는 정의된 속성이 'id'가 전부이므로, 선언된 내부의 구성이 변경되더라도 새로운 Plan 과정에서 실행 계획에 포함되지 못합니다. 따라서 사용자가 null_resource에 정의된 내용을 강제로 다시 실행하기 위한 인수로 trigger가 제공됩니다. trigger는 임의의 string 형태의 map 데이터를 정의하는데, 정의된 값이 변경되면 null_resource 내부에 정의돈 행위를 다시 실행합니다.

 

null_resource는 리소스를 생성하진 않고 선언된 동작만 수행합니다 여기서는 remote_exec라는 프로비저너의 connection 동작만 실행시켜 연결 테스트를 진행합니다.

 

terraform_data

테라폼 1.4 버전에서는 기존 null_resource의 기능적인 요소를 대체하기 위해 terraform_data 리소스가 추가되었습니다. 이 리소스 또한 자체적으로 아무것도 수행하지 않지만 null_resource는 별도의 프로바이더 구성이 필요하다는 점과 비교하여 추가 프로바이더 없이 테라폼 자체에 포함된 기본 수명주기 관리자가 제공한다는 것이 장점입니다. 

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"
}

 

기존 null_resource와 동일하며 강제 재실행을 위한 triggers_replace와 상태 저장을 위한 input 인수와 input에 저장된 값을 출력하는 output 속성이 제공됩니다. triggers_replace에 정의되는 값이 기존 map 형태에서 tuple로 변경되어 더욱 편리해졌습니다.

 

'스터디 이야기 > Terraform' 카테고리의 다른 글

[T101] 4-1. Terraform - Provider  (0) 2024.07.02
[T101] 3-8. Terraform - moved 블록  (0) 2024.06.28
[T101] 3-6. Terraform - 프로비저너  (0) 2024.06.28
[T101] 3-5. Terraform - 함수  (0) 2024.06.27