공부하면서/Terraform

[T1012] 3주차 - 테라폼 기본 사용 3/3 (terraform_data, moved, 환경변수)

omelette master 2023. 7. 22. 15:27
해당 내용은 T1012 스터디에 나온 내용과 '테라폼으로 시작하는 IaC' 책을 기준으로 정리 했습니다

3.13 null_resource와 terraform_data

테라폼 프로비저닝 동작을 설계하면서 사용자가 의도적으로 프로비저닝하는 동작을 조율해야 하는 상황이 발생하는데,
프로바이더가 제공하는 리소스 수명주기 관리만으로는 이를 해결하기 어렵기 때문에 해당 리소스가 필요하다
  • 1.3 까지는 null_resource 에 대한 소개
    1.4 부터는 terraform_data 에 대한 소개로 변경되었음을 확인 할수 있습니다
    하지만 1.4 이후 소개가 바뀌었을뿐 null_resource 사용 가능
  • null_resource 대체로 terraform_data에 대한 사용 방법 예제 도 따로 있습니다

null_resource/terraform_data 가 주로 사용되는 시나리오

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

예제) 상호 참조

# 실습환경 구성
mkdir 3.13 && cd 3.13

# main.tf 생성
provider "aws" {
  region = "ap-northeast-2"
}

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-dbc571b0" 
  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" { # aws_eip를 참조하여 echo 출력
    inline = [
      "echo ${aws_eip.myeip.public_ip}"
     ]
  }
}

resource "aws_eip" "myeip" { # aws_eip에서 aws_instance를 참조하여 생성
  #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"
}

실행

테라폼 구성 정의에서 상호 참조가 발생하는 상황으로, 실제 실행되는 코드를 작성하여 plan 수행 시 에러 발생

terraform init
terraform plan

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

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-0aeb3323c4754288b"
  private_ip             = "172.31.0.100"
  key_name               = "km-pem" # 각자 자신의 EC2 SSH Keypair 이름 지정
  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"
  }

}

resource "aws_internet_gateway" "gw" {
  vpc_id = "vpc-09f1a0dadca039acc"

  tags = {
    Name = "main"
  }
}

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

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

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

output "eip" {
  value       = aws_eip.myeip.public_ip
  description = "The EIP of the Instance"
}

(스터디 시간에 못한 삽질) 수동으로 igw를 생성하고, 라우팅 테이블에 igw 등록

스터디 시간에 이런 애러가 발생해서 해맷음...
실행 성공하면 이렇게 출력된다

output 출력으로 봤을때

public_ip는 ec2의 퍼블릭 ip

eip는 aws에서 발급받은 ip > 이후 ec2 연결시 사용되는 ip

그렇기 때문에 eip를 ec2 등록후 기존에 있던 public_ip는 폐기된다 (기존에는 신경 안쓴건데 이번에 알게 되었음 악분짱...)

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

resource "null_resource" "foo" {
  triggers = {
    ec2_id = aws_instance.bar.id # instance의 id가 변경되는 경우 재실행
  }
  ...생략...
}

resource "null_resource" "bar" {
  triggers = {
    ec2_id = time() # 테라폼으로 실행 계획을 생성할 떄마다 재실행
  }
  ...생략...
}

terraform_data 사용

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

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

3.14 moved 블록

기본적으로 state에 기록되는 리소스 주소의 이름이 변경되면 기존 리소스는 삭제되고 새로운 리소스가 생성되는데
moved 블록을 이용하여 변경되는 리소스 주소를 리소스 영향 없이 삭제하지 않고 반영할수 있다
 

Refactoring | Terraform | HashiCorp Developer

How to make backward-compatible changes to modules already in use.

