Création d’images généralisées sans agent d’approvisionnement
S’applique à : ✔️ Machines virtuelles Linux ✔️ Groupes identiques flexibles
Microsoft Azure fournit des agents d’approvisionnement pour les machines virtuelles Linux sous la forme de walinuxagent ou cloud-init (recommandé). Toutefois, il peut y avoir un scénario où vous ne souhaitez pas utiliser l’une de ces applications pour votre agent d’approvisionnement, par exemple :
- Votre version/distribution Linux ne prend pas en charge cloud-init/l’agent Linux.
- Vous devez définir des propriétés de machine virtuelle spécifiques, telles que le nom d'hôte.
Notes
Si vous n’avez pas besoin de définir des propriétés ou une forme d’approvisionnement, vous devez envisager de créer une image spécialisée.
Cet article décrit comment configurer l’image de votre machine virtuelle pour répondre aux exigences de la plateforme Azure et définir le nom d’hôte, sans installer d’agent d’approvisionnement.
Préparer la mise en réseau et les rapports
Pour que votre machine virtuelle Linux communique avec les composants Azure, un client DHCP est nécessaire. Le client est utilisé pour récupérer une adresse IP hôte, une résolution DNS et la gestion des itinéraires à partir du réseau virtuel. La plupart des distributions sont livrées avec ces utilitaires prêts à l’emploi. Les outils qui sont testés sur Azure par les fournisseurs de distribution Linux incluent dhclient
, network-manager
, systemd-networkd
et d’autres.
Notes
Actuellement, la création d’images généralisées sans agent d’approvisionnement ne prend en charge que les machines virtuelles compatibles DHCP.
Après avoir paramétrer et configurer la mise en réseau, sélectionnez « rapport prêt ». Cela indique à Azure que la machine virtuelle a été approvisionnée avec succès.
Important
Si vous ne réussissez pas à créer de rapports sur Azure, votre machine virtuelle sera redémarrée.
Démonstration/Exemple
Une image de la place de marché existante (dans ce cas, une machine virtuelle Debian Buster) sur laquelle l’agent Linux (walinuxagent) est supprimé et un script Python personnalisé est ajouté est le moyen le plus simple de dire à Azure que la machine virtuelle est « prête ».
Créez le groupe de ressources et la machine virtuelle de base :
$ az group create --location eastus --name demo1
Créez la machine virtuelle de 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"
Supprimer l’image de l’agent d’approvisionnement
Une fois la machine virtuelle approvisionnée, vous pouvez vous y connecter via SSH et supprimer l’agent Linux :
$ sudo apt purge -y waagent
$ sudo rm -rf /var/lib/waagent /etc/waagent.conf /var/log/waagent.log
Ajouter le code requis à la machine virtuelle
Étant donné que nous avons supprimé l’agent Linux Azure, nous devons fournir un mécanisme de rapport prêt à l’intérieur de la machine virtuelle.
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
Étapes génériques (si vous n’utilisez pas Python ou Bash)
Si Python n’est pas installé ou disponible sur votre machine virtuelle, vous pouvez reproduire par programmation la logique de script ci-dessus en procédant comme suit :
Récupérez les
ContainerId
,InstanceId
etIncarnation
en analysant la réponse de WireServer :curl -X GET -H 'x-ms-version: 2012-11-30' http://168.63.129.16/machine?comp=goalstate
.Construisez les données XML suivantes, en injectant
ContainerId
analysé,InstanceId
etIncarnation
à partir de l’étape ci-dessus :<Health> <GoalStateIncarnation>INCARNATION</GoalStateIncarnation> <Container> <ContainerId>CONTAINER_ID</ContainerId> <RoleInstanceList> <Role> <InstanceId>INSTANCE_ID</InstanceId> <Health> <State>Ready</State> </Health> </Role> </RoleInstanceList> </Container> </Health>
Publiez ces données dans 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
Automatisation de l’exécution du code au premier démarrage
Cette démonstration utilise SystemD, qui est le système init le plus courant dans les distributions Linux modernes. La méthode la plus simple et la plus native pour s’assurer que ce mécanisme prêt pour les rapports s’exécute au bon moment est la création d’une unité de service système. Vous pouvez ajouter le fichier d’unité suivant à /etc/systemd/system
(cet exemple nomme le fichier d’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
Ce service système fait trois choses pour l’approvisionnement de base :
- Préparez les rapports pour Azure (pour indiquer qu’ils ont été correctement exécutés).
- Renomme la machine virtuelle en fonction du nom de machine virtuelle fourni par l’utilisateur en extrayant ces données à partir d’Azure Instance Metadata Service (IMDS). Remarque IMDS fournit également d’autres métadonnées d’instance, telles que des clés publiques SSH, pour vous permettre de définir davantage le nom d’hôte.
- Se désactive pour s’exécuter uniquement au premier démarrage et non pas lors des redémarrages suivants.
Avec l’unité sur le système de fichiers, exécutez la commande suivante pour l’activer :
$ sudo systemctl enable azure-provisioning.service
La machine virtuelle est maintenant prête à être généralisée et une image est créée à partir de celle-ci.
Fin de la préparation de l’image
De retour sur votre ordinateur de développement, exécutez la commande suivante pour préparer la création d’images à partir de la machine virtuelle de base :
$ az vm deallocate --resource-group demo1 --name demo1
$ az vm generalize --resource-group demo1 --name demo1
Et créez l’image à partir de cette machine virtuelle :
$ az image create \
--resource-group demo1 \
--source demo1 \
--location eastus \
--name demo1img
Nous sommes maintenant prêts à créer une machine virtuelle à partir de l’image. Cela peut également être utilisé pour créer plusieurs machines virtuelles :
$ 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
Notes
Il est important de définir --enable-agent
sur false
, car walinuxagent n’existe pas sur la machine virtuelle qui va être créée à partir de l’image.
La machine virtuelle doit être approvisionnée correctement. Après vous être connecté à la machine virtuelle qui vient d’être approvisionnée, vous devez pouvoir consulter la sortie du service système « rapport prêt » :
$ 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.
Support
Si vous implémentez votre propre code/agent de provisionnement, vous possédez la prise en charge de ce code, le support technique de Microsoft n’examinera que les problèmes liés aux interfaces d’approvisionnement non disponibles. Nous apportons continuellement des améliorations et des modifications dans ce domaine. Par conséquent, vous devez surveiller les modifications apportées à cloud-init et à l’agent Linux Azure concernant l’approvisionnement des modifications de l’API.
Étapes suivantes
Pour plus d’informations, consultez Provisionnement Linux.