Provisionar VMs no Azure com boas Práticas: Imagens, Extensions e Cloud-init

Criar uma máquina virtual no Azure é simples. Criar dezenas ou centenas de VMs consistentes, seguras, atualizáveis e fáceis de operar é outro desafio. Uma implementação sólida não começa pela escolha do tamanho da VM. Começa por três decisões: qual é a imagem de origem, o que deve ser configurado no primeiro arranque e o que precisa de continuar sob controlo da plataforma depois do deployment.

É aqui que entram três componentes fundamentais:

  • Imagens, para definir uma base conhecida e repetível;
  • Cloud-init, para personalizar sistemas Linux durante o primeiro arranque;
  • VM extensions, para instalar e gerir capacidades integradas com o Azure.

Quando estas responsabilidades são bem separadas, o provisionamento torna-se previsível. Quando tudo é colocado num único script, surgem deployments lentos, difíceis de testar e ainda mais difíceis de recuperar.

A primeira pergunta não é “que script devo executar?”

Antes de escolher uma tecnologia, devemos perceber a natureza da configuração.

Esta divisão evita dois extremos frequentes: imagens excessivamente específicas, que precisam de ser reconstruídas por qualquer alteração, e scripts de arranque gigantes, que transformam cada deployment numa instalação completa e imprevisível.

1- Imagens: Construir uma base Controlada

As imagens de Marketplace são um bom ponto de partida para workloads comuns. Uma organização pode, no entanto, precisar de uma imagem própria com hardening, certificados empresariais, agentes obrigatórios ou dependências previamente instaladas. Para gerir imagens próprias, o Azure Compute Gallery oferece versionamento, replicação regional, controlo de acesso e distribuição para VMs e Virtual Machine Scale Sets.

Imagem generalizada ou especializada?

Uma imagem generalizada remove a identidade específica da máquina e conclui a configuração no primeiro arranque. É a opção habitual quando a mesma imagem será reutilizada para criar várias VMs. Uma imagem especializada conserva a configuração da máquina original. Pode ser útil para restaurar ou clonar um sistema já preparado, mas exige cuidado com elementos que devem ser únicos, como o hostname.
Para uma plataforma padronizada, a regra prática é simples: prefira imagens generalizadas e mantenha a personalização de cada instância fora da imagem.

Boas práticas para a fábrica de imagens

  • Automatize a construção. Use Azure VM Image Builder ou outro pipeline reproduzível, em vez de preparar manualmente uma VM.
  • Versione de forma explícita. Promova versões entre desenvolvimento, teste e produção; não dependa cegamente de latest.
  • Teste antes de promover. Valide arranque, conectividade, agentes, atualizações e requisitos de segurança.
  • Não incorpore segredos. Credenciais não devem existir na imagem, no template ou nos scripts usados para a construir. Use identidades geridas e Azure Key Vault.
  • Aplique menor privilégio. Restrinja o acesso aos templates, identidades e resource groups de staging usados pelo processo de construção.
  • Planeie a distribuição. Replique a imagem para as regiões onde será utilizada e ajuste o número de réplicas à concorrência esperada.
  • Proteja versões de produção. Utilize excludeFromLatest para retirar uma versão problemática da seleção automática e proteções contra eliminação acidental.
  • Reconstrua regularmente. Uma golden image não deve tornar-se uma imagem antiga. Atualize-a para incorporar correções do sistema operativo e dos componentes base.

Para produção, a documentação do Azure recomenda ZRS onde estiver disponível, pelo menos três réplicas por imagem e, como orientação de escala, uma réplica por cada 20 VMs criadas em simultâneo. Em cenários de disaster recovery, devem existir galerias em pelo menos duas regiões.

2- Cloud-init: Personalizar sem Transformar a Imagem

Em Linux, o cloud-init processa a configuração fornecida no primeiro arranque. Pode criar utilizadores, instalar pacotes, escrever ficheiros, configurar serviços e executar comandos.
O Azure recomenda cloud-init como agente de provisionamento para Linux. O Azure Linux Agent, também conhecido como WALA, continua normalmente instalado para processar VM extensions.