developer.hashicorp.com

  • 리소스의 이름은 변경되지만 이미 테라폼으로 프로비저닝된 환경을 그대로 유지하고자 하는 경우 테라폼 1.1 버전부터 moved 블록을 사용할 수 있다.
  • 'moved'라는 단어가 의미하는 것처럼 테라폼 State에서 옮겨진 대상의 이전 주소와 새 주소를 알리는 역할을 수행한다.
  • moved 블록 이전에는 State를 직접 편집하는 terraform state mv 명령을 사용하여 State를 건드려야 하는 부담이 있었다면, moved 블록은 State에 접근 권한이 없는 사용자라도 변경되는 주소를 리소스 영향 없이 반영할 수 있다

moved  주로 사용되는 예

  • 리소스 이름을 변경
  • count로 처리하던 반복문을 for_each로 변경
  • 리소스가 모듈로 이동하여 참조되는 주소가 변경

moved 실습

# 실습환경
mkdir 3.14 && cd 3.14

# main.tf 생성
resource "local_file" "a" {
  content  = "foo!"
  filename = "${path.module}/foo.bar"
}

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

실행

# 초기화 및 실행
terraform init && terraform plan && terraform apply -auto-approve
cat foo.bar ; echo

# 확인
terraform state list
echo "local_file.a" | terraform console

# local_file 의 이름을 a → b로 변경 가정
# main.tf 파일 수정

resource "local_file" "b" {
  content  = "foo!"
  filename = "${path.module}/foo.bar"
}

output "file_content" {
  value = local_file.b.content
}
# 리소스 주소가 변경되어 a를 삭제하고 b로 생성하려고 한다
# plan 실행
terraform plan

# local_file.a 의 프로비저닝 결과를 유지한 채 이름을 변경하기 위해 moved 블록을 활용
# main.tf 파일 수정

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
}
# plan 확인
terraform plan
...
Terraform will perform the following actions:

  # local_file.a has moved to local_file.b
    resource "local_file" "b" {
        id                   = "4bf3e335199107182c6f7638efaad377acc7f452"
        # (10 unchanged attributes hidden)
    }

Plan: 0 to add, 0 to change, 0 to destroy.

# 실행
terraform apply -auto-approve
terraform state list
echo "local_file.b" | terraform console

a has moved to b

a의 ID가 그대로 b로 옮겨진것을 확인할수 있었다

# state 에 기록되었기 때문에 moved 블록을 삭제하여 최종 수정 완료
# main.tf 파일 수정
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
}

3.15 CLI를 위한 시스템 환경 변수 (자세한건 링크 참고!)

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

Environment Variables | Terraform | HashiCorp Developer

Learn to use environment variables to change Terraform's default behavior. Configure log content and output, set variables, and more.

developer.hashicorp.com

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

TF_LOG

테라폼의 stderr 로그에 대한 레벨을 정의
[디버깅을 위한 변수]
TF_LOG: 로깅 레벨 지정 또는 해제
TF_LOG_PATH: 로그 출력 파일 위치 지정
TF_LOG_CORE: TF_LOG와 별도로 테라폼 자체 코어에 대한 로깅 레벨 지정 또는 해제
TF_LOG_PROVIDER: TF_LOG와 별도로 테라폼에서 사용하는 프로바이더에 대한 로깅 레벨 지정 또는 해제

TF_INPUT

값을 false 또는 0으로 설정하면 테라폼 실행 시 인수에 -input=false 를 추가한 것과 동일한 수행 결과를 확인

TF_VAR_name

TF_VAR_<변수 이름>을 사용하면 입력 시 또는 default로 선언된 변수 값을 대체한다
 

[T1012] 2주차 - 테라폼 기본 사용 2/3 (입력변수)

해당 내용은 T1012 스터디에 나온 내용과 '테라폼으로 시작하는 IaC' 책을 기준으로 정리 했습니다 실습 환경 mkdir 3.5 && cd 3.5 3.6 입력 변수 Variable 입력변수는 인프라를 구성하는데 필요한 속성 값

portnumber.tistory.com

TF_CLI_ARGS / TF_CLI_ARGS_subcommand

테라폼 실행 시 추가할 인수를 정의

TF_DATA_DIR

State 저장 백엔드 설정과 같은 작업 디렉터리별 데이터를 보관하는 위치를 지정