이 스터디는 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 |