Vytvoření škálovací sady virtuálních počítačů Azure z vlastní image Packeru pomocí Terraformu

Terraform umožňuje definici, verzi Preview a nasazení cloudové infrastruktury. Pomocí Terraformu vytvoříte konfigurační soubory pomocí syntaxe HCL. Syntaxe seznamu HCL umožňuje zadat poskytovatele cloudu , například Azure, a prvky, které tvoří vaši cloudovou infrastrukturu. Po vytvoření konfiguračních souborů vytvoříte plán provádění, který vám umožní zobrazit náhled změn infrastruktury před jejich nasazením. Jakmile ověříte změny, použijete plán provádění k nasazení infrastruktury.

Škálovací sady virtuálních počítačů Azure umožňují konfigurovat stejné virtuální počítače. Počet instancí virtuálních počítačů se může upravit na základě poptávky nebo plánu. Další informace najdete v tématu Automatické škálování škálovací sady virtuálních počítačů na webu Azure Portal.

V tomto článku získáte informace o těchto tématech:

  • Nastavit nasazení Terraformu
  • Použít proměnné a výstupy u nasazení Terraformu
  • Vytvořit a nasadit síťovou infrastrukturu
  • Vytvoření vlastní image virtuálního počítače pomocí Packeru
  • Vytvoření a nasazení škálovací sady virtuálních počítačů pomocí vlastní image
  • Vytvořit a nasadit jumpbox

1. Konfigurace prostředí

  • Předplatné Azure: Pokud ještě nemáte předplatné Azure, vytvořte si bezplatný účet před tím, než začnete.

2. Vytvoření image Packeru

  1. Nainstalujte packer.

    Klíčové body:

    • Pokud chcete ověřit, že máte přístup ke spustitelnému souboru Packeru, spusťte následující příkaz: packer -v.
    • V závislosti na vašem prostředí možná budete muset nastavit cestu a znovu otevřít příkazový řádek.
  2. Spuštěním příkazu az group create vytvořte skupinu prostředků pro uložení image Packeru.

    az group create -n myPackerImages -l eastus
    
  3. Spuštěním příkazu az ad sp create-for-rbac povolte Packer ověřování v Azure pomocí instančního objektu.

    az ad sp create-for-rbac --role Contributor --scopes /subscriptions/<subscription_id> --query "{ client_id: appId, client_secret: password, tenant_id: tenant }"
    

    Klíčové body:

    • Poznamenejte si výstupní hodnoty (appId, client_secret, tenant_id).
  4. Spuštěním příkazu az account show zobrazte aktuální předplatné Azure.

    az account show --query "{ subscription_id: id }"
    
  5. Vytvořte soubor proměnných šablony Packeru s názvem ubuntu.pkr.hcl a vložte následující kód. Aktualizujte zvýrazněné řádky pomocí informací o instančním objektu a předplatném Azure.

    packer {
      required_plugins {
        azure = {
          source  = "github.com/hashicorp/azure"
          version = "~> 2"
        }
      }
    }
    
    variable client_id {
      type    = string
      default = null
    }
    variable client_secret {
      type    = string
      default = null
    }
    
    variable subscription_id {
      type    = string
      default = null
    }
    
    variable tenant_id {
      type    = string
      default = null
    }
    
    variable location {
      default = "eastus"
    }
    
    variable "image_resource_group_name" {
      description = "Name of the resource group in which the Packer image will be created"
      default     = "myPackerImages"
    }
    
    variable "oidc_request_url" {
      default = null
    }
    
    variable "oidc_request_token" {
      default = null
    }
    
    # arm builder
    source "azure-arm" "builder" {
      client_id                         = var.client_id
      client_secret                     = var.client_secret
      image_offer                       = "UbuntuServer"
      image_publisher                   = "canonical"
      image_sku                         = "16.04-LTS"
      location                          = var.location
      managed_image_name                = "myPackerImage"
      managed_image_resource_group_name = var.image_resource_group_name
      os_type                           = "Linux"
      subscription_id                   = var.subscription_id
      tenant_id                         = var.tenant_id
      oidc_request_url                  = var.oidc_request_url
      oidc_request_token                = var.oidc_request_token
      vm_size                           = "Standard_DS2_v2"
      azure_tags = {
        "dept" : "Engineering",
        "task" : "Image deployment",
      }
    }
    
    build {
      sources = ["source.azure-arm.builder"]
      provisioner "shell" {
        execute_command = "chmod +x {{ .Path }}; {{ .Vars }} sudo -E sh '{{ .Path }}'"
        inline = [
          "apt-get update",
          "apt-get upgrade -y",
          "apt-get -y install nginx",
          "/usr/sbin/waagent -force -deprovision+user && export HISTSIZE=0 && sync",
        ]
      }
    }
    

    Klíčové body:

    • Nastavte hodnotu client_ida client_secrettenant_id pole na odpovídající hodnoty z vašeho instančního objektu.
    • subscription_id Nastavte pole na ID předplatného Azure.
  6. Sestavte image Packeru.

    packer build ubuntu.json
    

