Gegeneraliseerde installatiekopieën maken zonder een inrichtingsagent
Van toepassing op: ✔️ Flexibele schaalsets voor Linux-VM's ✔️
Microsoft Azure biedt inrichtingsagents voor Linux-VM's in de vorm van walinuxagent of cloud-init (aanbevolen). Er kan echter een scenario zijn wanneer u geen van deze toepassingen wilt gebruiken voor uw inrichtingsagent, zoals:
- Uw Linux-distributie/-versie biedt geen ondersteuning voor cloud-init/Linux-agent.
- U moet specifieke VM-eigenschappen instellen, zoals hostnaam.
Notitie
Als u geen eigenschappen hoeft in te stellen of als er een vorm van inrichting moet plaatsvinden, kunt u overwegen een gespecialiseerde installatiekopieën te maken.
In dit artikel wordt beschreven hoe u uw VM-installatiekopie kunt instellen om te voldoen aan de vereisten van het Azure-platform en de hostnaam kunt instellen, zonder dat u een inrichtingsagent hoeft te installeren.
Netwerken en rapportage gereed
Als u uw Linux-VM wilt laten communiceren met Azure-onderdelen, is een DHCP-client vereist. De client wordt gebruikt om een host-IP, DNS-resolutie en routebeheer op te halen uit het virtuele netwerk. De meeste distributies verzenden met deze hulpprogramma's out-of-the-box. Hulpprogramma's die zijn getest op Azure door Linux-distributieleveranciers zijn onder anderedhclient
, network-manager
systemd-networkd
en andere.
Notitie
Het maken van gegeneraliseerde installatiekopieën zonder een inrichtingsagent ondersteunt momenteel alleen VM's met DHCP-functionaliteit.
Nadat netwerken zijn ingesteld en geconfigureerd, selecteert u 'Rapport gereed'. Dit vertelt Azure dat de VIRTUELE machine is ingericht.
Belangrijk
Als u klaar voor Azure niet rapporteert, wordt uw VIRTUELE machine opnieuw opgestart.
Demo/voorbeeld
Een bestaande Marketplace-installatiekopieën (in dit geval een Debian Buster-VM) met de Linux-agent (walinuxagent) verwijderd en een aangepast Python-script toegevoegd, is de eenvoudigste manier om Azure te laten weten dat de VIRTUELE machine 'gereed' is.
Maak de resourcegroep en de basis-VM:
$ az group create --location eastus --name demo1
Maak de basis-VM:
$ 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"
De inrichtingsagent voor installatiekopieën verwijderen
Zodra de VIRTUELE machine is ingericht, kunt u er verbinding mee maken via SSH en de Linux-agent verwijderen:
$ sudo apt purge -y waagent
$ sudo rm -rf /var/lib/waagent /etc/waagent.conf /var/log/waagent.log
Vereiste code toevoegen aan de VIRTUELE machine
Ook binnen de VIRTUELE machine, omdat we de Azure Linux-agent hebben verwijderd, moeten we een mechanisme bieden om gereed te rapporteren.
Python-script
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()
Bash-script
#!/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
Algemene stappen (als u Python of Bash niet gebruikt)
Als op uw VM Python niet is geïnstalleerd of beschikbaar is, kunt u deze bovenstaande scriptlogica programmatisch reproduceren met de volgende stappen:
Haal de
ContainerId
,InstanceId
enIncarnation
door het antwoord van wireserver te parseren:curl -X GET -H 'x-ms-version: 2012-11-30' http://168.63.129.16/machine?comp=goalstate
.Maak de volgende XML-gegevens, injecteer de geparseerde
ContainerId
enInstanceId
Incarnation
uit de bovenstaande stap:<Health> <GoalStateIncarnation>INCARNATION</GoalStateIncarnation> <Container> <ContainerId>CONTAINER_ID</ContainerId> <RoleInstanceList> <Role> <InstanceId>INSTANCE_ID</InstanceId> <Health> <State>Ready</State> </Health> </Role> </RoleInstanceList> </Container> </Health>
Plaats deze gegevens op 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
Het uitvoeren van de code bij het eerste opstarten automatiseren
Deze demo maakt gebruik van systemd, het meest voorkomende init-systeem in moderne Linux-distributies. De eenvoudigste en meest systeemeigen manier om ervoor te zorgen dat dit rapportklare mechanisme op het juiste moment wordt uitgevoerd, is door een systeemservice-eenheid te maken. U kunt het volgende eenheidsbestand /etc/systemd/system
toevoegen (in dit voorbeeld wordt het eenheidsbestand azure-provisioning.service
genoemd):
[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
Deze systeemservice doet drie dingen voor basisinrichting:
- Rapporten die gereed zijn voor Azure (om aan te geven dat het is gelukt).
- Wijzigt de naam van de VIRTUELE machine op basis van de door de gebruiker opgegeven VM-naam door deze gegevens op te halen uit Azure Instance Metadata Service (IMDS). Opmerking IMDS biedt ook andere metagegevens van exemplaren, zoals openbare SSH-sleutels, zodat u meer kunt instellen dan de hostnaam.
- Schakelt zichzelf uit zodat deze alleen wordt uitgevoerd bij de eerste opstartbewerking en niet bij volgende herstarts.
Voer met de eenheid in het bestandssysteem het volgende uit om deze in te schakelen:
$ sudo systemctl enable azure-provisioning.service
De VIRTUELE machine is nu klaar om te worden gegeneraliseerd en er een installatiekopieën van gemaakt.
De voorbereiding van de afbeelding voltooien
Voer op uw ontwikkelcomputer het volgende uit om het maken van installatiekopieën vanaf de basis-VM voor te bereiden:
$ az vm deallocate --resource-group demo1 --name demo1
$ az vm generalize --resource-group demo1 --name demo1
En maak de installatiekopieën van deze VM:
$ az image create \
--resource-group demo1 \
--source demo1 \
--location eastus \
--name demo1img
Nu zijn we klaar om een nieuwe VIRTUELE machine te maken op basis van de installatiekopieën. Dit kan ook worden gebruikt om meerdere VM's te maken:
$ 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
Notitie
Het is belangrijk om in te stellen --enable-agent
false
op omdat walinuxagent niet bestaat op deze VM die wordt gemaakt op basis van de installatiekopieën.
De VIRTUELE machine moet worden ingericht. Nadat u zich hebt aangemeld bij de nieuwe inrichtings-VM, moet u de uitvoer van de rapportklare systeemservice kunnen zien:
$ 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.
Ondersteuning
Als u uw eigen inrichtingscode/agent implementeert, bent u eigenaar van de ondersteuning van deze code. Microsoft-ondersteuning onderzoekt alleen problemen met betrekking tot de inrichtingsinterfaces die niet beschikbaar zijn. We brengen voortdurend verbeteringen en wijzigingen in dit gebied aan, dus u moet controleren op wijzigingen in cloud-init en Azure Linux Agent voor het inrichten van API-wijzigingen.
Volgende stappen
Zie Linux-inrichting voor meer informatie.