terraform
Terraform을 이용해 Azure 인프라 구축하기(application gateway, virtual machine, Auto scaling)
kkuniyo
2025. 3. 27. 15:12
반응형
이번에 추가된 Terraform 구성요소
새롭게 추가된 Terraform 파일을 통해 다음과 같은 인프라 구성이 가능합니다.
- 애플리케이션 게이트웨이(Application Gateway)
- VM 및 이미지 관리(Shared Image Gallery)
- 가상 머신 스케일 세트(VMSS)
- DNS 영역 및 레코드 관리
- DB 및 프로비저닝 스크립트(null_resource)
세부 파일 구성
다음과 같은 파일 구조로 확장되었습니다.
10_appgw.tf # Application Gateway 생성
11_appgwasso.tf # Application Gateway 연결 구성
12_vm.tf # 가상 머신 생성
13_gallery.tf # 이미지 관리(Shared Image Gallery)
14_as.tf # 가용성 집합(Availability Set) 설정
15_vmss.tf # 가상 머신 스케일 세트(VMSS) 관리
16_dns.tf # DNS 영역 관리
17_dnsrecord.tf # DNS 레코드 관리
19.null.tf # 프로비저닝 및 자동화 스크립트 실행
20_db.tf # 데이터베이스 리소스 설정
구현 효과
- 확장성 및 가용성 증대: VM 스케일 세트와 가용성 집합을 통해 자동 확장과 고가용성을 실현할 수 있습니다.
- 효율적인 애플리케이션 배포 및 관리: Application Gateway를 통해 부하 분산 및 트래픽 관리가 용이해집니다.
- 빠른 복구 및 배포 효율성 향상: Shared Image Gallery를 이용한 이미지 관리로 VM 생성과 유지 보수가 간편해집니다.
10_appgw.tf
# Local Variables
# - Application Gateway 관련 공통 이름들을 미리 정의합니다.
locals {
backend_address_pool_name = "appGatewayBackendPool" # 백엔드 주소 풀의 이름
frontend_port_name = "port_80" # 프론트엔드 포트 구성 이름
frontend_ip_configuration_name = "appGatewayFrontendIP" # 프론트엔드 IP 구성 이름
http_setting_name = "appGatewayBackendHttpSettings" # 백엔드 HTTP 설정 이름
listener_name = "appGatewayHttpListener" # HTTP 리스너 이름
request_routing_rule_name = "appGatewayRule" # 요청 라우팅 규칙 이름
}
# Application Gateway 구성
# - 외부 트래픽을 받아 내부 백엔드 풀로 라우팅하는 역할을 수행합니다.
resource "azurerm_application_gateway" "main_appgw" {
name = "main-appgw"
resource_group_name = azurerm_resource_group.main_rg.name
location = azurerm_resource_group.main_rg.location
# SKU 설정: Gateway의 성능 및 인스턴스 수 정의
sku {
name = "Standard_v2"
tier = "Standard_v2"
capacity = 2
}
# 게이트웨이 IP 구성: Application Gateway가 연결될 서브넷 지정
gateway_ip_configuration {
name = "appGatewayIpConfig"
subnet_id = azurerm_subnet.main_appgw.id
}
# 프론트엔드 포트 구성: 클라이언트의 요청을 받을 포트 설정 (예: 80번)
frontend_port {
name = local.frontend_port_name
port = 80
}
# 프론트엔드 IP 구성: 외부와의 통신을 위한 Public IP와 연계
frontend_ip_configuration {
name = local.frontend_ip_configuration_name
public_ip_address_id = azurerm_public_ip.main_appgw_ip.id
}
# 백엔드 주소 풀: 요청을 전달할 내부 서버 그룹을 정의
backend_address_pool {
name = local.backend_address_pool_name
}
# 백엔드 HTTP 설정: 백엔드 서버와 통신할 때 사용할 프로토콜, 포트, 타임아웃 등을 정의
backend_http_settings {
name = local.http_setting_name
cookie_based_affinity = "Disabled"
port = 80
protocol = "Http"
request_timeout = 20
}
# HTTP 리스너: 클라이언트 요청을 수신할 IP와 포트, 프로토콜을 정의
http_listener {
name = local.listener_name
frontend_ip_configuration_name = local.frontend_ip_configuration_name
frontend_port_name = local.frontend_port_name
protocol = "Http"
}
# 요청 라우팅 규칙: 들어오는 요청을 백엔드 풀 및 HTTP 설정으로 라우팅
request_routing_rule {
name = local.request_routing_rule_name
priority = 1
rule_type = "Basic"
http_listener_name = local.listener_name
backend_http_settings_name = "appGatewayBackendHttpSettings" # 백엔드 HTTP 설정 이름 (위에서 정의한 값과 일치해야 함)
backend_address_pool_name = "appGatewayBackendPool" # 백엔드 주소 풀 이름 (위에서 정의한 값과 일치해야 함)
}
}
11_appgwasso.tf
# Application Gateway 백엔드 풀과 NIC 연관
# - WEB1 서버의 네트워크 인터페이스를 Application Gateway의 백엔드 풀에 연결
resource "azurerm_network_interface_application_gateway_backend_address_pool_association" "main_web1_nic_appgw" {
network_interface_id = azurerm_network_interface.main_web1_nic.id
backend_address_pool_id = tolist(azurerm_application_gateway.main_appgw.backend_address_pool)[0].id
# Application Gateway 백엔드 풀 ID (리스트 변환 후 첫번째 요소 선택)
ip_configuration_name = "main-web1-ipconfig"
}
# Application Gateway 백엔드 풀과 NIC 연관
# - WEB2 서버의 네트워크 인터페이스를 Application Gateway의 백엔드 풀에 연결
resource "azurerm_network_interface_application_gateway_backend_address_pool_association" "main_web2_nic_appgw" {
network_interface_id = azurerm_network_interface.main_web2_nic.id
backend_address_pool_id = tolist(azurerm_application_gateway.main_appgw.backend_address_pool)[0].id
# Application Gateway 백엔드 풀 ID
ip_configuration_name = "main-web2-ipconfig"
}
12_vm.tf
# Linux Virtual Machine: BAT 서버
# - 배치 작업이나 백엔드 서비스용으로 사용되는 Linux VM
resource "azurerm_linux_virtual_machine" "main_bat" {
name = "main-bat"
location = azurerm_resource_group.main_rg.location
resource_group_name = azurerm_resource_group.main_rg.name
size = "Standard_F1s"
admin_username = "main"
network_interface_ids = [azurerm_network_interface.main_bat_nic.id] # 연결된 NIC 목록
# SSH 접속을 위한 공개키 인증 설정
admin_ssh_key {
username = "main" # SSH 접속 사용자 이름
public_key = file("main.pub") # 공개키 파일 경로
}
# VM 부팅 시 실행할 사용자 데이터 스크립트 (Base64 인코딩)
user_data = base64encode(file("key.sh"))
# OS 디스크 구성
os_disk {
caching = "ReadWrite" # 캐싱 모드 설정
storage_account_type = "StandardSSD_LRS" # 스토리지 계정 종류
}
# VM에 사용될 소스 이미지 정보 (Rocky Linux 이미지)
source_image_reference {
publisher = "resf" # 이미지 퍼블리셔
offer = "rockylinux-x86_64" # 이미지 오퍼
sku = "9-lvm" # 이미지 SKU
version = "9.3.20231113" # 이미지 버전
}
# 이미지 사용을 위한 플랜 정보 (Marketplace 이미지인 경우 필요)
plan {
publisher = "resf"
product = "rockylinux-x86_64"
name = "9-lvm"
}
# 부팅 진단 (여기서는 사용하지 않음)
boot_diagnostics {
storage_account_uri = null
}
}
# Linux Virtual Machine: WEB1 서버
# - 웹 서비스를 제공하기 위한 Linux VM (WEB1)
resource "azurerm_linux_virtual_machine" "main_web1" {
name = "main-web1"
location = azurerm_resource_group.main_rg.location
resource_group_name = azurerm_resource_group.main_rg.name
size = "Standard_F1s"
admin_username = "main"
network_interface_ids = [azurerm_network_interface.main_web1_nic.id]
# SSH 공개키 인증 설정
admin_ssh_key {
username = "main"
public_key = file("main.pub")
}
# 초기화 스크립트 (Base64 인코딩)
user_data = base64encode(file("main-ins1.sh"))
# OS 디스크 구성
os_disk {
caching = "ReadWrite"
storage_account_type = "StandardSSD_LRS"
}
# 사용될 소스 이미지 정보
source_image_reference {
publisher = "resf"
offer = "rockylinux-x86_64"
sku = "9-lvm"
version = "9.3.20231113"
}
# 이미지 플랜 정보
plan {
publisher = "resf"
product = "rockylinux-x86_64"
name = "9-lvm"
}
# 부팅 진단 설정 (비활성화)
boot_diagnostics {
storage_account_uri = null
}
# 의존성: WEB1 VM 생성 전에 NAT Gateway 연관이 완료되어야 함
depends_on = [ azurerm_subnet_nat_gateway_association.main_sub_nat_assoc1 ]
}
# Linux Virtual Machine: WEB2 서버
# - 추가 웹 서비스를 제공하기 위한 Linux VM (WEB2)
resource "azurerm_linux_virtual_machine" "main_web2" {
name = "main-web2"
location = azurerm_resource_group.main_rg.location
resource_group_name = azurerm_resource_group.main_rg.name
size = "Standard_F1s"
admin_username = "main"
network_interface_ids = [azurerm_network_interface.main_web2_nic.id]
# SSH 공개키 인증 설정
admin_ssh_key {
username = "main"
public_key = file("main.pub")
}
# 초기화 스크립트 (Base64 인코딩)
user_data = base64encode(file("main-ins2.sh"))
# OS 디스크 구성
os_disk {
caching = "ReadWrite"
storage_account_type = "StandardSSD_LRS"
}
# 소스 이미지 정보
source_image_reference {
publisher = "resf"
offer = "rockylinux-x86_64"
sku = "9-lvm"
version = "9.3.20231113"
}
# 이미지 플랜 정보
plan {
publisher = "resf"
product = "rockylinux-x86_64"
name = "9-lvm"
}
# 부팅 진단 설정 (비활성화)
boot_diagnostics {
storage_account_uri = null
}
}
13_gallery.tf
# Managed Image 생성: WEB1 이미지를 기반으로 생성
# - 이후 이미지 갤러리 등에 사용하기 위한 관리 이미지
resource "azurerm_image" "main_web1_image" {
name = "main-web1-image"
location = azurerm_resource_group.main_rg.location
resource_group_name = azurerm_resource_group.main_rg.name
hyper_v_generation = "V2" # Hyper-V 세대
source_virtual_machine_id = azurerm_linux_virtual_machine.main_web1.id # 원본 VM (WEB1) ID
# OS 디스크 설정 정보
os_disk {
size_gb = 10
os_type = "Linux"
os_state = "Specialized"
caching = "ReadWrite"
storage_type = "StandardSSD_LRS"
}
# 의존성: WEB1 VM의 일반화(generalize) 과정 완료 후 이미지 생성
depends_on = [ null_resource.Generalize_web1_delay2 ]
}
# Shared Image Gallery 생성
# - 여러 이미지 버전을 저장하고 관리할 수 있는 이미지 갤러리 생성
resource "azurerm_shared_image_gallery" "main_gallery" {
name = "maingallery"
location = azurerm_resource_group.main_rg.location
resource_group_name = azurerm_resource_group.main_rg.name
}
# Shared Image 생성
# - 갤러리 내에 사용할 공유 이미지 정의
resource "azurerm_shared_image" "main_rocky" {
name = "main-rocky"
gallery_name = azurerm_shared_image_gallery.main_gallery.name
resource_group_name = azurerm_resource_group.main_rg.name
location = azurerm_resource_group.main_rg.location
os_type = "Linux"
hyper_v_generation = "V2"
architecture = "x64"
min_recommended_vcpu_count = 1
max_recommended_vcpu_count = 1
min_recommended_memory_in_gb = 1
max_recommended_memory_in_gb = 2
# 이미지 식별자: 원본 이미지의 퍼블리셔, 오퍼, SKU 정보를 지정
identifier {
publisher = "resf"
offer = "rockylinux-x86_64"
sku = "9-lvm"
}
}
# 데이터 소스: 관리 이미지 조회
# - 이전에 생성한 WEB1 이미지를 조회합니다.
data "azurerm_image" "cont" {
name = "main-web1-image" # 조회할 이미지 이름
resource_group_name = azurerm_resource_group.main_rg.name # 해당 이미지가 속한 리소스 그룹
# 의존성: 이미지가 생성된 후에 데이터 조회
depends_on = [ azurerm_image.main_web1_image ]
}
# Shared Image Version 생성
# - 공유 이미지에 버전 정보를 부여하여 관리
resource "azurerm_shared_image_version" "main-image-version" {
name = "1.0.0"
gallery_name = azurerm_shared_image_gallery.main_gallery.name
resource_group_name = azurerm_resource_group.main_rg.name
location = azurerm_resource_group.main_rg.location
image_name = azurerm_shared_image.main_rocky.name
managed_image_id = data.azurerm_image.cont.id
# 타깃 지역 설정: 이미지 버전을 복제할 대상 지역 지정
target_region {
name = "koreacentral"
regional_replica_count = 1
storage_account_type = "Standard_LRS"
}
# 의존성: 관리 이미지 생성 후에 공유 이미지 버전 생성
depends_on = [azurerm_image.main_web1_image]
}
14_as.tf
# Monitor Autoscale Setting 설정
# - Virtual Machine Scale Set(VMSS)의 자동 확장 정책 설정
resource "azurerm_monitor_autoscale_setting" "main_as" {
name = "main-as"
resource_group_name = azurerm_resource_group.main_rg.name
location = azurerm_resource_group.main_rg.location
target_resource_id = azurerm_linux_virtual_machine_scale_set.main_vmss.id
enabled = true # 자동 확장 활성화
profile {
name = "main-as-profile"
# 용량 설정: 기본, 최소, 최대 인스턴스 수 정의
capacity {
default = 1 # 기본 인스턴스 수
minimum = 1 # 최소 인스턴스 수
maximum = 6 # 최대 인스턴스 수
}
# 스케일 아웃(인스턴스 증가) 규칙: CPU 사용률이 특정 임계치를 초과할 경우
rule {
metric_trigger {
metric_name = "Percentage CPU" # 모니터링할 메트릭
metric_resource_id = azurerm_linux_virtual_machine_scale_set.main_vmss.id # 메트릭이 적용될 VMSS ID
time_grain = "PT1M" # 데이터 측정 간격 (1분)
statistic = "Average" # 평균 값 사용
time_window = "PT5M"
time_aggregation = "Average"
operator = "GreaterThan"
threshold = 70
}
scale_action {
direction = "Increase" # 인스턴스 증가
type = "ChangeCount" # 인스턴스 수 변경 방식
value = "1" # 1개씩 증가
cooldown = "PT5M" # 확장 후 대기 시간 (5분)
}
}
# 스케일 인(인스턴스 감소) 규칙: CPU 사용률이 특정 임계치보다 낮을 경우
rule {
metric_trigger {
metric_name = "Percentage CPU"
metric_resource_id = azurerm_linux_virtual_machine_scale_set.main_vmss.id
time_grain = "PT1M"
statistic = "Average"
time_window = "PT5M"
time_aggregation = "Average"
operator = "LessThan"
threshold = 20
}
scale_action {
direction = "Decrease"
type = "ChangeCount"
value = "1"
cooldown = "PT5M"
}
}
}
}
15_vmss.tf
# Linux Virtual Machine Scale Set 구성
# - 여러 인스턴스로 구성된 Linux VM Scale Set을 생성합니다.
resource "azurerm_linux_virtual_machine_scale_set" "main_vmss" {
name = "main-vmss"
resource_group_name = azurerm_resource_group.main_rg.name
location = azurerm_resource_group.main_rg.location
instances = 1
# 이미지 소스 ID: 공유 이미지 버전 리소스의 ID 사용
source_image_id = azurerm_shared_image_version.main-image-version.id
admin_username = "main"
sku = "Standard_F1s"
# 업그레이드 모드 (수동으로 업그레이드)
upgrade_mode = "Manual"
# VM 인스턴스의 우선순위 (일반)
priority = "Regular"
# Plan 설정 (Marketplace 이미지인 경우 필요)
plan {
publisher = "resf"
product = "rockylinux-x86_64"
name = "9-lvm"
}
# SSH 접속 설정: 관리자가 SSH로 접속할 수 있도록 공개키 인증 구성
admin_ssh_key {
username = "main"
public_key = file("main.pub")
}
# OS 디스크 설정: 운영체제 디스크의 캐싱 및 스토리지 종류 설정
os_disk {
caching = "ReadWrite"
storage_account_type = "StandardSSD_LRS"
}
# 네트워크 인터페이스 설정: VMSS 인스턴스의 네트워크 연결 구성
network_interface {
name = "main-vmss-nic"
primary = true
network_security_group_id = azurerm_network_security_group.main_web_nsg.id
ip_configuration {
name = "main-vmss-ipconfig"
subnet_id = azurerm_subnet.main_web1.id
# Application Gateway의 백엔드 풀과 연관할 NIC의 IP 구성을 설정
application_gateway_backend_address_pool_ids = [
for pool in azurerm_application_gateway.main_appgw.backend_address_pool : pool.id
]
}
}
# 부팅 진단 설정: 부팅 진단 기능 설정
boot_diagnostics {
storage_account_uri = null
}
}
19_null.tf
# Generalize_web1_vm: 초기 대기 작업
# - main-web1 VM의 생성 완료 후 일정 시간(약 120초) 대기하여,
# 이후 일반화 작업이 안정적으로 진행될 수 있도록 합니다.
resource "null_resource" "Generalize_web1_vm" {
provisioner "local-exec" {
command = "ping 127.0.0.1 -n 121 > nul" # 120초 대기 (ping 121회)
}
depends_on = [azurerm_linux_virtual_machine.main_web1]
}
# Generalize_web1_1: VM 할당 해제
# - main-web1 VM을 deallocate 하여 일반화(generalize) 작업을 위한 준비를 합니다.
resource "null_resource" "Generalize_web1_1" {
provisioner "local-exec" {
command = "az vm deallocate --resource-group 02-main-rg --name main-web1"
}
depends_on = [null_resource.Generalize_web1_vm]
}
# Generalize_web1_delay: 첫 번째 대기 작업
# - deallocate 명령 실행 후 충분한 시간이 지나도록 약 30초 대기합니다.
resource "null_resource" "Generalize_web1_delay" {
provisioner "local-exec" {
command = "ping 127.0.0.1 -n 31 > nul" # 약 30초 대기
}
depends_on = [null_resource.Generalize_web1_1]
}
# Generalize_web1_2: VM 일반화 작업 실행
# - main-web1 VM에 대해 일반화(generalize) 명령을 실행합니다.
resource "null_resource" "Generalize_web1_2" {
provisioner "local-exec" {
command = "az vm generalize --resource-group 02-main-rg --name main-web1"
}
depends_on = [null_resource.Generalize_web1_delay]
}
# Generalize_web1_delay2: 두 번째 대기 작업
# - 일반화 작업 후 추가 안정화를 위해 약 30초 대기합니다.
resource "null_resource" "Generalize_web1_delay2" {
provisioner "local-exec" {
command = "ping 127.0.0.1 -n 31 > nul" # 약 30초 대기
}
depends_on = [null_resource.Generalize_web1_delay]
}
부록 ins.sh
#! /bin/bash
# SELinux의 즉시 적용 모드를 비활성화 (임시)
setenforce 0
# 모든 커널에 대해 SELinux를 영구적으로 비활성화 (부팅 시에도 적용)
grubby --update-kernel=ALL --args selinux=0
dnf install -y wget tar httpd php php-gd php-curl php-mysqlnd
wget https://ko.wordpress.org/wordpress-6.7.2-ko_KR.tar.gz
tar xvfz wordpress-6.7.2-ko_KR.tar.gz -C /root/
cp -ar /root/wordpress/* /var/www/html/
sed -i "s/DirectoryIndex index.html/DirectoryIndex index.php/g" /etc/httpd/conf/httpd.conf
cp /var/www/html/{wp-config-sample.php,wp-config.php}
sed -i "s/database_name_here/wordpress/g" /var/www/html/wp-config.php
sed -i "s/username_here/main/g" /var/www/html/wp-config.php
sed -i "s/localhost/10.0.5.4/g" /var/www/html/wp-config.php
sed -i "s/password_here/It12345\!/g" /var/www/html/wp-config.php
# base64 인코딩된 문자열을 디코딩하여 간단한 헬스 체크 페이지(health.html)를 생성
echo -n 'PGh0bWw+PGJvZHk+PGgxPmhlYWx0aC10ZXN0cGFnZS0xPC9oMT48L2JvZHk+PC9odG1sPg==' | base64 -d > /var/www/html/health.html
chown -R apache.apache /var/www/
systemctl enable --now httpd
반응형