plan -out → apply plan.bin.목적: 필요한 자원을 원하는 상태로 만들어 놓는 과정
예) 네트워크(VPC/Subnet/RT), 보안(SG/IAM), 컴퓨트(EC2/ASG), 데이터(RDS/S3) 등.
선언형 vs 절차형
프로비저닝 vs 구성관리 vs 배포
| 구분 | 하는 일 | 대표 도구 |
|---|---|---|
| 프로비저닝 | VPC/EC2/RDS 등 리소스 생성 | Terraform, CloudFormation |
| 구성관리 | OS/미들웨어 설정/패키지 | cloud-init(UserData), Ansible |
| 배포 | 애플리케이션 릴리즈/롤백 | GitHub Actions, Argo CD, Helm |
variables)과 출력(outputs)만 깔끔히.plan -out → apply plan.bin.sensitive), 상태는 암호화/KMS.VPC/서브넷이 있다고 가정. 퍼블릭 서브넷에 Nginx 준비.
# versions.tf
terraform {
required_version = "~> 1.9"
required_providers { aws = { source = "hashicorp/aws", version = "~> 5.0" } }
}
provider "aws" { region = "ap-northeast-2" }
# data & locals
data "aws_ssm_parameter" "al2023" {
name = "/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64"
}
variable "subnet_id" { type = string }
variable "vpc_id" { type = string }
resource "aws_security_group" "web" {
name = "nginx-sg"
vpc_id = var.vpc_id
ingress { from_port=80 to_port=80 protocol="tcp" cidr_blocks=["0.0.0.0/0"] }
egress { from_port=0 to_port=0 protocol="-1" cidr_blocks=["0.0.0.0/0"] }
}
# cloud-init(UserData)로 멱등적 설치
locals {
user_data = <<-YAML
#cloud-config
packages:
- nginx
runcmd:
- systemctl enable --now nginx
YAML
}
resource "aws_instance" "web" {
ami = data.aws_ssm_parameter.al2023.value
instance_type = "t3.micro"
subnet_id = var.subnet_id
vpc_security_group_ids = [aws_security_group.web.id]
associate_public_ip_address = true
user_data = local.user_data
tags = { Name = "web-nginx" }
}
output "web_public_ip" { value = aws_instance.web.public_ip }
명령
terraform init
terraform plan -out=plan.bin
terraform apply plan.bin
# 접속 확인
curl http://$(terraform output -raw web_public_ip)
포인트
- 소프트웨어 설치는 UserData/cloud-init으로 충분한 범위부터.
- 더 복잡하면 Ansible(아래 예제) 또는 Packer(AMI 미리 굽기) 고려.
부팅 즉시 준비된 이미지를 쓰면 배포가 빠르고 일관.
packer.pkr.hcl
packer {
required_plugins { amazon = { source = "github.com/hashicorp/amazon" } }
}
source "amazon-ebs" "al2023" {
region = "ap-northeast-2"
instance_type = "t3.micro"
source_ami_filter {
filters = { name = "al2023-ami-*-x86_64" }
owners = ["137112412989"]
most_recent = true
}
ssh_username = "ec2-user"
ami_name = "nginx-al2023-{{timestamp}}"
}
build {
sources = ["source.amazon-ebs.al2023"]
provisioner "shell" {
inline = ["sudo dnf -y install nginx", "sudo systemctl enable --now nginx"]
}
}
사용(개념): Packer가 만든 AMI ID를 Terraform 변수/SSM 파라미터로 참조.
# playbook.yml
- hosts: web
become: yes
tasks:
- name: ensure nginx
ansible.builtin.package:
name: nginx
state: present
- name: start nginx
ansible.builtin.service:
name: nginx
state: started
enabled: yes
Terraform은 서버(외부) 만들기, Ansible은 서버 안(내부) 설정에 강함.
StorageClass가 PVC 생성 시 PV를 자동 할당.# pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata: { name: data }
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: gp2
resources: { requests: { storage: 5Gi } }
해야 할 것
required_version, provider 버전 고정 + .terraform.lock.hcl 관리locals) + outputs로 필요한 값만 공개plan -out → apply plan.bin, PR에서 리뷰sensitive/Secrets Manager 사용(로그/유저데이터 평문 금지)피해야 할 것
count 인덱스 흔들림으로 대량 재생성 → for_each + 안정 키 사용프로비저닝은 원하는 상태의 인프라를 ‘안전하고 일관되게’ 준비하는 일.
Terraform으로 리소스를 만들고, cloud-init/Ansible/Packer로 내부를 준비하며,
멱등성·불변성·보안을 습관처럼 지키면 된다.