Terraform으로 azure 인프라 코드화 하기

2025. 10. 8. 17:29·Infra
Azure Portal에서 수동으로 리소스를 생성하는 대신, Terraform을 사용하여 인프라를 코드로 정의하고(IaC) 자동화된 방식으로 배포했습니다.

 

Terraform을 사용하는 이유

VM을 생성하고, 네트워크를 설정하고, 방화벽 규칙을 추가하는 등 모든 과정을 Azure Portal에서 수동으로 진행하면 몇 가지 문제가 발생합니다.

  • Human Error: 사람이 직접 작업하다 보면 실수가 발생하기 쉽습니다.
  • 재사용성 및 확장성 부족: 동일한 환경을 다시 구축하려면 모든 과정을 반복해야 합니다.
  • 변경 이력 관리의 어려움: 누가, 언제, 무엇을 변경했는지 추적하기 어렵습니다. (일명 'Infrastructure Drift')

이러한 문제들을 해결하기 위해 Terraform을 도입하여 인프라를 코드로 관리(IaC)하기로 했습니다.

 

Tech Stack

  • Cloud: Azure
  • Infrastructure as Code: Terraform
  • CI/CD: Github Actions
  • Application Environment:
    • Azure VM (Standard_B2s)
    • Docker & Docker Compose
  • Services on VM:
    • Node.js Application
    • MongoDB (Cosmos DB 대체)
    • Redis (Azure Cache for Redis 대체)
  • Data Storage: Azure Blob Storage (파일 등 정적 데이터 저장용

 

인프라 구축을 위한 준비

Terraform으로 인프라를 생성하기 전, 몇 가지 준비가 필요합니다.

1) Azure CLI 설치 및 로그인

Terraform이 Azure와 통신하려면 Azure CLI를 통해 인증을 받아야 합니다.

## azure cli 설치 후 진행
## 아래 명령어 실행 후 azure subscription 선택 
$ az login

2) ssh 키 생성

Terraform이 VM 생성할 때 등록할 ssh키를 생성합니다.

## SSH 키 생성
$ ssh-keygen -t rsa -b 4096 -C "{azure 가입 이메일}" -f ~/.ssh/azure_vm

## 공개 키 확인
# 이 키 값은 나중에 Terraform 변수로 사용됩니다.
$ cat ~/.ssh/azure_vm.pub

3) Terraform으로 인프라 코드 작성하기

 

프로젝트 구조

terraform/
├── main.tf         # 전체 모듈을 호출하고 프로바이더를 설정하는 메인 파일
├── variables.tf    # 사용할 변수들을 정의하는 파일
├── outputs.tf      # 배포 후 출력할 값(VM IP 주소 등)을 정의하는 파일
├── terraform.tfvars # 변수에 실제 값을 할당하는 파일 
└── modules/
    ├── acr/
    │   ├── main.tf
    │   ├── variables.tf
    │   └── outputs.tf
    ├── networking/ # 가상 네트워크, 서브넷 등 네트워크 관련 리소스
    │   ├── main.tf
    │   ├── variables.tf
    │   └── outputs.tf
    ├── storage/
    │   ├── main.tf
    │   ├── variables.tf
    │   └── outputs.tf
    └── vm/ # 가상 머신, 보안 그룹 등 VM 관련 리소스
        ├── main.tf
        ├── variables.tf
        ├── outputs.tf
        └── cloud-init.yaml # VM 생성 시 실행할 초기화 스크립트

핵심 코드 살펴보기

1. VM 모듈 (modules/vm/main.tf) 가상 머신을 생성하는 핵심 코드입니다. 여기서는 VM의 크기, 이미지, 네트워크 인터페이스, 그리고 SSH 공개 키를 지정합니다.

# Public IP Address
resource "azurerm_public_ip" "main" {
  name                = "${var.project_name}-${var.environment}-pip"
  location            = var.location
  resource_group_name = var.resource_group_name
  allocation_method   = "Static" 
  sku                 = "Standard"
  domain_name_label   = "${var.project_name}-${var.environment}" 

  tags = var.tags
}

# Network Interface
resource "azurerm_network_interface" "main" {
  name                = "${var.project_name}-${var.environment}-nic"
  location            = var.location
  resource_group_name = var.resource_group_name

  ip_configuration {
    name                          = "internal"
    subnet_id                     = var.subnet_id
    private_ip_address_allocation = "Dynamic"
    public_ip_address_id          = azurerm_public_ip.main.id
  }

  tags = var.tags
}

# Virtual Machine
resource "azurerm_linux_virtual_machine" "main" {
  name                = "${var.project_name}-${var.environment}-vm"
  location            = var.location
  resource_group_name = var.resource_group_name
  size                = var.vm_size
  admin_username      = var.admin_username

  network_interface_ids = [
    azurerm_network_interface.main.id,
  ]

  # SSH 키 인증 (비밀번호보다 안전)
  admin_ssh_key {
    username   = var.admin_username
    public_key = var.ssh_public_key
  }

  # OS 디스크 설정
  os_disk {
    name                 = "${var.project_name}-${var.environment}-osdisk"
    caching              = "ReadWrite"
    storage_account_type = "Premium_LRS"
    disk_size_gb         = 30  
  }

  # Ubuntu 22.04 LTS 이미지
  source_image_reference {
    publisher = "Canonical"
    offer     = "0001-com-ubuntu-server-jammy"
    sku       = "22_04-lts-gen2"
    version   = "latest"
  }

  # VM 생성 시 실행할 스크립트
  custom_data = base64encode(templatefile("${path.module}/cloud-init.yaml", {
    jwt_secret                = var.jwt_secret
    storage_account_name      = var.storage_account_name
    storage_account_key       = var.storage_account_key
    storage_connection_string = var.storage_connection_string
  }))

  tags = var.tags
}

