🔧 DevOps

IaC

Infrastructure as Code

인프라를 코드로 정의하고 관리하는 방식. Terraform, Pulumi, CloudFormation 사용.

상세 설명

IaC(Infrastructure as Code)는 인프라를 코드로 정의하고 관리하는 방식입니다. 수동으로 콘솔을 클릭하는 대신, 선언적 또는 명령적 코드로 서버, 네트워크, 데이터베이스 등의 인프라를 프로비저닝합니다. 버전 관리, 코드 리뷰, 자동화된 테스트가 가능해져 인프라 관리가 소프트웨어 개발처럼 됩니다.

IaC의 핵심 원칙

  • 버전 관리: 모든 인프라 변경사항을 Git으로 추적
  • 멱등성: 같은 코드를 여러 번 실행해도 동일한 결과
  • 재현성: 동일한 환경을 언제든 재생성 가능
  • 문서화: 코드 자체가 인프라 문서 역할
  • 자동화: CI/CD 파이프라인과 통합

선언적 vs 명령적

  • 선언적(Declarative): 원하는 최종 상태를 정의. Terraform, CloudFormation
  • 명령적(Imperative): 수행할 단계를 정의. Ansible, Bash 스크립트

주요 IaC 도구

  • Terraform: 멀티 클라우드 IaC 도구 (HashiCorp)
  • Pulumi: 프로그래밍 언어로 인프라 정의
  • AWS CloudFormation: AWS 네이티브 IaC
  • Azure Bicep: Azure ARM 템플릿 DSL
  • Ansible: 구성 관리 및 프로비저닝

코드 예제

Terraform - AWS 인프라 정의

# main.tf - AWS VPC와 EC2 인스턴스
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"
    dynamodb_table = "terraform-locks"
    encrypt        = true
  }
}

provider "aws" {
  region = var.region

  default_tags {
    tags = {
      Environment = var.environment
      ManagedBy   = "Terraform"
    }
  }
}

# VPC
module "vpc" {
  source  = "terraform-aws-modules/vpc/aws"
  version = "5.1.0"

  name = "${var.project}-vpc"
  cidr = "10.0.0.0/16"

  azs             = ["ap-northeast-2a", "ap-northeast-2c"]
  private_subnets = ["10.0.1.0/24", "10.0.2.0/24"]
  public_subnets  = ["10.0.101.0/24", "10.0.102.0/24"]

  enable_nat_gateway = true
  single_nat_gateway = var.environment != "production"

  tags = {
    Project = var.project
  }
}

# Security Group
resource "aws_security_group" "web" {
  name        = "${var.project}-web-sg"
  description = "Security group for web servers"
  vpc_id      = module.vpc.vpc_id

  ingress {
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  ingress {
    from_port   = 443
    to_port     = 443
    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"]
  }

  tags = {
    Name = "${var.project}-web-sg"
  }
}

# EC2 Instance
resource "aws_instance" "web" {
  count = var.instance_count

  ami           = data.aws_ami.amazon_linux.id
  instance_type = var.instance_type
  subnet_id     = module.vpc.private_subnets[count.index % length(module.vpc.private_subnets)]

  vpc_security_group_ids = [aws_security_group.web.id]

  root_block_device {
    volume_size = 20
    volume_type = "gp3"
    encrypted   = true
  }

  tags = {
    Name = "${var.project}-web-${count.index + 1}"
  }
}

# Data source
data "aws_ami" "amazon_linux" {
  most_recent = true
  owners      = ["amazon"]

  filter {
    name   = "name"
    values = ["al2023-ami-*-x86_64"]
  }
}

variables.tf - 변수 정의

# variables.tf
variable "region" {
  description = "AWS region"
  type        = string
  default     = "ap-northeast-2"
}

variable "project" {
  description = "Project name"
  type        = string
}

variable "environment" {
  description = "Environment (development, staging, production)"
  type        = string

  validation {
    condition     = contains(["development", "staging", "production"], var.environment)
    error_message = "Environment must be development, staging, or production."
  }
}

variable "instance_type" {
  description = "EC2 instance type"
  type        = string
  default     = "t3.micro"
}

variable "instance_count" {
  description = "Number of EC2 instances"
  type        = number
  default     = 2
}

terraform.tfvars - 환경별 값

# environments/prod.tfvars
region         = "ap-northeast-2"
project        = "myapp"
environment    = "production"
instance_type  = "t3.medium"
instance_count = 4

Terraform CLI 명령어

# 초기화 - provider 다운로드
terraform init

# 계획 - 변경사항 미리 확인
terraform plan -var-file=environments/prod.tfvars

# 적용 - 실제 인프라 생성/수정
terraform apply -var-file=environments/prod.tfvars

# 상태 확인
terraform state list
terraform state show aws_instance.web[0]

# 특정 리소스만 적용
terraform apply -target=aws_instance.web

# 삭제
terraform destroy -var-file=environments/prod.tfvars

# 포맷팅 및 검증
terraform fmt -recursive
terraform validate

실무 대화 예제

주니어 DevOps
"AWS 콘솔에서 인프라를 만들었는데, 스테이징 환경을 똑같이 만들어야 해요. 또 클릭해서 만들어야 하나요?"
시니어 DevOps
"수동으로 만들면 실수하기 쉽고 시간도 오래 걸려요. Terraform으로 코드화하면 `terraform apply -var-file=staging.tfvars`로 똑같은 환경을 5분 만에 만들 수 있어요."
주니어 DevOps
"인프라 변경할 때 실수로 프로덕션을 날릴까 봐 무서워요."
시니어 DevOps
"IaC의 장점이 바로 그거예요. `terraform plan`으로 변경사항을 미리 확인하고, PR로 코드 리뷰를 받은 후 적용해요. 상태 파일을 S3에 저장하고 DynamoDB로 락을 걸면 여러 명이 동시에 apply해서 꼬이는 것도 방지할 수 있어요."

주의사항

State 파일 보안

Terraform state 파일에는 비밀번호, 키 등 민감 정보가 평문으로 저장됩니다. S3 암호화, 접근 제한을 필수로 설정하세요.

Plan 확인 필수

apply 전에 항상 plan 결과를 확인하세요. 특히 `destroy` 또는 `replace`가 포함된 경우 신중히 검토하세요.

State 락

여러 명이 동시에 terraform apply를 실행하면 state가 꼬일 수 있습니다. DynamoDB 등으로 락을 설정하세요.

Drift 관리

콘솔에서 수동 변경하면 코드와 실제 상태가 달라집니다. 주기적으로 `terraform plan`으로 drift를 감지하세요.

더 배우기