빠른 시작: Terraform을 사용하여 Azure에서 Linux VM 클러스터 만들기
적용 대상: ✔️ Linux VM
이 문서에서는 Terraform을 사용하여 Azure에서 Linux VM 클러스터(두 개의 Linux VM 인스턴스 포함)를 만드는 방법을 보여 줍니다.
이 문서에서는 다음 방법을 설명합니다.
- random_pet을 사용하여 Azure 리소스 그룹 이름에 대한 임의 값 만들기.
- azurerm_resource_group을 사용하여 Azure 리소스 그룹 만들기.
- azurerm_virtual_network를 사용하여 가상 네트워크 만들기
- azurerm_subnet을 사용하여 서브넷 만들기
- azurerm_public_ip를 사용하여 공용 IP 만들기
- azurerm_lb를 사용하여 부하 분산 장치 만들기
- azurerm_lb_backend_address_pool을 사용하여 부하 분산 장치 주소 풀 만들기
- azurerm_network_interface를 사용하여 네트워크 인터페이스 만들기
- azurerm_managed_disk를 사용하여 관리 디스크 만들기
- azurerm_availability_set을 사용하여 가용성 집합 만들기
- azurerm_linux_virtual_machine을 사용하여 Linux 가상 머신 만들기
- AzAPI 리소스 azapi_resource 만들기.
- azapi_resource_action을 사용하여 SSH 키 쌍을 생성하기 위해 AzAPI 리소스 만들기.
필수 조건
Terraform 코드 구현
참고 항목
이 문서의 샘플 코드는 Azure Terraform GitHub 리포지토리에 있습니다. Terraform의 현재 및 이전 버전의 테스트 결과가 포함된 로그 파일을 볼 수 있습니다.
Terraform을 사용하여 Azure 리소스를 관리하는 방법을 보여 주는 추가 문서 및 샘플 코드를 참조하세요.
샘플 Terraform 코드를 테스트할 디렉터리를 만들고, 이를 현재 디렉터리로 만듭니다.
providers.tf
라는 파일을 만들고 다음 코드를 삽입합니다.terraform { required_version = ">=1.0" required_providers { azapi = { source = "azure/azapi" version = "~>1.5" } azurerm = { source = "hashicorp/azurerm" version = "~>3.0" } random = { source = "hashicorp/random" version = "~>3.0" } } } provider "azurerm" { features {} }
ssh.tf
라는 파일을 만들고 다음 코드를 삽입합니다.resource "random_pet" "ssh_key_name" { prefix = "ssh" separator = "" } resource "azapi_resource_action" "ssh_public_key_gen" { type = "Microsoft.Compute/sshPublicKeys@2022-11-01" resource_id = azapi_resource.ssh_public_key.id action = "generateKeyPair" method = "POST" response_export_values = ["publicKey", "privateKey"] } resource "azapi_resource" "ssh_public_key" { type = "Microsoft.Compute/sshPublicKeys@2022-11-01" name = random_pet.ssh_key_name.id location = azurerm_resource_group.rg.location parent_id = azurerm_resource_group.rg.id } output "key_data" { value = azapi_resource_action.ssh_public_key_gen.output.publicKey }
main.tf
라는 파일을 만들고 다음 코드를 삽입합니다.resource "random_pet" "rg_name" { prefix = var.resource_group_name_prefix } resource "azurerm_resource_group" "rg" { name = random_pet.rg_name.id location = var.resource_group_location } resource "random_pet" "azurerm_virtual_network_name" { prefix = "vnet" } resource "azurerm_virtual_network" "test" { name = random_pet.azurerm_virtual_network_name.id address_space = ["10.0.0.0/16"] location = azurerm_resource_group.rg.location resource_group_name = azurerm_resource_group.rg.name } resource "random_pet" "azurerm_subnet_name" { prefix = "sub" } resource "azurerm_subnet" "test" { name = random_pet.azurerm_subnet_name.id resource_group_name = azurerm_resource_group.rg.name virtual_network_name = azurerm_virtual_network.test.name address_prefixes = ["10.0.2.0/24"] } resource "azurerm_public_ip" "test" { name = "publicIPForLB" location = azurerm_resource_group.rg.location resource_group_name = azurerm_resource_group.rg.name allocation_method = "Static" } resource "azurerm_lb" "test" { name = "loadBalancer" location = azurerm_resource_group.rg.location resource_group_name = azurerm_resource_group.rg.name frontend_ip_configuration { name = "publicIPAddress" public_ip_address_id = azurerm_public_ip.test.id } } resource "azurerm_lb_backend_address_pool" "test" { loadbalancer_id = azurerm_lb.test.id name = "BackEndAddressPool" } resource "azurerm_network_interface" "test" { count = 2 name = "acctni${count.index}" location = azurerm_resource_group.rg.location resource_group_name = azurerm_resource_group.rg.name ip_configuration { name = "testConfiguration" subnet_id = azurerm_subnet.test.id private_ip_address_allocation = "Dynamic" } } resource "azurerm_availability_set" "avset" { name = "avset" location = azurerm_resource_group.rg.location resource_group_name = azurerm_resource_group.rg.name platform_fault_domain_count = 2 platform_update_domain_count = 2 managed = true } resource "random_pet" "azurerm_linux_virtual_machine_name" { prefix = "vm" } resource "azurerm_linux_virtual_machine" "test" { count = 2 name = "${random_pet.azurerm_linux_virtual_machine_name.id}${count.index}" location = azurerm_resource_group.rg.location availability_set_id = azurerm_availability_set.avset.id resource_group_name = azurerm_resource_group.rg.name network_interface_ids = [azurerm_network_interface.test[count.index].id] size = "Standard_DS1_v2" # Uncomment this line to delete the OS disk automatically when deleting the VM # delete_os_disk_on_termination = true # Uncomment this line to delete the data disks automatically when deleting the VM # delete_data_disks_on_termination = true source_image_reference { publisher = "Canonical" offer = "UbuntuServer" sku = "16.04-LTS" version = "latest" } admin_ssh_key { username = var.username public_key = azapi_resource_action.ssh_public_key_gen.output.publicKey } os_disk { caching = "ReadWrite" storage_account_type = "Standard_LRS" name = "myosdisk${count.index}" } computer_name = "hostname" admin_username = var.username } resource "azurerm_managed_disk" "test" { count = 2 name = "datadisk_existing_${count.index}" location = azurerm_resource_group.rg.location resource_group_name = azurerm_resource_group.rg.name storage_account_type = "Standard_LRS" create_option = "Empty" disk_size_gb = "1024" } resource "azurerm_virtual_machine_data_disk_attachment" "test" { count = 2 managed_disk_id = azurerm_managed_disk.test[count.index].id virtual_machine_id = azurerm_linux_virtual_machine.test[count.index].id lun = "10" caching = "ReadWrite" }
variables.tf
라는 파일을 만들고 다음 코드를 삽입합니다.variable "resource_group_location" { type = string description = "Location for all resources." default = "eastus" } variable "resource_group_name_prefix" { type = string description = "Prefix of the resource group name that's combined with a random ID so name is unique in your Azure subscription." default = "rg" } variable "username" { type = string description = "The username for the local account that will be created on the new VM." default = "azureadmin" }
outputs.tf
라는 파일을 만들고 다음 코드를 삽입합니다.output "resource_group_name" { value = azurerm_resource_group.rg.name } output "virtual_network_name" { value = azurerm_virtual_network.test.name } output "subnet_name" { value = azurerm_subnet.test.name } output "linux_virtual_machine_names" { value = [for s in azurerm_linux_virtual_machine.test : s.name[*]] }
Terraform 초기화
terraform init를 실행하여 Terraform 배포를 초기화합니다. 이 명령은 Azure 리소스를 관리하는 데 필요한 Azure 공급자를 다운로드합니다.
terraform init -upgrade
주요 정보:
-upgrade
매개 변수는 필요한 공급자 플러그 인을 구성의 버전 제약 조건을 준수하는 최신 버전으로 업그레이드합니다.
Terraform 실행 계획 만들기
terraform plan을 실행하여 실행 계획을 만듭니다.
terraform plan -out main.tfplan
주요 정보:
terraform plan
명령은 실행 계획을 만들지만 실행하지는 않습니다. 대신 구성 파일에 지정된 구성을 만드는 데 필요한 작업을 결정합니다. 이 패턴을 사용하면 실제 리소스를 변경하기 전에 실행 계획이 예상과 일치하는지 확인할 수 있습니다.- 선택 사항인
-out
매개 변수를 사용하여 계획의 출력 파일을 지정할 수 있습니다.-out
매개 변수를 사용하면 검토한 계획이 정확하게 적용됩니다.
Terraform 실행 계획 적용
terraform apply를 실행하여 실행 계획을 클라우드 인프라에 적용합니다.
terraform apply main.tfplan
주요 정보:
- 예시
terraform apply
명령은 이전에terraform plan -out main.tfplan
를 실행했다고 가정합니다. -out
매개 변수에 다른 파일 이름을 지정한 경우terraform apply
에 대한 호출에서 동일한 파일 이름을 사용합니다.-out
매개 변수를 사용하지 않은 경우 매개 변수 없이terraform apply
를 호출합니다.
Azure Portal과 마찬가지로 Terraform의 가상 머신 만들기 프로세스 중에는 비용 정보가 표시되지 않습니다. 가상 머신의 비용 계산 방식에 대해 자세히 알아보려면 비용 최적화 개요 페이지를 참조하세요.
결과 확인
Azure 리소스 그룹 이름을 가져옵니다.
resource_group_name=$(terraform output -raw resource_group_name)
JMESPath 쿼리와 함께 az vm list를 실행하여 리소스 그룹에 생성된 가상 머신의 이름을 표시합니다.
az vm list \ --resource-group $resource_group_name \ --query "[].{\"VM Name\":name}" -o table
리소스 정리
Terraform을 통해 리소스를 만들 필요가 더 이상 없으면 다음 단계를 수행합니다.
terraform 플랜을 실행하고
destroy
플래그를 지정합니다.terraform plan -destroy -out main.destroy.tfplan
주요 정보:
terraform plan
명령은 실행 계획을 만들지만 실행하지는 않습니다. 대신 구성 파일에 지정된 구성을 만드는 데 필요한 작업을 결정합니다. 이 패턴을 사용하면 실제 리소스를 변경하기 전에 실행 계획이 예상과 일치하는지 확인할 수 있습니다.- 선택 사항인
-out
매개 변수를 사용하여 계획의 출력 파일을 지정할 수 있습니다.-out
매개 변수를 사용하면 검토한 계획이 정확하게 적용됩니다.
terraform apply를 실행하여 실행 계획을 적용합니다.
terraform apply main.destroy.tfplan
Azure의 Terraform 문제 해결
Azure에서 Terraform을 사용할 때 일반적인 문제 해결