Criar um conjunto de dimensionamento de máquinas virtuais do Azure com base em uma imagem personalizada do Packer usando o Terraform
O Terraform permite a definição, a visualização e a implantação da infraestrutura de nuvem. Usando o Terraform, você cria arquivos de configuração usando sintaxe de HCL. A sintaxe da HCL permite que você especifique o provedor de nuvem, como o Azure, e os elementos que compõem sua infraestrutura de nuvem. Depois de criar os arquivos de configuração, você cria um plano de execução que permite visualizar as alterações de infraestrutura antes de serem implantadas. Depois de verificar as alterações, aplique o plano de execução para implantar a infraestrutura.
Os conjuntos de dimensionamento de máquinas virtuais do Azure permitem configurar VMs idênticas. O número de instâncias de VM pode ser ajustado com base na demanda ou em uma agenda. Para obter mais informações, consulte Dimensionar automaticamente um conjunto de dimensionamento de máquinas virtuais no portal do Azure.
Neste artigo, você aprenderá como:
- Configurar sua implantação do Terraform
- Usar variáveis e saídas para a implantação do Terraform
- Criar e implantar uma infraestrutura de rede
- Criar uma imagem de máquina virtual personalizada usando o Packer
- Criar e implantar um conjunto de dimensionamento de máquinas virtuais usando uma imagem personalizada
- Criar e implantar um jumpbox
1. Configurar seu ambiente
- Assinatura do Azure: caso você não tenha uma assinatura do Azure, crie uma conta gratuita antes de começar.
Configurar o Terraform: se ainda não tiver feito isso, configure o Terraform usando uma das seguintes opções:
2. Criar uma imagem do Packer
-
Pontos principais:
- Execute o comando
packer -v
para confirmar se você tem acesso ao executável do Packer. - Conforme o ambiente, talvez seja preciso definir o caminho e reabrir a linha de comando.
- Execute o comando
Execute az group create para criar um grupo de recursos no qual armazenar a imagem do Packer.
az group create -n myPackerImages -l eastus
Execute az ad sp create-for-rbac para permitir que o Packer execute uma autenticação no Azure usando uma entidade de serviço.
az ad sp create-for-rbac --role Contributor --scopes /subscriptions/<subscription_id> --query "{ client_id: appId, client_secret: password, tenant_id: tenant }"
Pontos principais:
- Anote os valores de saída (
appId
,client_secret
etenant_id
).
- Anote os valores de saída (
Execute az account show para exibir uma assinatura do Azure atual.
az account show --query "{ subscription_id: id }"
Crie um arquivo de variáveis de modelo Packer nomeado
ubuntu.pkr.hcl
e insira o código a seguir. Atualize as linhas realçadas com sua entidade de serviço e informações de assinatura do 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", ] } }
Pontos principais:
- Defina os campos
client_id
,client_secret
etenant_id
para os respectivos valores da entidade de serviço. - Defina o campo para sua ID de assinatura do
subscription_id
Azure.
- Defina os campos
Crie uma imagem do Packer.
packer build ubuntu.json
3. Implementar o código do Terraform
Crie um diretório em que você vai testar o código de exemplo do Terraform, depois transforme-o no diretório atual.
Crie um arquivo chamado
main.tf
e insira o seguinte código: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 }
Crie um arquivo chamado
variables.tf
para incluir as variáveis do projeto, depois insira o seguinte código: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 }
Crie um arquivo chamado
output.tf
para especificar quais valores serão exibidos pelo Terraform, depois insira o seguinte código: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. Inicializar o Terraform
Execute terraform init para inicializar a implantação do Terraform. Esse comando baixa o provedor do Azure necessário para gerenciar seus recursos do Azure.
terraform init -upgrade
Pontos principais:
- O parâmetro
-upgrade
atualiza os plug-ins do provedor necessários para a versão mais recente que esteja em conformidade com as restrições de versão da configuração.
5. Criar um plano de execução do Terraform
Execute o comando terraform plan para criar um plano de execução.
terraform plan -out main.tfplan
Pontos principais:
- O comando
terraform plan
cria um plano de execução, mas não o executa. Em vez disso, ele determina quais ações são necessárias para criar a configuração especificada em seus arquivos de configuração. Esse padrão permite que você verifique se o plano de execução corresponde às suas expectativas antes de fazer qualquer alteração nos recursos reais. - O parâmetro opcional
-out
permite que você especifique um arquivo de saída para o plano. Usar o parâmetro-out
garante que o plano que você examinou seja exatamente o que é aplicado.
6. Aplicar um plano de execução do Terraform
Execute terraform apply para aplicar o plano de execução à sua infraestrutura de nuvem.
terraform apply main.tfplan
Pontos principais:
- O exemplo de comando do
terraform apply
pressupõe que você executou oterraform plan -out main.tfplan
anteriormente. - Se você especificou um nome de arquivo diferente para o parâmetro
-out
, use esse mesmo nome de arquivo na chamada paraterraform apply
. - Se você não usou o parâmetro
-out
, chameterraform apply
sem nenhum parâmetro.
7. Verificar os resultados
Na saída do comando
terraform apply
, você verá os valores dos seguintes itens:- O FQDN da máquina virtual
- O FQDN do Jumpbox
- O endereço IP do Jumpbox
Acesse a URL da máquina virtual para confirmar uma página padrão com o texto Bem-vindo(a) ao nginx!.
Use o SSH para se conectar à VM Jumpbox com o nome de usuário definido no arquivo de variáveis e a senha indicada quando você executou
terraform apply
. Por exemplo:ssh azureuser@<ip_address>
.
8. Limpar os recursos
Excluir o conjunto de dimensionamento de máquinas virtuais
Quando você não precisar mais dos recursos criados por meio o Terraform, execute as seguintes etapas:
Execute terraform plan e especifique o sinalizador
destroy
.terraform plan -destroy -out main.destroy.tfplan
Pontos principais:
- O comando
terraform plan
cria um plano de execução, mas não o executa. Em vez disso, ele determina quais ações são necessárias para criar a configuração especificada em seus arquivos de configuração. Esse padrão permite que você verifique se o plano de execução corresponde às suas expectativas antes de fazer qualquer alteração nos recursos reais. - O parâmetro opcional
-out
permite que você especifique um arquivo de saída para o plano. Usar o parâmetro-out
garante que o plano que você examinou seja exatamente o que é aplicado.
- O comando
Execute a aplicação do Terraform para aplicar o plano de execução.
terraform apply main.destroy.tfplan
Excluir a imagem do Packer e o grupo de recursos
Execute az group delete a fim de excluir o grupo de recursos usado para conter a imagem do Packer. A imagem do Packer também será excluída.
az group delete --name myPackerImages --yes
Solucionar problemas do Terraform no Azure
Solucionar problemas comuns ao usar o Terraform no Azure