3. Implementace kódu Terraformu

  1. Vytvořte adresář, ve kterém otestujete ukázkový kód Terraformu a nastavíte ho jako aktuální adresář.

  2. Vytvořte soubor s názvem main.tf a vložte následující kód:

    terraform {
    
      required_version = ">=0.12"
    
      required_providers {
        azurerm = {
          source  = "hashicorp/azurerm"
          version = "~>3.0"
        }
        azapi = {
          source  = "Azure/azapi"
          version = "~> 1.0"
        }
        local = {
          source  = "hashicorp/local"
          version = "2.4.0"
        }
        random = {
          source  = "hashicorp/random"
          version = "3.5.1"
        }
        tls = {
          source  = "hashicorp/tls"
          version = "4.0.4"
        }
      }
    }
    
    provider "azurerm" {
      features {
        resource_group {
          prevent_deletion_if_contains_resources = false
        }
      }
    }
    
    resource "random_pet" "id" {}
    
    resource "azurerm_resource_group" "vmss" {
      name     = coalesce(var.resource_group_name, "201-vmss-packer-jumpbox-${random_pet.id.id}")
      location = var.location
      tags     = var.tags
    }
    
    resource "random_string" "fqdn" {
      length  = 6
      special = false
      upper   = false
      numeric = false
    }
    
    resource "azurerm_virtual_network" "vmss" {
      name                = "vmss-vnet"
      address_space       = ["10.0.0.0/16"]
      location            = var.location
      resource_group_name = azurerm_resource_group.vmss.name
      tags                = var.tags
    }
    
    resource "azurerm_subnet" "vmss" {
      name                 = "vmss-subnet"
      resource_group_name  = azurerm_resource_group.vmss.name
      virtual_network_name = azurerm_virtual_network.vmss.name
      address_prefixes     = ["10.0.2.0/24"]
    }
    
    resource "azurerm_public_ip" "vmss" {
      name                = "vmss-public-ip"
      location            = var.location
      resource_group_name = azurerm_resource_group.vmss.name
      allocation_method   = "Static"
      domain_name_label   = random_string.fqdn.result
      tags                = var.tags
    }
    
    resource "azurerm_lb" "vmss" {
      name                = "vmss-lb"
      location            = var.location
      resource_group_name = azurerm_resource_group.vmss.name
    
      frontend_ip_configuration {
        name                 = "PublicIPAddress"
        public_ip_address_id = azurerm_public_ip.vmss.id
      }
    
      tags = var.tags
    }
    
    resource "azurerm_lb_backend_address_pool" "bpepool" {
      loadbalancer_id = azurerm_lb.vmss.id
      name            = "BackEndAddressPool"
    }
    
    resource "azurerm_lb_probe" "vmss" {
      loadbalancer_id = azurerm_lb.vmss.id
      name            = "ssh-running-probe"
      port            = var.application_port
    }
    
    resource "azurerm_lb_rule" "lbnatrule" {
      loadbalancer_id                = azurerm_lb.vmss.id
      name                           = "http"
      protocol                       = "Tcp"
      frontend_port                  = var.application_port
      backend_port                   = var.application_port
      backend_address_pool_ids       = [azurerm_lb_backend_address_pool.bpepool.id]
      frontend_ip_configuration_name = "PublicIPAddress"
      probe_id                       = azurerm_lb_probe.vmss.id
    }
    
    data "azurerm_resource_group" "image" {
      name = var.packer_resource_group_name
    }
    
    data "azurerm_image" "image" {
      name                = var.packer_image_name
      resource_group_name = data.azurerm_resource_group.image.name
    }
    
    resource "azapi_resource" "ssh_public_key" {
      type      = "Microsoft.Compute/sshPublicKeys@2022-11-01"
      name      = random_pet.id.id
      location  = azurerm_resource_group.vmss.location
      parent_id = azurerm_resource_group.vmss.id
    }
    
    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 "random_password" "password" {
      count  = var.admin_password == null ? 1 : 0
      length = 20
    }
    
    locals {
      admin_password = try(random_password.password[0].result, var.admin_password)
    }
    
    resource "azurerm_virtual_machine_scale_set" "vmss" {
      name                = "vmscaleset"
      location            = var.location
      resource_group_name = azurerm_resource_group.vmss.name
      upgrade_policy_mode = "Manual"
    
      sku {
        name     = "Standard_DS1_v2"
        tier     = "Standard"
        capacity = 2
      }
    
      storage_profile_image_reference {
        id = data.azurerm_image.image.id
      }
    
      storage_profile_os_disk {
        name              = ""
        caching           = "ReadWrite"
        create_option     = "FromImage"
        managed_disk_type = "Standard_LRS"
      }
    
      storage_profile_data_disk {
        lun           = 0
        caching       = "ReadWrite"
        create_option = "Empty"
        disk_size_gb  = 10
      }
    
      os_profile {
        computer_name_prefix = "vmlab"
        admin_username       = var.admin_user
        admin_password       = local.admin_password
      }
    
      os_profile_linux_config {
        disable_password_authentication = true
    
        ssh_keys {
          path     = "/home/azureuser/.ssh/authorized_keys"
          key_data = azapi_resource_action.ssh_public_key_gen.output.publicKey
        }
      }
    
      network_profile {
        name    = "terraformnetworkprofile"
        primary = true
    
        ip_configuration {
          name                                   = "IPConfiguration"
          subnet_id                              = azurerm_subnet.vmss.id
          load_balancer_backend_address_pool_ids = [azurerm_lb_backend_address_pool.bpepool.id]
          primary                                = true
        }
      }
    
      tags = var.tags
    }
    
    resource "azurerm_public_ip" "jumpbox" {
      name                = "jumpbox-public-ip"
      location            = var.location
      resource_group_name = azurerm_resource_group.vmss.name
      allocation_method   = "Static"
      domain_name_label   = "${random_string.fqdn.result}-ssh"
      tags                = var.tags
    }
    
    resource "azurerm_network_interface" "jumpbox" {
      name                = "jumpbox-nic"
      location            = var.location
      resource_group_name = azurerm_resource_group.vmss.name
    
      ip_configuration {
        name                          = "IPConfiguration"
        subnet_id                     = azurerm_subnet.vmss.id
        private_ip_address_allocation = "Dynamic"
        public_ip_address_id          = azurerm_public_ip.jumpbox.id
      }
    
      tags = var.tags
    }
    
    resource "azurerm_virtual_machine" "jumpbox" {
      name                  = "jumpbox"
      location              = var.location
      resource_group_name   = azurerm_resource_group.vmss.name
      network_interface_ids = [azurerm_network_interface.jumpbox.id]
      vm_size               = "Standard_DS1_v2"
    
      storage_image_reference {
        publisher = "Canonical"
        offer     = "UbuntuServer"
        sku       = "16.04-LTS"
        version   = "latest"
      }
    
      storage_os_disk {
        name              = "jumpbox-osdisk"
        caching           = "ReadWrite"
        create_option     = "FromImage"
        managed_disk_type = "Standard_LRS"
      }
    
      os_profile {
        computer_name  = "jumpbox"
        admin_username = var.admin_user
        admin_password = local.admin_password
      }
    
      os_profile_linux_config {
        disable_password_authentication = true
    
        ssh_keys {
          path     = "/home/azureuser/.ssh/authorized_keys"
          key_data = azapi_resource_action.ssh_public_key_gen.output.publicKey
        }
      }
    
      tags = var.tags
    }
    
  3. Vytvořte soubor s názvem variables.tf , který bude obsahovat proměnné projektu, a vložte následující kód:

    variable "packer_resource_group_name" {
      description = "Name of the resource group in which the Packer image will be created"
      default     = "myPackerImages"
    }
    
    variable "packer_image_name" {
      description = "Name of the Packer image"
      default     = "myPackerImage"
    }
    
    variable "resource_group_name" {
      description = "Name of the resource group in which the Packer image  will be created"
      default     = null
    }
    
    variable "location" {
      default     = "eastus"
      description = "Location where resources will be created"
    }
    
    variable "tags" {
      description = "Map of the tags to use for the resources that are deployed"
      type        = map(string)
      default = {
        environment = "codelab"
      }
    }
    
    variable "application_port" {
      description = "Port that you want to expose to the external load balancer"
      default     = 80
    }
    
    variable "admin_user" {
      description = "User name to use as the admin account on the VMs that will be part of the VM scale set"
      default     = "azureuser"
    }
    
    variable "admin_password" {
      description = "Default password for admin account"
      default     = null
    }
    
  4. Vytvořte soubor s názvem output.tf , který určuje, jaké hodnoty Terraform zobrazí, a vložte následující kód:

    output "vmss_public_ip_fqdn" {
      value = azurerm_public_ip.vmss.fqdn
    }
    
    output "jumpbox_public_ip_fqdn" {
      value = azurerm_public_ip.jumpbox.fqdn
    }
    
    output "jumpbox_public_ip" {
      value = azurerm_public_ip.jumpbox.ip_address
    }
    

