Esercitazione: Come usare cloud-init per personalizzare una macchina virtuale Linux in Azure al primo avvio

Si applica a: ✔️ macchine virtuali di Linux ✔️ set di scalabilità flessibili

In un'esercitazione precedente si è appreso come eseguire una connessione SSH a una macchina virtuale (VM) e come installare manualmente NGINX. Per creare macchine virtuali in modo rapido e coerente, di norma è consigliabile una qualche forma di automazione. Un approccio comune per personalizzare una macchina virtuale al primo avvio consiste nell'usare cloud-init. In questa esercitazione si apprenderà come:

  • Creare un file di configurazione cloud-init
  • Creare una macchina virtuale che usa un file cloud-init
  • Visualizzare un'esecuzione dell'app Node.js dopo aver creato la macchina virtuale
  • Usare Key Vault per archiviare in modo sicuro i certificati
  • Automatizzare le distribuzioni sicure di NGINX con cloud-init

Se si sceglie di installare e usare l'interfaccia della riga di comando in locale, per questa esercitazione è necessario eseguire l'interfaccia della riga di comando di Azure versione 2.0.30 o successiva. Eseguire az --version per trovare la versione. Se è necessario eseguire l'installazione o l'aggiornamento, vedere Installare l'interfaccia della riga di comando di Azure.

Panoramica di cloud-init

Cloud-init è un approccio diffuso per personalizzare una macchina virtuale Linux al primo avvio. Cloud-init consente di installare pacchetti e scrivere file o configurare utenti e impostazioni di sicurezza. Quando cloud-init viene eseguito durante il processo di avvio iniziale non vi sono altri passaggi o agenti necessari per applicare la configurazione.

Cloud-init funziona anche fra distribuzioni. Ad esempio, non si usa apt-get install o yum install per installare un pacchetto. In alternativa, è possibile definire un elenco di pacchetti da installare. Cloud-init userà automaticamente lo strumento di gestione del pacchetto nativo per la distribuzione selezionata.

Microsoft collabora con i partner per promuovere l'inclusione e il funzionamento di cloud-init con le immagini da essi fornite per Azure. Per informazioni dettagliate sul supporto di cloud-init per ogni distribuzione, vedere Supporto di cloud-init per le macchine virtuali in Azure.

Creare un file di configurazione cloud-init

Per visualizzare cloud-init in azione, creare una macchina virtuale, installare NGINX ed eseguire una semplice app Node.js "Hello World". La configurazione cloud-init seguente installa i pacchetti necessari, crea un'applicazione Node.js, quindi inizializza e avvia l'applicazione.

Al prompt di Bash o in Cloud Shell creare un file denominato cloud-init.txt e incollare la configurazione seguente. Ad esempio, digitare sensible-editor cloud-init.txt per creare il file e visualizzare un elenco degli editor disponibili. Assicurarsi che l'intero file cloud-init venga copiato correttamente, in particolare la prima riga:

#cloud-config
package_upgrade: true
packages:
  - nginx
  - nodejs
  - npm
write_files:
  - owner: www-data:www-data
    path: /etc/nginx/sites-available/default
    defer: true
    content: |
      server {
        listen 80;
        location / {
          proxy_pass http://localhost:3000;
          proxy_http_version 1.1;
          proxy_set_header Upgrade $http_upgrade;
          proxy_set_header Connection keep-alive;
          proxy_set_header Host $host;
          proxy_cache_bypass $http_upgrade;
        }
      }
  - owner: azureuser:azureuser
    path: /home/azureuser/myapp/index.js
    defer: true
    content: |
      var express = require('express')
      var app = express()
      var os = require('os');
      app.get('/', function (req, res) {
        res.send('Hello World from host ' + os.hostname() + '!')
      })
      app.listen(3000, function () {
        console.log('Hello world app listening on port 3000!')
      })
runcmd:
  - service nginx restart
  - cd "/home/azureuser/myapp"
  - npm init
  - npm install express -y
  - nodejs index.js

Per altre informazioni sulle opzioni di configurazione di cloud-init, vedere gli esempi di configurazione di cloud-init.

Crea macchina virtuale

Per poter creare una macchina virtuale è prima necessario creare un gruppo di risorse con il comando az group create. Nell'esempio seguente viene creato un gruppo di risorse denominato myResourceGroupAutomate nella posizione eastus:

az group create --name myResourceGroupAutomate --location eastus

