Creazione di immagini generalizzate senza un agente di provisioning
Si applica a: ✔️ macchine virtuali Linux ✔️ set di scalabilità flessibili
Microsoft Azure fornisce agenti di provisioning per macchine virtuali Linux sotto forma di walinuxagent o cloud-init (scelta consigliata). Tuttavia, potrebbe esserci uno scenario in cui non si vuole usare una di queste applicazioni per l'agente di provisioning, ad esempio:
- La distribuzione/versione di Linux non supporta l'agente cloud-init/Linux.
- È necessario impostare proprietà specifiche della macchina virtuale, ad esempio il nome host.
Nota
Se non è necessario impostare proprietà o qualsiasi forma di provisioning, è consigliabile creare un'immagine specializzata.
Questo articolo illustra come configurare l'immagine della macchina virtuale per soddisfare i requisiti della piattaforma Azure e impostare il nome host, senza installare un agente di provisioning.
Rete e segnalazione di una macchina come pronta
Per fare in modo che la macchina virtuale Linux comunichi con i componenti di Azure, è necessario un client DHCP. Il client viene usato per recuperare un indirizzo IP host, una risoluzione DNS e una gestione delle route dalla rete virtuale. La maggior parte delle distribuzioni è disponibile con queste utilità predefinite. Gli strumenti testati in Azure dai fornitori di distribuzioni Linux includono dhclient
, network-manager
, systemd-networkd
e altri.
Nota
Attualmente la creazione di immagini generalizzate senza un agente di provisioning supporta solo macchine virtuali abilitate per DHCP.
Dopo aver configurato e configurato la rete, selezionare "Segnala come pronta". Questo indica ad Azure che il provisioning della macchina virtuale è stato eseguito correttamente.
Importante
Se non si riesce a segnalare la preparazione ad Azure, la macchina virtuale verrà riavviata.
Demo/esempio
Un'immagine del Marketplace esistente (in questo caso, una macchina virtuale Debian Buster) con l'agente Linux (walinuxagent) rimossa e uno script Python personalizzato aggiunto è il modo più semplice per indicare ad Azure che la macchina virtuale è "pronta".
Creare il gruppo di risorse e la macchina virtuale di base:
$ az group create --location eastus --name demo1
Creare la macchina virtuale di base:
$ az vm create \
--resource-group demo1 \
--name demo1 \
--location eastus \
--ssh-key-value <ssh_pub_key_path> \
--public-ip-address-dns-name demo1 \
--image "debian:debian-10:10:latest"
Rimuovere l'agente di provisioning delle immagini
Dopo aver effettuato il provisioning della macchina virtuale, è possibile connettersi tramite SSH e rimuovere l'agente Linux:
$ sudo apt purge -y waagent
$ sudo rm -rf /var/lib/waagent /etc/waagent.conf /var/log/waagent.log
Aggiungere il codice necessario alla macchina virtuale
Anche all'interno della macchina virtuale, poiché è stato rimosso l'agente Linux di Azure, è necessario fornire un meccanismo per segnalare che la macchina è pronta.
Script Python
import http.client
import sys
from xml.etree import ElementTree
wireserver_ip = '168.63.129.16'
wireserver_conn = http.client.HTTPConnection(wireserver_ip)
print('Retrieving goal state from the Wireserver')
wireserver_conn.request(
'GET',
'/machine?comp=goalstate',
headers={'x-ms-version': '2012-11-30'}
)
resp = wireserver_conn.getresponse()
if resp.status != 200:
print('Unable to connect with wireserver')
sys.exit(1)
wireserver_goalstate = resp.read().decode('utf-8')
xml_el = ElementTree.fromstring(wireserver_goalstate)
container_id = xml_el.findtext('Container/ContainerId')
instance_id = xml_el.findtext('Container/RoleInstanceList/RoleInstance/InstanceId')
incarnation = xml_el.findtext('Incarnation')
print(f'ContainerId: {container_id}')
print(f'InstanceId: {instance_id}')
print(f'Incarnation: {incarnation}')
# Construct the XML response we need to send to Wireserver to report ready.
health = ElementTree.Element('Health')
goalstate_incarnation = ElementTree.SubElement(health, 'GoalStateIncarnation')
goalstate_incarnation.text = incarnation
container = ElementTree.SubElement(health, 'Container')
container_id_el = ElementTree.SubElement(container, 'ContainerId')
container_id_el.text = container_id
role_instance_list = ElementTree.SubElement(container, 'RoleInstanceList')
role = ElementTree.SubElement(role_instance_list, 'Role')
instance_id_el = ElementTree.SubElement(role, 'InstanceId')
instance_id_el.text = instance_id
health_second = ElementTree.SubElement(role, 'Health')
state = ElementTree.SubElement(health_second, 'State')
state.text = 'Ready'
out_xml = ElementTree.tostring(
health,
encoding='unicode',
method='xml'
)
print('Sending the following data to Wireserver:')
print(out_xml)
wireserver_conn.request(
'POST',
'/machine?comp=health',
headers={
'x-ms-version': '2012-11-30',
'Content-Type': 'text/xml;charset=utf-8',
'x-ms-agent-name': 'custom-provisioning'
},
body=out_xml
)
resp = wireserver_conn.getresponse()
print(f'Response: {resp.status} {resp.reason}')
wireserver_conn.close()
Script Bash
#!/bin/bash
attempts=1
until [ "$attempts" -gt 5 ]
do
echo "obtaining goal state - attempt $attempts"
goalstate=$(curl --fail -v -X 'GET' -H "x-ms-agent-name: azure-vm-register" \
-H "Content-Type: text/xml;charset=utf-8" \
-H "x-ms-version: 2012-11-30" \
"http://168.63.129.16/machine/?comp=goalstate")
if [ $? -eq 0 ]
then
echo "successfully retrieved goal state"
retrieved_goal_state=true
break
fi
sleep 5
attempts=$((attempts+1))
done
if [ "$retrieved_goal_state" != "true" ]
then
echo "failed to obtain goal state - cannot register this VM"
exit 1
fi
container_id=$(grep ContainerId <<< "$goalstate" | sed 's/\s*<\/*ContainerId>//g' | sed 's/\r$//')
instance_id=$(grep InstanceId <<< "$goalstate" | sed 's/\s*<\/*InstanceId>//g' | sed 's/\r$//')
ready_doc=$(cat << EOF
<?xml version="1.0" encoding="utf-8"?>
<Health xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<GoalStateIncarnation>1</GoalStateIncarnation>
<Container>
<ContainerId>$container_id</ContainerId>
<RoleInstanceList>
<Role>
<InstanceId>$instance_id</InstanceId>
<Health>
<State>Ready</State>
</Health>
</Role>
</RoleInstanceList>
</Container>
</Health>
EOF
)
attempts=1
until [ "$attempts" -gt 5 ]
do
echo "registering with Azure - attempt $attempts"
curl --fail -v -X 'POST' -H "x-ms-agent-name: azure-vm-register" \
-H "Content-Type: text/xml;charset=utf-8" \
-H "x-ms-version: 2012-11-30" \
-d "$ready_doc" \
"http://168.63.129.16/machine?comp=health"
if [ $? -eq 0 ]
then
echo "successfully register with Azure"
break
fi
sleep 5 # sleep to prevent throttling from wire server
done
Passaggi generici (se non si usano Python o Bash)
Se la macchina virtuale non ha Python installato o disponibile, è possibile riprodurre a livello di codice questa logica di script precedente seguendo questa procedura:
Recuperare
ContainerId
,InstanceId
eIncarnation
analizzando la risposta da WireServer:curl -X GET -H 'x-ms-version: 2012-11-30' http://168.63.129.16/machine?comp=goalstate
.Costruire i dati XML seguenti, inserendo i valori
ContainerId
,InstanceId
eIncarnation
analizzati dal passaggio precedente:<Health> <GoalStateIncarnation>INCARNATION</GoalStateIncarnation> <Container> <ContainerId>CONTAINER_ID</ContainerId> <RoleInstanceList> <Role> <InstanceId>INSTANCE_ID</InstanceId> <Health> <State>Ready</State> </Health> </Role> </RoleInstanceList> </Container> </Health>
Pubblicare questi dati in WireServer:
curl -X POST -H 'x-ms-version: 2012-11-30' -H "x-ms-agent-name: WALinuxAgent" -H "Content-Type: text/xml;charset=utf-8" -d "$REPORT_READY_XML" http://168.63.129.16/machine?comp=health
Automazione dell'esecuzione del codice al primo avvio
Questa demo usa systemd, che è il sistema init più comune nelle distribuzioni Linux moderne. Pertanto, il modo più semplice e nativo per garantire che questo meccanismo di segnalazione della prontezza venga eseguito al momento giusto consiste nel creare un'unità di servizio di sistema. È possibile aggiungere il file di unità seguente a /etc/systemd/system
(in questo esempio il file di unità azure-provisioning.service
):
[Unit]
Description=Azure Provisioning
[Service]
Type=oneshot
ExecStart=/usr/bin/python3 /usr/local/azure-provisioning.py
ExecStart=/bin/bash -c "hostnamectl set-hostname $(curl \
-H 'metadata: true' \
'http://169.254.169.254/metadata/instance/compute/name?api-version=2019-06-01&format=text')"
ExecStart=/usr/bin/systemctl disable azure-provisioning.service
[Install]
WantedBy=multi-user.target
Questo servizio systemd esegue tre operazioni per il provisioning di base:
- Segnalazioni di prontezza ad Azure (per indicare la corretta esecuzione).
- Rinomina la macchina virtuale in base al nome della macchina virtuale fornita dall'utente eseguendo il pull di questi dati dal Servizio metadati dell'istanza di Azure (IMDS). Nota IMDS fornisce anche altri metadati dell'istanza, ad esempio chiavi pubbliche SSH, in modo da poter impostare altri elementi oltre al nome host.
- Si disabilita in modo che venga eseguito solo al primo avvio e non ai riavvii successivi.
Con l'unità nel file system, eseguire quanto segue per abilitarla:
$ sudo systemctl enable azure-provisioning.service
Ora la macchina virtuale è pronta per essere generalizzata e creare un'immagine.
Completamento della preparazione dell'immagine
Tornare al computer di sviluppo per preparare la creazione di immagini dalla macchina virtuale di base:
$ az vm deallocate --resource-group demo1 --name demo1
$ az vm generalize --resource-group demo1 --name demo1
Creare l'immagine da questa macchina virtuale:
$ az image create \
--resource-group demo1 \
--source demo1 \
--location eastus \
--name demo1img
A questo punto è possibile creare una nuova macchina virtuale dall'immagine. Questa operazione può essere usata anche per creare più macchine virtuali:
$ IMAGE_ID=$(az image show -g demo1 -n demo1img --query id -o tsv)
$ az vm create \
--resource-group demo12 \
--name demo12 \
--location eastus \
--ssh-key-value <ssh_pub_key_path> \
--public-ip-address-dns-name demo12 \
--image "$IMAGE_ID"
--enable-agent false
Nota
È importante impostare --enable-agent
su false
perché walinuxagent non esiste in questa macchina virtuale che verrà creata dall'immagine.
Il provisioning della macchina virtuale deve essere eseguito correttamente. Dopo aver eseguito l'accesso alla macchina virtuale appena sottoposta a provisioning, dovrebbe essere possibile visualizzare l'output del servizio di sistema di segnalazione della prontezza:
$ sudo journalctl -u azure-provisioning.service
-- Logs begin at Thu 2020-06-11 20:28:45 UTC, end at Thu 2020-06-11 20:31:24 UTC. --
Jun 11 20:28:49 thstringnopa systemd[1]: Starting Azure Provisioning...
Jun 11 20:28:54 thstringnopa python3[320]: Retrieving goal state from the Wireserver
Jun 11 20:28:54 thstringnopa python3[320]: ContainerId: 7b324f53-983a-43bc-b919-1775d6077608
Jun 11 20:28:54 thstringnopa python3[320]: InstanceId: fbb84507-46cd-4f4e-bd78-a2edaa9d059b._thstringnopa2
Jun 11 20:28:54 thstringnopa python3[320]: Sending the following data to Wireserver:
Jun 11 20:28:54 thstringnopa python3[320]: <Health><GoalStateIncarnation>1</GoalStateIncarnation><Container><ContainerId>7b324f53-983a-43bc-b919-1775d6077608</ContainerId><RoleInstanceList><Role><InstanceId>fbb84507-46cd-4f4e-bd78-a2edaa9d059b._thstringnopa2</InstanceId><Health><State>Ready</State></Health></Role></RoleInstanceList></Container></Health>
Jun 11 20:28:54 thstringnopa python3[320]: Response: 200 OK
Jun 11 20:28:56 thstringnopa bash[472]: % Total % Received % Xferd Average Speed Time Time Time Current
Jun 11 20:28:56 thstringnopa bash[472]: Dload Upload Total Spent Left Speed
Jun 11 20:28:56 thstringnopa bash[472]: [158B blob data]
Jun 11 20:28:56 thstringnopa2 systemctl[475]: Removed /etc/systemd/system/multi-user.target.wants/azure-provisioning.service.
Jun 11 20:28:56 thstringnopa2 systemd[1]: azure-provisioning.service: Succeeded.
Jun 11 20:28:56 thstringnopa2 systemd[1]: Started Azure Provisioning.
Supporto tecnico
Se si implementa un codice/agente di provisioning personalizzato, si è proprietari del supporto di questo codice. Il supporto tecnico Microsoft esaminerà solamente i problemi relativi alle interfacce di provisioning non disponibili. Stiamo apportando continuamente miglioramenti e modifiche in questa area, quindi è necessario monitorare le modifiche apportate a cloud-init e all'agente Linux di Azure per il provisioning delle modifiche dell'API.
Passaggi successivi
Per altre informazioni, vedere Provisioning Linux.