Terraform은 HashiCorp에서 개발한 오픈소스 Infrastructure as Code(IaC) 도구입니다. 선언적 구성 언어(HCL - HashiCorp Configuration Language)를 사용하여 클라우드 및 온프레미스 리소스를 안전하고 효율적으로 구축, 변경, 버전 관리할 수 있습니다.
주요 특징 1. 멀티 클라우드 지원
AWS, Azure, GCP, Kubernetes 등 다양한 프로바이더 지원
700개 이상의 프로바이더 사용 가능
단일 구성으로 여러 클라우드 관리 가능
2. 선언적 구성
원하는 최종 상태를 선언하면 Terraform이 자동으로 실행 계획 수립
명령형이 아닌 선언형 접근 방식
3. 실행 계획 (Plan)
변경 사항을 미리 확인 가능
예상치 못한 변경 방지
terraform plan 명령으로 변경 사항 시뮬레이션
4. 상태 관리
인프라의 현재 상태를 추적
원격 상태 저장 지원 (S3, Terraform Cloud 등)
팀 협업 시 상태 잠금(lock) 기능 제공
5. 리소스 그래프
리소스 간 종속성을 자동으로 파악
병렬 실행으로 효율성 향상
올바른 순서로 리소스 생성/삭제
설치 방법 macOS brew tap hashicorp/tap brew install hashicorp/tap/terraform terraform version
Linux wget -O- https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.listsudo apt update && sudo apt install terraform sudo yum install -y yum-utils sudo yum-config-manager --add-repo https://rpm.releases.hashicorp.com/RHEL/hashicorp.repo sudo yum -y install terraform
Windows choco install terraform scoop install terraform
기본 구조 프로젝트 구조 terraform-project/ ├── main.tf # 주요 리소스 정의 ├── variables.tf # 입력 변수 정의 ├── outputs.tf # 출력 값 정의 ├── terraform.tfvars # 변수 값 설정 ├── providers.tf # 프로바이더 설정 └── modules/ # 재사용 가능한 모듈 ├── vpc/ ├── ec2/ └── rds/
HCL 기본 문법 1. 프로바이더 설정 providers.tf terraform { required_version = ">= 1.0" required_providers { aws = { source = "hashicorp/aws" version = "~> 5.0" } } backend "s3" { bucket = "my-terraform-state" key = "prod/terraform.tfstate" region = "ap-northeast-2" encrypt = true dynamodb_table = "terraform-lock" } } provider "aws" { region = var.aws_region default_tags { tags = { Environment = var.environment ManagedBy = "Terraform" } } }
2. 변수 정의 variables.tf variable "aws_region" { description = "AWS 리전" type = string default = "ap-northeast-2" } variable "environment" { description = "환경 (dev, staging, prod)" type = string validation { condition = contains(["dev", "staging", "prod"], var.environment) error_message = "환경은 dev, staging, prod 중 하나여야 합니다." } } variable "vpc_cidr" { description = "VPC CIDR 블록" type = string default = "10.0.0.0/16" } variable "instance_type" { description = "EC2 인스턴스 타입" type = string default = "t3.micro" } variable "tags" { description = "리소스 태그" type = map(string) default = {} }
3. 리소스 정의 main.tf # VPC 생성 resource "aws_vpc" "main" { cidr_block = var.vpc_cidr enable_dns_hostnames = true enable_dns_support = true tags = merge( var.tags, { Name = "${var.environment}-vpc" } ) } # 서브넷 생성 resource "aws_subnet" "public" { count = length(var.availability_zones) vpc_id = aws_vpc.main.id cidr_block = cidrsubnet(var.vpc_cidr, 8, count.index) availability_zone = var.availability_zones[count.index] map_public_ip_on_launch = true tags = { Name = "${var.environment}-public-subnet-${count.index + 1}" Type = "Public" } } # 보안 그룹 resource "aws_security_group" "web" { name = "${var.environment}-web-sg" description = "Web server security group" vpc_id = aws_vpc.main.id ingress { description = "HTTP" from_port = 80 to_port = 80 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } ingress { description = "HTTPS" from_port = 443 to_port = 443 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } egress { description = "All outbound" from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] } tags = { Name = "${var.environment}-web-sg" } } # EC2 인스턴스 resource "aws_instance" "web" { ami = data.aws_ami.amazon_linux_2.id instance_type = var.instance_type subnet_id = aws_subnet.public[0].id vpc_security_group_ids = [aws_security_group.web.id] user_data = <<-EOF #!/bin/bash yum update -y yum install -y httpd systemctl start httpd systemctl enable httpd echo "<h1>Hello from Terraform</h1>" > /var/www/html/index.html EOF tags = { Name = "${var.environment}-web-server" } }
4. 데이터 소스 data.tf data "aws_ami" "amazon_linux_2" { most_recent = true owners = ["amazon"] filter { name = "name" values = ["amzn2-ami-hvm-*-x86_64-gp2"] } filter { name = "virtualization-type" values = ["hvm"] } } data "aws_availability_zones" "available" { state = "available" }
5. 출력 값 outputs.tf output "vpc_id" { description = "VPC ID" value = aws_vpc.main.id } output "public_subnet_ids" { description = "Public 서브넷 ID 목록" value = aws_subnet.public[*].id } output "web_server_public_ip" { description = "웹 서버 공인 IP" value = aws_instance.web.public_ip } output "web_server_url" { description = "웹 서버 URL" value = "http://${aws_instance.web.public_ip}" }
주요 명령어 초기화 terraform init terraform init -upgrade
포맷팅 및 검증 terraform fmt terraform fmt -recursive terraform validate
계획 및 적용 terraform plan terraform plan -var-file="prod.tfvars" terraform plan -target=aws_instance.web terraform apply terraform apply -auto-approve terraform apply -target=aws_instance.web
상태 관리 terraform state list terraform state show aws_instance.web terraform state mv aws_instance.old aws_instance.new terraform state rm aws_instance.web terraform import aws_instance.web i-1234567890abcdef0
출력 및 조회 terraform output terraform output vpc_id terraform output -json
리소스 삭제 terraform destroy terraform destroy -target=aws_instance.web terraform destroy -auto-approve
워크스페이스 관리 terraform workspace list terraform workspace new dev terraform workspace select prod terraform workspace show terraform workspace delete dev
모듈 활용 모듈 구조 modules/ └── vpc/ ├── main.tf ├── variables.tf ├── outputs.tf └── README.md
모듈 정의 modules/vpc/main.tf resource "aws_vpc" "this" { cidr_block = var.cidr_block enable_dns_hostnames = var.enable_dns_hostnames enable_dns_support = var.enable_dns_support tags = merge( var.tags, { Name = var.name } ) } resource "aws_internet_gateway" "this" { vpc_id = aws_vpc.this.id tags = merge( var.tags, { Name = "${var.name}-igw" } ) }
modules/vpc/variables.tf variable "name" { description = "VPC 이름" type = string } variable "cidr_block" { description = "VPC CIDR 블록" type = string } variable "enable_dns_hostnames" { description = "DNS 호스트명 활성화" type = bool default = true } variable "enable_dns_support" { description = "DNS 지원 활성화" type = bool default = true } variable "tags" { description = "태그" type = map(string) default = {} }
modules/vpc/outputs.tf output "vpc_id" { description = "VPC ID" value = aws_vpc.this.id } output "igw_id" { description = "Internet Gateway ID" value = aws_internet_gateway.this.id }
모듈 사용 main.tf module "vpc" { source = "./modules/vpc" name = "${var.environment}-vpc" cidr_block = var.vpc_cidr tags = { Environment = var.environment ManagedBy = "Terraform" } } # 모듈 출력 값 사용 resource "aws_subnet" "public" { vpc_id = module.vpc.vpc_id cidr_block = "10.0.1.0/24" tags = { Name = "${var.environment}-public-subnet" } }
고급 기능 1. 반복문 (Loop) count 사용 resource "aws_instance" "server" { count = 3 ami = data.aws_ami.amazon_linux_2.id instance_type = "t3.micro" tags = { Name = "server-${count.index + 1}" } }
for_each 사용 variable "instances" { type = map(object({ instance_type = string ami = string })) default = { web = { instance_type = "t3.micro" ami = "ami-0c55b159cbfafe1f0" } app = { instance_type = "t3.small" ami = "ami-0c55b159cbfafe1f0" } } } resource "aws_instance" "server" { for_each = var.instances ami = each.value.ami instance_type = each.value.instance_type tags = { Name = "${each.key}-server" } }
2. 조건문 resource "aws_instance" "server" { ami = data.aws_ami.amazon_linux_2.id instance_type = var.environment == "prod" ? "t3.large" : "t3.micro" tags = { Name = var.environment == "prod" ? "prod-server" : "dev-server" } } # 조건부 리소스 생성 resource "aws_eip" "server" { count = var.create_eip ? 1 : 0 instance = aws_instance.server.id domain = "vpc" }
3. 동적 블록 variable "ingress_rules" { type = list(object({ from_port = number to_port = number protocol = string cidr_blocks = list(string) description = string })) default = [ { from_port = 80 to_port = 80 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] description = "HTTP" }, { from_port = 443 to_port = 443 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] description = "HTTPS" } ] } resource "aws_security_group" "web" { name = "web-sg" vpc_id = aws_vpc.main.id dynamic "ingress" { for_each = var.ingress_rules content { from_port = ingress.value.from_port to_port = ingress.value.to_port protocol = ingress.value.protocol cidr_blocks = ingress.value.cidr_blocks description = ingress.value.description } } }
4. 로컬 변수 locals { common_tags = { Environment = var.environment Project = var.project_name ManagedBy = "Terraform" } vpc_name = "${var.environment}-${var.project_name}-vpc" availability_zones = slice(data.aws_availability_zones.available.names, 0, 3) } resource "aws_vpc" "main" { cidr_block = var.vpc_cidr tags = merge( local.common_tags, { Name = local.vpc_name } ) }
5. 함수 활용 # 문자열 함수 output "upper_name" { value = upper(var.name) } output "lower_name" { value = lower(var.name) } # 숫자 함수 output "max_value" { value = max(5, 12, 9) } # 컬렉션 함수 output "subnet_ids" { value = join(",", aws_subnet.public[*].id) } output "first_subnet" { value = element(aws_subnet.public[*].id, 0) } # IP 네트워크 함수 output "subnets" { value = [ for i in range(3) : cidrsubnet(var.vpc_cidr, 8, i) ] } # 날짜/시간 함수 output "timestamp" { value = timestamp() } # 파일시스템 함수 resource "aws_instance" "web" { user_data = file("${path.module}/user-data.sh") } # 인코딩 함수 output "encoded" { value = base64encode("Hello Terraform") }
상태 관리 Best Practices 원격 백엔드 설정 (S3) terraform { backend "s3" { bucket = "my-terraform-state" key = "prod/terraform.tfstate" region = "ap-northeast-2" encrypt = true dynamodb_table = "terraform-lock" # 상태 파일 버전 관리 versioning = true } }
DynamoDB 잠금 테이블 생성 resource "aws_dynamodb_table" "terraform_lock" { name = "terraform-lock" billing_mode = "PAY_PER_REQUEST" hash_key = "LockID" attribute { name = "LockID" type = "S" } tags = { Name = "Terraform Lock Table" } }
워크플로우 예제 환경별 관리 terraform/ ├── environments/ │ ├── dev/ │ │ ├── main.tf │ │ ├── terraform.tfvars │ │ └── backend.tf │ ├── staging/ │ │ ├── main.tf │ │ ├── terraform.tfvars │ │ └── backend.tf │ └── prod/ │ ├── main.tf │ ├── terraform.tfvars │ └── backend.tf └── modules/ ├── vpc/ ├── ec2/ └── rds/
CI/CD 파이프라인 (GitHub Actions) name: Terraform on: push: branches: [ main ] pull_request: branches: [ main ] env: TF_VERSION: 1.6 .0 AWS_REGION: ap-northeast-2 jobs: terraform: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v3 - name: Setup Terraform uses: hashicorp/setup-terraform@v2 with: terraform_version: ${{ env.TF_VERSION }} - name: Configure AWS Credentials uses: aws-actions/configure-aws-credentials@v2 with: aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws-region: ${{ env.AWS_REGION }} - name: Terraform Format run: terraform fmt -check -recursive - name: Terraform Init run: terraform init - name: Terraform Validate run: terraform validate - name: Terraform Plan run: terraform plan -out=tfplan - name: Terraform Apply if: github.ref == 'refs/heads/main' && github.event_name == 'push' run: terraform apply -auto-approve tfplan
Best Practices 1. 코드 구조화
리소스를 논리적으로 그룹화
재사용 가능한 모듈 작성
환경별로 분리 (dev, staging, prod)
2. 명명 규칙 # 일관된 명명 규칙 사용 resource "aws_instance" "web_server" { # snake_case tags = { Name = "${var.environment}-web-server" # kebab-case } }
3. 변수 사용
하드코딩 대신 변수 사용
민감한 정보는 환경 변수나 Vault 사용
기본값 제공
4. 상태 관리
원격 백엔드 사용 (S3 + DynamoDB)
상태 파일 암호화
버전 관리 활성화
5. 버전 관리 terraform { required_version = ">= 1.0, < 2.0" required_providers { aws = { source = "hashicorp/aws" version = "~> 5.0" # 5.x 버전 사용 } } }
6. 태그 전략 locals { common_tags = { Environment = var.environment Project = var.project_name ManagedBy = "Terraform" Owner = var.owner CostCenter = var.cost_center } } # 모든 리소스에 공통 태그 적용 resource "aws_instance" "web" { tags = merge( local.common_tags, { Name = "web-server" Role = "web" } ) }
7. 문서화 # 주석으로 의도 명확히 표현 variable "instance_type" { description = "EC2 인스턴스 타입. 프로덕션 환경에서는 최소 t3.small 권장" type = string default = "t3.micro" }
8. 보안 # 민감한 정보는 sensitive 표시 variable "db_password" { description = "데이터베이스 비밀번호" type = string sensitive = true } output "db_password" { value = var.db_password sensitive = true }
실전 예제: 3-Tier 아키텍처 # VPC module "vpc" { source = "./modules/vpc" name = "${var.environment}-vpc" cidr_block = "10.0.0.0/16" availability_zones = ["ap-northeast-2a", "ap-northeast-2c"] public_subnets = ["10.0.1.0/24", "10.0.2.0/24"] private_subnets = ["10.0.11.0/24", "10.0.12.0/24"] database_subnets = ["10.0.21.0/24", "10.0.22.0/24"] enable_nat_gateway = true single_nat_gateway = var.environment != "prod" tags = local.common_tags } # ALB module "alb" { source = "./modules/alb" name = "${var.environment}-alb" vpc_id = module.vpc.vpc_id public_subnets = module.vpc.public_subnet_ids security_groups = [aws_security_group.alb.id] tags = local.common_tags } # EC2 Auto Scaling module "asg" { source = "./modules/asg" name = "${var.environment}-web-asg" vpc_id = module.vpc.vpc_id private_subnets = module.vpc.private_subnet_ids target_group_arns = [module.alb.target_group_arn] min_size = var.environment == "prod" ? 2 : 1 max_size = var.environment == "prod" ? 10 : 3 desired_capacity = var.environment == "prod" ? 2 : 1 instance_type = var.instance_type ami_id = data.aws_ami.amazon_linux_2.id tags = local.common_tags } # RDS module "rds" { source = "./modules/rds" identifier = "${var.environment}-db" engine = "mysql" engine_version = "8.0" instance_class = var.environment == "prod" ? "db.t3.medium" : "db.t3.micro" allocated_storage = var.environment == "prod" ? 100 : 20 db_name = var.db_name username = var.db_username password = var.db_password vpc_id = module.vpc.vpc_id subnet_ids = module.vpc.database_subnet_ids security_group_ids = [aws_security_group.rds.id] multi_az = var.environment == "prod" backup_retention_period = var.environment == "prod" ? 7 : 1 tags = local.common_tags }
트러블슈팅 일반적인 문제 해결 1. 상태 파일 충돌 terraform force-unlock <LOCK_ID> terraform refresh
2. 리소스 import terraform import aws_instance.web i-1234567890abcdef0
3. 상태 파일 복구 aws s3 cp s3://my-terraform-state/prod/terraform.tfstate.backup terraform.tfstate
4. 디버깅 export TF_LOG=DEBUGexport TF_LOG_PATH=./terraform.logterraform plan
유용한 도구 1. Terragrunt
Terraform 코드를 DRY하게 관리
여러 환경 구성 간소화
2. tflint brew install tflint tflint
brew install terraform-docs terraform-docs markdown table . > README.md
4. Checkov pip install checkov checkov -d .
5. Infracost brew install infracost infracost breakdown --path .