Creare quindi una macchina virtuale con il comando az vm create. Usare il parametro --custom-data per specificare il file di configurazione di cloud-init. Se il file è stato salvato all'esterno della directory di lavoro corrente, specificare il percorso completo della configurazione cloud-init.txt . L'esempio seguente crea una VM denominata myVM:

az vm create \
    --resource-group myResourceGroupAutomate \
    --name myAutomatedVM \
    --image Ubuntu2204 \
    --admin-username azureuser \
    --generate-ssh-keys \
    --custom-data cloud-init.txt

Per creare la macchina virtuale, installare i pacchetti e avviare l'applicazione sono necessari alcuni minuti. Sono presenti attività in background la cui esecuzione continua dopo che l'interfaccia della riga di comando di Azure è tornata al prompt. Potrebbe trascorrere ancora qualche minuto prima che sia possibile accedere all'app. Dopo aver creato la macchina virtuale, prendere nota del publicIpAddress visualizzato dall'interfaccia della riga di comando di Azure. Questo indirizzo viene usato per accedere all'app Node.js tramite un Web browser.

Per consentire al traffico Web di raggiungere la macchina virtuale, aprire la porta 80 da Internet con il comando az vm open_port:

az vm open-port --port 80 --resource-group myResourceGroupAutomate --name myAutomatedVM

Testare l'app Web

È ora possibile aprire un Web browser e immettere http://<publicIpAddress> nella barra degli indirizzi. Fornire il proprio indirizzo IP pubblico dal processo di creazione della macchina virtuale. L'app Node.js viene visualizzata come illustrato nell'esempio seguente:

View running NGINX site

Inserire certificati da Key Vault

Questa sezione facoltativa illustra come archiviare in modo protetto i certificati in Azure Key Vault e inserirli durante la distribuzione della macchina virtuale. Anziché usare un'immagine personalizzata che includa i certificati integrati, questo processo assicura che i certificati aggiornati vengano inseriti in una macchina virtuale al primo avvio. Durante il processo, il certificato non lascia mai la piattaforma Azure e non viene mai esposto in uno script, in una cronologia della riga di comando o in un modello.

Azure Key Vault consente di proteggere chiavi crittografiche e segreti, come certificati e password. Key Vault semplifica il processo di gestione delle chiavi e consente di mantenere il controllo delle chiavi che accedono ai dati e li crittografano. Questo scenario introduce alcuni concetti chiave di Key Vault per creare e usare un certificato, sebbene non rappresenti una panoramica esaustiva sull'uso di Key Vault.

I passaggi seguenti mostrano come sia possibile:

  • Creare un Azure Key Vault
  • Generare o caricare un certificato in Key Vault
  • Creare una chiave privata dal certificato da inserire in una macchina virtuale
  • Creare una macchina virtuale e inserire il certificato

Creare un Azure Key Vault

Innanzitutto, creare un Key Vault con il comando az keyvault create e abilitarlo all'uso quando si distribuisce una macchina virtuale. Ogni Key Vault deve avere un nome univoco in lettere minuscole. Nell'esempio seguente sostituire mykeyvault con il nome univoco del proprio Key Vault:

keyvault_name=mykeyvault
az keyvault create \
    --resource-group myResourceGroupAutomate \
    --name $keyvault_name \
    --enabled-for-deployment

Generare certificati e archiviarli in Key Vault

Per la produzione è necessario importare un certificato valido firmato da un provider attendibile con il comando az keyvault certificate import. Per questa esercitazione, l'esempio seguente illustra come sia possibile generare un certificato autofirmato con il comando az keyvault certificate create che usi i criteri dei certificati predefiniti:

az keyvault certificate create \
    --vault-name $keyvault_name \
    --name mycert \
    --policy "$(az keyvault certificate get-default-policy --output json)"

Preparare i certificati per l'uso con macchine virtuali

Per usare il certificato durante il processo di creazione della macchina virtuale, ottenere l'ID del certificato con il comando az keyvault secret list-versions. La macchina virtuale richiede che il certificato abbia un formato specifico per inserirlo all'avvio, quindi è necessario convertire il certificato con il comando az vm secret format. L'esempio seguente assegna l'output di questi comandi a delle variabili per semplificarne l'uso nei passaggi successivi:

secret=$(az keyvault secret list-versions \
          --vault-name $keyvault_name \
          --name mycert \
          --query "[?attributes.enabled].id" --output tsv)
vm_secret=$(az vm secret format --secret "$secret" --output json)

Creare una configurazione cloud-init per proteggere NGINX