4. Inicializace Terraformu

Spuštěním inicializace nasazení Terraformu spusťte inicializaci terraformu. Tento příkaz stáhne poskytovatele Azure potřebného ke správě prostředků Azure.

terraform init -upgrade

Klíčové body:

  • Parametr -upgrade upgraduje potřebné moduly plug-in zprostředkovatele na nejnovější verzi, která splňuje omezení verzí konfigurace.

5. Vytvoření plánu provádění Terraformu

Spuštěním plánu terraformu vytvořte plán provádění.

terraform plan -out main.tfplan

Klíčové body:

  • Příkaz terraform plan vytvoří plán provádění, ale nespustí ho. Místo toho určuje, jaké akce jsou nezbytné k vytvoření konfigurace zadané v konfiguračních souborech. Tento model umožňuje ověřit, jestli plán provádění odpovídá vašim očekáváním, než provede jakékoli změny skutečných prostředků.
  • Volitelný -out parametr umožňuje zadat výstupní soubor pro plán. Použití parametru -out zajišťuje, že plán, který jste zkontrolovali, je přesně to, co se použije.

6. Použití plánu provádění Terraformu

Spuštění terraformu platí pro použití plánu provádění na cloudovou infrastrukturu.

terraform apply main.tfplan

Klíčové body:

  • terraform apply Ukázkový příkaz předpokládá, že jste dříve spustili terraform plan -out main.tfplan.
  • Pokud jste pro -out parametr zadali jiný název souboru, použijte stejný název souboru při volání terraform apply.
  • Pokud jste parametr nepoužíli -out , zavolejte terraform apply bez parametrů.