Um exemplo pequeno e idempotente:

#cloud-config
package_update: true
packages:
  - nginx

write_files:
  - path: /etc/nginx/conf.d/health.conf
    permissions: "0644"
    content: |
      server {
        listen 8080;
        location /health {
          access_log off;
          return 200 "healthy\n";
        }
      }

runcmd:
  - [systemctl, enable, --now, nginx]

final_message: "Provisionamento concluído em $UPTIME segundos."

Regras para um cloud-init saudável

  • Mantenha-o curto. Instalações grandes e compilações pertencem à imagem ou ao pipeline da aplicação.
  • Escreva operações idempotentes. Reexecutar uma ação não deve duplicar utilizadores, entradas ou configuração.
  • Evite segredos. O customData não deve conter passwords, tokens ou chaves privadas.
  • Evite dependências frágeis. Se o arranque depender de um endpoint externo, prepare retries, timeouts e observabilidade.
  • Não confunda VM criada com configuração concluída. O Azure pode reportar a VM como criada enquanto tarefas de cloud-init ainda estão em execução.
  • Valide o resultado. Verifique cloud-init status --wait e consulte /var/log/cloud-init.log e /var/log/cloud-init-output.log.
  • Respeite o limite. O customData tem um limite de 64 KB e, numa VM individual, não pode ser atualizado no modelo depois da criação.

Quando a informação precisa de continuar acessível ou ser alterada durante a vida da VM, considere user data, recuperável através do Azure Instance Metadata Service, em vez de customData.

3- VM extensions: Capacidades Geridas depois do Deployment

As VM extensions são pequenos componentes executados pelo Azure VM Agent. Permitem adicionar funcionalidades como Azure Monitor Agent, acesso a segredos do Key Vault, configuração de políticas, diagnóstico ou execução controlada de scripts.
Uma extension não deve ser tratada como um substituto universal para gestão de configuração. Quanto mais trabalho colocarmos numa Custom Script Extension, maior será o risco de timeout, conflitos no gestor de pacotes e falhas difíceis de diagnosticar.

Boas práticas para extensions

  • Use uma extension por responsabilidade. Monitorização, segurança e configuração não devem ficar escondidas num único script.
  • Prefira extensions oficiais e suportadas.
  • Ative atualizações automáticas quando suportadas. enableAutomaticUpgrade permite upgrades graduais e orientados à disponibilidade; autoUpgradeMinorVersion seleciona uma versão secundária mais recente no momento do deployment ou de uma alteração ao modelo.
  • Proteja valores sensíveis. Coloque-os em protectedSettings, nunca em settings.
  • Declare dependências. Em VM Scale Sets, use o sequenciamento quando uma extension depende de outra.
  • Garanta conectividade. A VM deve alcançar os endpoints, repositórios e serviços exigidos pela extension.
  • Observe o estado real. A model view mostra a configuração pedida; a instance view mostra a versão e o estado efetivamente instalados.
  • Evite concorrência no gestor de pacotes. cloud-init e extensions executados ao mesmo tempo podem disputar apt, dnf ou yum.

As extensions têm limites de execução. Em geral, dispõem de 20 minutos; Custom Script e Chef podem dispor de 90 minutos. Operações demoradas devem ser retiradas do caminho de provisionamento.

Um padrão Bicep simples

O exemplo seguinte mostra a separação de responsabilidades: a VM nasce de uma versão explícita da Compute Gallery, recebe cloud-init por customData e instala uma extension gerida separadamente.

param location string = resourceGroup().location
param vmName string
param adminUsername string
param sshPublicKey string
param imageVersionId string
param nicId string

