Userdata와 Provisioner의 기본 개념
Userdata: AWS EC2나 다른 클라우드 서비스의 인스턴스 생성 시 자동으로 실행할 스크립트를 정의하는 부분입니다. 주로 초기 설정이나 패키지 설치 등에 사용됩니다.
Provisioner: 테라폼이 리소스를 생성하거나 제거한 후에 실행되는 추가적인 도구입니다.
Userdata
Userdata는 클라우드 인스턴스가 처음 실행될 때 한 번만 실행됩니다. 예를 들어, AWS EC2 인스턴스가 시작될 때 필요한 소프트웨어를 설치하거나 설정 파일을 변경할 수 있습니다.
한계점: 이미 실행된 인스턴스에 대해 변경 사항을 반영하기 어렵습니다. 변경을 위해서는 인스턴스 재시작 또는 다시 생성이 필요합니다.
Userdata 예시:
versions.tf
# 테라폼과 AWS 프로바이더의 버전을 지정
terraform {
required_version = ">= 1.2.6"
required_providers {
aws = ">= 4.25.0"
}
}
variables.tf
# locals
locals {
vpc_name = "jeffVpc"
common_tags = {
"Project" = "ec2-userdata"
}
}
# aws region
variable "region" {
type = string
}
# aws ec2
variable "key_name" {
type = string
}
variable "vpc_id" {
type = string
}
variable "sub_pub_m_a" {
type = string
}
variable "sub_pub_m_c" {
type = string
}
terraform.tfvars
# aws region
region = "ap-northeast-2"
# aws ec2
key_name = "jeff"
sub_pub_m_a = "subnet-004f0791a0c0c4e57"
sub_pub_m_c = "subnet-0dd6cfeb8fe83cadc"
vpc_id = "vpc-0741d8ecd58197d0c"
terraform.tf
provider "aws" {
region = var.region
}
aws-ec2.tf
Userdata: ec2가 첫 부팅될 때 사용된다.
data "aws_ami" "ubuntu" {
most_recent = true
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"]
}
filter {
name = "virtualization-type"
values = ["hvm"]
}
owners = ["099720109477"] # Canonical
}
resource "aws_instance" "userdata" {
ami = data.aws_ami.ubuntu.id
instance_type = "t2.large"
key_name = var.key_name
subnet_id = var.sub_pub_m_a
vpc_security_group_ids = [aws_security_group.jeffSecurityGroup.id]
user_data = <<EOT
#!/bin/bash
sudo apt update
sudo apt install -y nginx
EOT
tags = {
Name = "jeff-userdata"
}
}
참고) multiline string
<<EOT
여러 줄의
내용
작성
EOT
aws-sg.tf
# aws security group
resource "aws_security_group" "jeffSecurityGroup" {
name = "jeffSecurityGroup"
description = "jeffSecurityGroup"
vpc_id = var.vpc_id
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
description = "Allow HTTP from anywhere"
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
description = "Allow SSH from anywhere"
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
}
outputs.tf
output "userdata_instance" {
value = {
public_ip = aws_instance.userdata.public_ip
public_dns = aws_instance.userdata.public_dns
private_ip = aws_instance.userdata.private_ip
private_dns = aws_instance.userdata.private_dns
}
}
tf apply
Apply complete! Resources: 0 added, 1 changed, 0 destroyed.
Outputs:
userdata_instance = {
"private_dns" = "ip-10-0-240-187.ap-northeast-2.compute.internal"
"private_ip" = "10.0.240.187"
"public_dns" = "ec2-3-38-166-232.ap-northeast-2.compute.amazonaws.com"
"public_ip" = "3.38.166.232"
}
Provisioner
Terraform Resources Provisioner doc link
테라폼 Provisioner를 사용하여 리소스 생성 후에 구성 및 초기화 작업을 수행할 수 있습니다.
주요 특징:
- 스크립트가 실행 로그가 생성되므로 오류 해결에 도움이 됩니다.
- 기본적으로 리소스 생성 시점에 실행되지만, 옵션을 통해 리소스 삭제 시점에도 실행이 가능합니다.
Creation-Time Provisioners doc
# 리소스 삭제 시점에 provisioner 실행
provisioner "local-exec" {
when = destroy
Provisioner의 주요 유형:
- local-exec: 로컬 머신에서 명령 실행 ((테라폼 수행 머신에서 실행))
- remote-exec: 원격 머신에서 명령 실행 (ssh, winrm)
- file: 로컬에서 원격으로 파일 전송
Provisioner - in EC2
- 테라폼 프로비저닝에서는 스크립트가 실행되는 로그들이 남아 디버깅을 할 수 있다
- provisioner는 default로 creation time provisioner이다
- 아래와 같은 옵션으로 destroy 시점에서도 적용할 수 있다.
provisioner "local-exec" { when = destroy command = "echo hello world" }
- 기본적으로 aws resource가 생성되는 시점에 동작한다.
Provisioner 예시:
data "aws_ami" "ubuntu" {
most_recent = true
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"]
}
filter {
name = "virtualization-type"
values = ["hvm"]
}
owners = ["099720109477"] # Canonical
}
resource "aws_instance" "userdata" {
ami = data.aws_ami.ubuntu.id
instance_type = "t2.large"
key_name = var.key_name
subnet_id = var.sub_pub_m_a
vpc_security_group_ids = [aws_security_group.jeffSecurityGroup.id]
tags = {
Name = "jeff-provisioner"
}
# remote 머신에서 ssh로 ubunut 계정으로 머신에 접근, option (inline, script, scripts)
provisioner "remote-exec" {
inline = [
"sudo apt-get update",
"sudo apt-get install -y nginx"
]
connection {
type = "ssh"
user = "ubuntu"
host = self.public_ip
private_key = file("${path.module}/files/jeff.pem")
}
}
}
provisioner connection settings - doc link
- provisioner
- "local-exec"
- local → terraform apply 를 실행하는 현재 머신
- "remote-exec”
- 리소스를 생성한 후 타켓 머신에서 실행
- remote 머신에서 ssh로 ubunut 계정으로 머신에 접근, option (inline, script, scripts)
- scripts: 여러 스크립트 파일을 실행
- script: 로컬에 스크립트 파일을 업로드하여 사용
- inline: 명령어 리스트를 나열하여 명령 실행
- connection
- passwd, private key option 설정 후 사용
- e.g. host = aws_instance.provisioner.public_ip
- e.g. private_key = file("${path.module}/jeff.pem")
- host = self.public_ip
- provisioner에서만 사용 가능한 변수
- self는 부모 리소스를 가리킨다. 여기서는 resource "aws_instance" "provisioner” 의 public_ip
- passwd, private key option 설정 후 사용
- "local-exec"
결과 - tf apply
provisioner_instance = {
"private_dns" = "ip-172-31-47-191.ap-northeast-2.compute.internal"
"private_ip" = "172.31.47.191"
"public_dns" = "ec2-15-164-94-75.ap-northeast-2.compute.amazonaws.com"
"public_ip" = "15.164.94.75"
}
provisioner에서 같은 내용을 추가하는 경우
No changes. Your infrastructure matches the configuration.
Terraform has compared your real infrastructure against your configuration and found no differences, so no changes are
needed.
Apply complete! Resources: 0 added, 0 changed, 0 destroyed.
provisioner의 default는 creation time provisioner이므로 이미 생성된 리소스에는 변경사항이 없습니다. destroy option으로 종료 시점에 적용할 수 도 있습니다.
null-resources와 함께 사용하기 with multi provisioner
null provider - doc link 의도적으로 아무것도 하지 않는 구성을 제공하여 까다로운 동작을 조율하거나 제한 사항을 해결하는 데 유용한 다양한 상황에 유용합니다.
resource "null_resource" "provisioner" {
triggers = {
instance_id = aws_instance.provisioner.id
script = filemd5("${path.module}/files/install-nginx.sh")
index_file = filemd5("${path.module}/files/index.html")
}
- triggers안에 있는 값이 변경되면 replaced, re-running이 됩니다.
- 여기서는 instance_id,script, index_file이 변경되는 경우 trigger가 됩니다.
- intsance가 변하면 id가 합니다.
resource "null_resource" "provisioner" {
triggers = {
instance_id = aws_instance.provisioner.id
script = filemd5("${path.module}/files/install-nginx.sh")
index_file = filemd5("${path.module}/files/index.html")
}
provisioner "local-exec" {
command = "echo Hello World"
}
provisioner "file" {
source = "${path.module}/files/index.html"
destination = "/tmp/index.html"
connection {
type = "ssh"
user = "ubuntu"
host = aws_instance.provisioner.public_ip
}
}
provisioner "remote-exec" {
script = "${path.module}/files/install-nginx.sh"
connection {
type = "ssh"
user = "ubuntu"
host = aws_instance.provisioner.public_ip
}
}
provisioner "remote-exec" {
inline = [
"sudo cp /tmp/index.html /var/www/html/index.html"
]
connection {
type = "ssh"
user = "ubuntu"
host = aws_instance.provisioner.public_ip
}
}
}
결과 -tf apply
null_resource.provisioner: Creating...
null_resource.provisioner: Provisioning with 'local-exec'...
null_resource.provisioner (local-exec): Executing: ["/bin/sh" "-c" "echo Hello World"]
null_resource.provisioner (local-exec): Hello World
# 중략
결과 - triggers 동작 확인
- index file 변경 후 tf apply
files/index.html 의 내용을 아래와 같이 변경 후 확인
<h1>nginx index after triggers</h1>
- index_file 의 hash sum이 변경 됐다.
Terraform will perform the following actions:
# null_resource.provisioner must be replaced
-/+ resource "null_resource" "provisioner" {
~ id = "4762354494490443565" -> (known after apply)
~ triggers = { # forces replacement
~ "index_file" = "7940ab48cfe1e5d9cc9b75a7669daa1d" -> "6f9755c24a2176a0a909e47f38317e19"
# (2 unchanged elements hidden)
}
}
Plan: 1 to add, 0 to change, 1 to destroy.
'IaC > Terraform' 카테고리의 다른 글
[Terraform]파일(file, templatefile)을 활용한 리소스 구성하기 (0) | 2023.12.10 |
---|---|
[Terraform]워크스페이스 디렉토리 구성 전략 (0) | 2023.09.11 |
[Terraform]다른 워크스페이스의 정보 가져오기 - terraform_remote_state 사용법 (0) | 2023.09.03 |
[Terraform]리소스 강제 교체하기: taint와 untaint 사용법 (0) | 2023.09.01 |
[Terraform]상태(state) 관리 명령어 사용 방법 (0) | 2023.08.27 |