Skapa generaliserade avbildningar utan en etableringsagent
Gäller för: ✔️ Flexibla skalningsuppsättningar för virtuella Linux-datorer ✔️
Microsoft Azure tillhandahåller etableringsagenter för virtuella Linux-datorer i form av walinuxagent eller cloud-init (rekommenderas). Men det kan finnas ett scenario när du inte vill använda något av dessa program för din etableringsagent, till exempel:
- Din Linux-distribution/-version stöder inte cloud-init/Linux-agenten.
- Du måste ange specifika VM-egenskaper, till exempel värdnamn.
Kommentar
Om du inte kräver att några egenskaper anges eller om någon form av etablering sker bör du överväga att skapa en specialiserad avbildning.
Den här artikeln visar hur du kan konfigurera den virtuella datoravbildningen så att den uppfyller kraven för Azure-plattformen och ange värdnamnet, utan att installera en etableringsagent.
Nätverks- och rapporteringsklar
För att din virtuella Linux-dator ska kunna kommunicera med Azure-komponenter krävs en DHCP-klient. Klienten används för att hämta en värd-IP, DNS-matchning och routningshantering från det virtuella nätverket. De flesta distributioner levereras med dessa verktyg out-of-the-box. Verktyg som testas på Azure av Linux-distributionsleverantörer är dhclient
, network-manager
systemd-networkd
och andra.
Kommentar
För närvarande har generaliserade avbildningar utan en etableringsagent endast stöd för DHCP-aktiverade virtuella datorer.
När nätverk har konfigurerats väljer du "rapportklar". Detta talar om för Azure att den virtuella datorn har etablerats.
Viktigt!
Om du inte rapporterar redo till Azure kommer den virtuella datorn att startas om!
Demo/exempel
En befintlig Marketplace-avbildning (i det här fallet en virtuell Debian Buster-dator) med Linux-agenten (walinuxagent) borttagen och ett anpassat Python-skript som lagts till är det enklaste sättet att berätta för Azure att den virtuella datorn är "klar".
Skapa resursgruppen och den virtuella basdatorn:
$ az group create --location eastus --name demo1
Skapa den virtuella basdatorn:
$ 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"
Ta bort avbildningsetableringsagenten
När den virtuella datorn har etablerats kan du ansluta till den via SSH och ta bort Linux-agenten:
$ sudo apt purge -y waagent
$ sudo rm -rf /var/lib/waagent /etc/waagent.conf /var/log/waagent.log
Lägga till nödvändig kod till den virtuella datorn
Även i den virtuella datorn, eftersom vi har tagit bort Azure Linux-agenten måste vi tillhandahålla en mekanism för att rapportera redo.
Python-skript
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-skript
#!/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
Allmänna steg (om de inte använder Python eller Bash)
Om den virtuella datorn inte har Python installerat eller tillgängligt kan du programmatiskt återskapa det här ovanstående skriptlogik med följande steg:
ContainerId
Hämta ,InstanceId
ochIncarnation
genom att parsa svaret från WireServer:curl -X GET -H 'x-ms-version: 2012-11-30' http://168.63.129.16/machine?comp=goalstate
.Konstruera följande XML-data och mata in parsade
ContainerId
,InstanceId
ochIncarnation
från ovanstående steg:<Health> <GoalStateIncarnation>INCARNATION</GoalStateIncarnation> <Container> <ContainerId>CONTAINER_ID</ContainerId> <RoleInstanceList> <Role> <InstanceId>INSTANCE_ID</InstanceId> <Health> <State>Ready</State> </Health> </Role> </RoleInstanceList> </Container> </Health>
Publicera dessa data till 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
Automatisera körning av koden vid första starten
Den här demonstrationen använder systemd, vilket är det vanligaste init-systemet i moderna Linux-distributioner. Så det enklaste och mest inbyggda sättet att se till att den här rapportklara mekanismen körs vid rätt tidpunkt är att skapa en systembaserad tjänstenhet. Du kan lägga till följande enhetsfil /etc/systemd/system
i (i det här exemplet namnges enhetsfilen 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
Den här systemd-tjänsten gör tre saker för grundläggande etablering:
- Rapporter som är redo för Azure (för att indikera att det har kommit upp).
- Byter namn på den virtuella datorn baserat på namnet på den virtuella datorn som angetts av användaren genom att hämta dessa data från Azure Instance Metadata Service (IMDS). Obs! IMDS innehåller även andra instansmetadata, till exempel offentliga SSH-nycklar, så att du kan ange mer än värdnamnet.
- Inaktiverar sig själv så att den bara körs vid första starten och inte vid efterföljande omstarter.
Med enheten i filsystemet kör du följande för att aktivera det:
$ sudo systemctl enable azure-provisioning.service
Nu är den virtuella datorn redo att generaliseras och få en avbildning skapad från den.
Slutföra förberedelsen av bilden
Tillbaka på utvecklingsdatorn kör du följande för att förbereda för att skapa avbildningar från den virtuella basdatorn:
$ az vm deallocate --resource-group demo1 --name demo1
$ az vm generalize --resource-group demo1 --name demo1
Och skapa avbildningen från den här virtuella datorn:
$ az image create \
--resource-group demo1 \
--source demo1 \
--location eastus \
--name demo1img
Nu är vi redo att skapa en ny virtuell dator från avbildningen. Detta kan också användas för att skapa flera virtuella datorer:
$ 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
Kommentar
Det är viktigt att ange --enable-agent
till false
eftersom walinuxagent inte finns på den virtuella datorn som kommer att skapas från avbildningen.
Den virtuella datorn bör etableras korrekt. När du har loggat in på den nyligen etablerade virtuella datorn bör du kunna se utdata från den rapportklara systemtjänsten:
$ 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
Om du implementerar din egen etableringskod/agent äger du supporten för den här koden. Microsoft-supporten undersöker endast problem som rör att etableringsgränssnitten inte är tillgängliga. Vi gör kontinuerligt förbättringar och ändringar på det här området, så du måste övervaka ändringar i cloud-init och Azure Linux Agent för etablering av API-ändringar.
Nästa steg
Mer information finns i Linux-etablering.