Quando si crea una macchina virtuale, certificati e chiavi vengono archiviati nella directory /var/lib/waagent/ protetta. Per automatizzare l'aggiunta del certificato alla macchina virtuale e la configurazione di NGINX, è possibile usare una configurazione di cloud-init aggiornata dall'esempio precedente.

Creare un file denominato cloud-init-secured.txt e incollare la configurazione seguente. Se si usa Cloud Shell, creare il file di configurazione cloud-init in questa posizione e non nel computer locale. Ad esempio, digitare sensible-editor cloud-init-secured.txt per creare il file e visualizzare un elenco degli editor disponibili. Assicurarsi che l'intero file cloud-init venga copiato correttamente, in particolare la prima riga:

#cloud-config
package_upgrade: true
packages:
  - nginx
  - nodejs
  - npm
write_files:
  - owner: www-data:www-data
    path: /etc/nginx/sites-available/default
    defer: true
    content: |
      server {
        listen 80;
        listen 443 ssl;
        ssl_certificate /etc/nginx/ssl/mycert.cert;
        ssl_certificate_key /etc/nginx/ssl/mycert.prv;
        location / {
          proxy_pass http://localhost:3000;
          proxy_http_version 1.1;
          proxy_set_header Upgrade $http_upgrade;
          proxy_set_header Connection keep-alive;
          proxy_set_header Host $host;
          proxy_cache_bypass $http_upgrade;
        }
      }
  - owner: azureuser:azureuser
    path: /home/azureuser/myapp/index.js
    defer: true
    content: |
      var express = require('express')
      var app = express()
      var os = require('os');
      app.get('/', function (req, res) {
        res.send('Hello World from host ' + os.hostname() + '!')
      })
      app.listen(3000, function () {
        console.log('Hello world app listening on port 3000!')
      })
runcmd:
  - secretsname=$(find /var/lib/waagent/ -name "*.prv" | cut -c -57)
  - mkdir /etc/nginx/ssl
  - cp $secretsname.crt /etc/nginx/ssl/mycert.cert
  - cp $secretsname.prv /etc/nginx/ssl/mycert.prv
  - service nginx restart
  - cd "/home/azureuser/myapp"
  - npm init
  - npm install express -y
  - nodejs index.js

Creare una macchina virtuale protetta

Creare quindi una macchina virtuale con il comando az vm create. I dati del certificato sono inseriti da Key Vault con il parametro --secrets. Come nell'esempio precedente, si seleziona anche la configurazione cloud-init con il parametro --custom-data:

az vm create \
    --resource-group myResourceGroupAutomate \
    --name myVMWithCerts \
    --image Ubuntu2204 \
    --admin-username azureuser \
    --generate-ssh-keys \
    --custom-data cloud-init-secured.txt \
    --secrets "$vm_secret"

Per creare la macchina virtuale, installare i pacchetti e avviare l'applicazione sono necessari alcuni minuti. Sono presenti attività in background la cui esecuzione continua dopo che l'interfaccia della riga di comando di Azure è tornata al prompt. Potrebbe trascorrere ancora qualche minuto prima che sia possibile accedere all'app. Dopo aver creato la macchina virtuale, prendere nota del publicIpAddress visualizzato dall'interfaccia della riga di comando di Azure. Questo indirizzo viene usato per accedere all'app Node.js tramite un Web browser.

Per consentire al traffico Web protetto di raggiungere la macchina virtuale, aprire la porta 443 da Internet con il comando az vm open-port:

az vm open-port \
    --resource-group myResourceGroupAutomate \
    --name myVMWithCerts \
    --port 443

Testare l'applicazione Web protetta

È ora possibile aprire un Web browser e immettere https://<publicIpAddress> nella barra degli indirizzi. Fornire il proprio indirizzo IP pubblico come illustrato nell'output del processo di creazione della macchina virtuale riportato in precedenza. Se è stato usato un certificato autofirmato, accettare l'avviso di sicurezza:

Accept web browser security warning

Il sito protetto NGINX e la app Node.js sono visualizzati come illustrato nell'esempio seguente:

View running secure NGINX site

Passaggi successivi

In questa esercitazione vengono configurate macchine virtuali al primo avvio con cloud-init. Contenuto del modulo:

  • Creare un file di configurazione cloud-init
  • Creare una macchina virtuale che usa un file cloud-init
  • Visualizzare un'esecuzione dell'app Node.js dopo aver creato la macchina virtuale
  • Usare Key Vault per archiviare in modo sicuro i certificati
  • Automatizzare le distribuzioni sicure di NGINX con cloud-init

Passare all'esercitazione successiva per imparare a creare immagini di macchine virtuali personalizzate.