# Data Disk
resource "azurerm_managed_disk" "data" {
  name                 = "${var.project_name}-${var.environment}-datadisk"
  location             = var.location
  resource_group_name  = var.resource_group_name
  storage_account_type = "Premium_LRS"
  create_option        = "Empty"
  disk_size_gb         = 64  

  tags = var.tags
}

# Data Disk를 VM에 연결
resource "azurerm_virtual_machine_data_disk_attachment" "data" {
  managed_disk_id    = azurerm_managed_disk.data.id
  virtual_machine_id = azurerm_linux_virtual_machine.main.id
  lun                = 0
  caching            = "ReadWrite"
}

 

2. cloud-init.yaml - cloud-init은 VM이 처음 부팅될 때 단 한 번 실행되는 스크립트로, 수동으로 서버에 접속해서 Docker나 Node.js를 설치할 필요가 없게 해줍니다.

#cloud-config

# 패키지 업데이트
package_update: true
package_upgrade: true

# 설치할 패키지
packages:
  - apt-transport-https
  - ca-certificates
  - curl
  - gnupg
  - lsb-release
  - git
  - make

# 실행할 명령어들
runcmd:
  # Docker 설치
  - curl -fsSL https://get.docker.com -o get-docker.sh
  - sh get-docker.sh
  - usermod -aG docker azureuser
  
  # Docker Compose 설치
  - curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
  - chmod +x /usr/local/bin/docker-compose
  
  # 데이터 디스크 마운트 (MongoDB 데이터 저장용)
  - mkdir -p /data
  - |
    if [ -b /dev/sdc ]; then
      mkfs.ext4 /dev/sdc
      echo '/dev/sdc /data ext4 defaults,nofail 0 2' >> /etc/fstab
      mount /data
    fi
  
  # 애플리케이션 디렉토리 생성
  - mkdir -p /opt/photo-storage
  - chown -R azureuser:azureuser /opt/photo-storage
  
  # 환경 변수 파일 생성
  - |
    cat > /opt/photo-storage/.env << 'EOF'
    NODE_ENV=production
    PORT=3000
    
    # MongoDB
    MONGO_URI=mongodb://mongodb:27017/photo_storage
    
    # Redis
    REDIS_HOST=redis
    REDIS_PORT=6379
    
    # Azure Blob Storage (MinIO 대체)
    AZURE_STORAGE_ACCOUNT_NAME=${storage_account_name}
    AZURE_STORAGE_ACCOUNT_KEY=${storage_account_key}
    AZURE_STORAGE_CONNECTION_STRING=${storage_connection_string}
    AZURE_STORAGE_CONTAINER=photos
    
    # JWT
    JWT_SECRET=${jwt_secret}
    EOF
  
  # Docker Compose 파일은 GitHub Actions에서 배포됨
  # 여기서는 기본 설정만 준비
  
  # Docker 서비스 시작
  - systemctl enable docker
  - systemctl start docker
  
  # 로그 디렉토리 생성
  - mkdir -p /var/log/photo-storage
  - chown -R azureuser:azureuser /var/log/photo-storage
  
 # 재부팅 (Docker 그룹 적용)
power_state:
  mode: reboot
  timeout: 300
  condition: True

 

 

3)  배포 및 실행

### terraform 디렉토리에서 초기화       
$ terraform init

### terraform 실행계획 확인
$ terraform plan

### terraform 인프라 생성
$ terraform apply

 

 

인프라 생성 성공시 아래와 같은 명령어 수행 결과와 azure portal에서 생성된 인프라를 확인할 수 있습니다.

'Infra' 카테고리의 다른 글

방화벽 이용해서 폐쇄망 구축하기  (1) 2025.08.20
LVM 다뤄보기  (0) 2025.02.16
terraform으로 aws 인프라 구축하기 (3) (상태 관리)  (0) 2024.12.04
terraform으로 aws 인프라 구축하기 (2) (Auto Scaling)  (0) 2024.12.04
terraform으로 aws 인프라 구축하기 (1)  (0) 2024.12.04
'Infra' 카테고리의 다른 글
  • 방화벽 이용해서 폐쇄망 구축하기
  • LVM 다뤄보기
  • terraform으로 aws 인프라 구축하기 (3) (상태 관리)
  • terraform으로 aws 인프라 구축하기 (2) (Auto Scaling)
khjoon
khjoon
  • khjoon
    기록기록
    khjoon
  • 전체
    오늘
    어제
    • 분류 전체보기 (39)
      • Security (2)
      • Dev (14)
      • Infra (14)
      • Ops (9)
  • 블로그 메뉴

    • 홈
  • 링크

  • 공지사항

  • 인기 글

  • 태그

  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.1
khjoon
Terraform으로 azure 인프라 코드화 하기
상단으로

티스토리툴바