7. Ověření výsledků

  1. Ve výstupu terraform apply příkazu se zobrazí následující hodnoty:

    • Plně kvalifikovaný název domény virtuálního počítače
    • Plně kvalifikovaný název domény jumpboxu
    • IP adresa jumpboxu
  2. Přejděte na adresu URL virtuálního počítače a potvrďte výchozí stránku s textem Vítá vás nginx!.

  3. Pomocí SSH se připojte k virtuálnímu počítači jumpbox pomocí uživatelského jména definovaného v souboru proměnných a hesla, které jste zadali při spuštění terraform apply. Například: ssh azureuser@<ip_address>.

8. Vyčištění prostředků

Odstranění škálovací sady virtuálních počítačů

Pokud už prostředky vytvořené přes Terraform nepotřebujete, proveďte následující kroky:

  1. Spusťte plán terraformu destroy a zadejte příznak.

    terraform plan -destroy -out main.destroy.tfplan
    

    Klíčové body:

    • Příkaz terraform plan vytvoří plán provádění, ale nespustí ho. Místo toho určuje, jaké akce jsou nezbytné k vytvoření konfigurace zadané v konfiguračních souborech. Tento model umožňuje ověřit, jestli plán provádění odpovídá vašim očekáváním, než provede jakékoli změny skutečných prostředků.
    • Volitelný -out parametr umožňuje zadat výstupní soubor pro plán. Použití parametru -out zajišťuje, že plán, který jste zkontrolovali, je přesně to, co se použije.
  2. Spuštění terraformu platí pro použití plánu provádění.

    terraform apply main.destroy.tfplan
    

Odstranění image Packeru a skupiny prostředků

Spuštěním příkazu az group delete odstraňte skupinu prostředků použitou k zahrnutí image Packeru. Image Packeru je také odstraněna.

az group delete --name myPackerImages --yes

Řešení potíží s Terraformem v Azure

Řešení běžných problémů při používání Terraformu v Azure

Další kroky