resource vm 'Microsoft.Compute/virtualMachines@2024-07-01' = {
  name: vmName
  location: location
  properties: {
    hardwareProfile: {
      vmSize: 'Standard_D2s_v5'
    }
    storageProfile: {
      imageReference: {
        id: imageVersionId
      }
      osDisk: {
        createOption: 'FromImage'
        managedDisk: {
          storageAccountType: 'Premium_LRS'
        }
      }
    }
    osProfile: {
      computerName: vmName
      adminUsername: adminUsername
      customData: base64(loadTextContent('cloud-init.yaml'))
      linuxConfiguration: {
        disablePasswordAuthentication: true
        ssh: {
          publicKeys: [
            {
              path: '/home/${adminUsername}/.ssh/authorized_keys'
              keyData: sshPublicKey
            }
          ]
        }
      }
    }
    networkProfile: {
      networkInterfaces: [
        {
          id: nicId
        }
      ]
    }
  }
}

resource monitorAgent 'Microsoft.Compute/virtualMachines/extensions@2024-07-01' = {
  parent: vm
  name: 'AzureMonitorLinuxAgent'
  location: location
  properties: {
    publisher: 'Microsoft.Azure.Monitor'
    type: 'AzureMonitorLinuxAgent'
    typeHandlerVersion: '1.0'
    autoUpgradeMinorVersion: true
    enableAutomaticUpgrade: true
    settings: {}
  }
}

Em produção, complemente este padrão com Trusted Launch quando suportado, identidade gerida, regras de rede restritivas, encriptação, Azure Update Manager, recolha de logs e políticas de backup adequadas ao workload.

Uma sequência de provisionamento mais segura

Uma abordagem previsível pode seguir estes passos:

  • O pipeline constrói e testa uma nova imagem.
  • A imagem é publicada como uma nova versão na Azure Compute Gallery.
  • A versão é validada num ambiente não produtivo.
  • O template de infraestrutura passa a referenciar essa versão explícita.
  • O cloud-init aplica apenas a configuração específica da instância.
  • As extensions adicionam capacidades geridas pelo Azure.
  • Testes de saúde confirmam que a VM está realmente pronta.
  • A versão é promovida gradualmente e existe um caminho claro de rollback.

Este processo reduz a diferença entre ambientes e torna cada alteração rastreável.

Checklist antes de colocar em produção

  • A origem e a versão da imagem estão explicitamente definidas.
  • A imagem é reconstruída e testada automaticamente.
  • Não existem segredos na imagem, em customData ou em settings.
  • O acesso administrativo usa chaves SSH e regras de rede restritivas.
  • O cloud-init é curto, idempotente e observável.
  • O pipeline espera pela conclusão real da configuração e executa health checks.
  • As extensions têm atualização automática ativada quando suportada.
  • Valores sensíveis de extensions usam protectedSettings ou Key Vault.
  • A instance view e os logs das extensions são monitorizados.
  • Existe estratégia de versionamento, promoção e rollback.
  • As imagens estão replicadas nas regiões necessárias.
  • Backup, patching, monitorização e recuperação foram planeados.

Concluindo, provisionar bem uma VM não é apenas conseguir que ela arranque. É conseguir reproduzir o mesmo resultado, compreender o que foi aplicado, atualizar cada camada com segurança e recuperar quando algo falha. A imagem deve fornecer uma base controlada. O cloud-init deve tratar a personalização inicial. As VM extensions devem ligar a máquina às capacidades de gestão do Azure. Com responsabilidades claras, infraestrutura como código e validação automática, o provisionamento deixa de ser uma sequência de scripts e passa a ser um processo de engenharia.

Boas decisões hoje constroem um Azure mais forte amanhã.

Referências

Overview of Linux provisioning – Azure Virtual Machines | Microsoft Learn
Overview of cloud-init support for Linux VMs in Azure – Azure Virtual Machines | Microsoft Learn
Custom data on Azure virtual machines – Azure Virtual Machines | Microsoft Learn
Overview of Azure Compute Gallery – Azure Virtual Machines | Microsoft Learn
Best Practices for Azure VM Image Builder – Azure Virtual Machines | Microsoft Learn

Até ao próximo artigo!

Leave a Comment

O seu endereço de email não será publicado. Campos obrigatórios marcados com *