Tworzenie uogólnionych obrazów bez agenta aprowizacji
Dotyczy: ✔️ Maszyny wirtualne z systemem Linux — elastyczne zestawy skalowania ✔️
Platforma Microsoft Azure udostępnia agentów aprowizacji dla maszyn wirtualnych z systemem Linux w postaci walinuxagent lub cloud-init (zalecane). Może jednak wystąpić scenariusz, w którym nie chcesz używać żadnej z tych aplikacji dla agenta aprowizacji, na przykład:
- Twoja dystrybucja/wersja systemu Linux nie obsługuje agenta cloud-init/Linux.
- Należy ustawić określone właściwości maszyny wirtualnej, takie jak nazwa hosta.
Uwaga
Jeśli nie musisz ustawiać żadnych właściwości ani jakiejkolwiek formy aprowizacji, rozważ utworzenie wyspecjalizowanego obrazu.
W tym artykule pokazano, jak skonfigurować obraz maszyny wirtualnej w celu spełnienia wymagań platformy Azure i ustawienia nazwy hosta bez instalowania agenta aprowizacji.
Gotowość do obsługi sieci i raportowania
Aby maszyna wirtualna z systemem Linux komunikowała się ze składnikami platformy Azure, wymagany jest klient DHCP. Klient jest używany do pobierania adresu IP hosta, rozpoznawania nazw DNS i zarządzania trasami z sieci wirtualnej. Większość dystrybucji dostarcza te narzędzia gotowe do użycia. Narzędzia testowane na platformie Azure przez dostawców dystrybucji systemu Linux obejmują dhclient
, network-manager
systemd-networkd
i inne.
Uwaga
Obecnie tworzenie uogólnionych obrazów bez agenta aprowizacji obsługuje tylko maszyny wirtualne z obsługą protokołu DHCP.
Po skonfigurowaniu i skonfigurowaniu sieci wybierz pozycję "Raport gotowy". Informuje ona platformę Azure, że maszyna wirtualna została pomyślnie aprowizowana.
Ważne
Nie można zgłosić gotowości na platformę Azure, co spowoduje ponowne uruchomienie maszyny wirtualnej.
Pokaz/przykład
Istniejący obraz witryny Marketplace (w tym przypadku maszyna wirtualna Debian Buster) z agentem systemu Linux (walinuxagent) został usunięty, a dodany niestandardowy skrypt języka Python jest najprostszym sposobem na powiedzenie platformie Azure, że maszyna wirtualna jest "gotowa".
Utwórz grupę zasobów i podstawową maszynę wirtualną:
$ az group create --location eastus --name demo1
Utwórz podstawową maszynę wirtualną:
$ 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"
Usuwanie agenta aprowizacji obrazów
Po aprowizacji maszyny wirtualnej możesz nawiązać z nią połączenie za pośrednictwem protokołu SSH i usunąć agenta systemu Linux:
$ sudo apt purge -y waagent
$ sudo rm -rf /var/lib/waagent /etc/waagent.conf /var/log/waagent.log
Dodawanie wymaganego kodu do maszyny wirtualnej
Ponadto wewnątrz maszyny wirtualnej, ponieważ usunęliśmy agenta systemu Linux platformy Azure, musimy udostępnić mechanizm gotowy do raportowania.
Skrypt języka 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()
Skrypt powłoki 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
Ogólne kroki (jeśli nie używasz języka Python lub powłoki Bash)
Jeśli maszyna wirtualna nie ma zainstalowanego lub dostępnego języka Python, możesz programowo odtworzyć tę powyżej logikę skryptu, wykonując następujące czynności:
Pobierz element
ContainerId
,InstanceId
iIncarnation
, przeanalizuj odpowiedź z serwera WireServer:curl -X GET -H 'x-ms-version: 2012-11-30' http://168.63.129.16/machine?comp=goalstate
.Skonstruuj następujące dane XML, wstrzykiwając przeanalizowane
ContainerId
wartości ,InstanceId
iIncarnation
z powyższego kroku:<Health> <GoalStateIncarnation>INCARNATION</GoalStateIncarnation> <Container> <ContainerId>CONTAINER_ID</ContainerId> <RoleInstanceList> <Role> <InstanceId>INSTANCE_ID</InstanceId> <Health> <State>Ready</State> </Health> </Role> </RoleInstanceList> </Container> </Health>
Opublikuj te dane w usłudze 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
Automatyzowanie uruchamiania kodu podczas pierwszego rozruchu
W tym pokazie użyto systemd, który jest najczęściej używanym systemem init w nowoczesnych dystrybucjach systemu Linux. Najprostszym i najbardziej natywnym sposobem zapewnienia, że ten mechanizm gotowy do raportowania działa w odpowiednim czasie, jest utworzenie systemowej jednostki usługi. Do pliku jednostkowego można dodać następujący plik /etc/systemd/system
jednostkowy (w tym przykładzie nazwano plik azure-provisioning.service
jednostkowy ):
[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
Ta usługa systemowa wykonuje trzy czynności na potrzeby podstawowej aprowizacji:
- Raporty gotowe na platformę Azure (aby wskazać, że zostały pomyślnie wyświetlone).
- Zmienia nazwę maszyny wirtualnej na podstawie nazwy maszyny wirtualnej dostarczonej przez użytkownika, ściągając te dane z usługi Azure Instance Metadata Service (IMDS). Uwaga Usługa IMDS udostępnia również inne metadane wystąpienia, takie jak klucze publiczne SSH, dzięki czemu można ustawić więcej niż nazwa hosta.
- Wyłącza się tak, aby był uruchamiany tylko podczas pierwszego rozruchu, a nie podczas kolejnych ponownych uruchomień.
W przypadku lekcji w systemie plików uruchom następujące polecenie, aby je włączyć:
$ sudo systemctl enable azure-provisioning.service
Teraz maszyna wirtualna jest gotowa do uogólnień i ma utworzony na jej podstawie obraz.
Kończenie przygotowywania obrazu
Po powrocie do maszyny deweloperów uruchom następujące polecenie, aby przygotować się do utworzenia obrazu z podstawowej maszyny wirtualnej:
$ az vm deallocate --resource-group demo1 --name demo1
$ az vm generalize --resource-group demo1 --name demo1
Utwórz obraz na podstawie tej maszyny wirtualnej:
$ az image create \
--resource-group demo1 \
--source demo1 \
--location eastus \
--name demo1img
Teraz możemy utworzyć nową maszynę wirtualną na podstawie obrazu. Można to również użyć do utworzenia wielu maszyn wirtualnych:
$ 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
Uwaga
Należy ustawić wartość --enable-agent
na false
, ponieważ walinuxagent nie istnieje na tej maszynie wirtualnej, która ma zostać utworzona na podstawie obrazu.
Maszyna wirtualna powinna zostać pomyślnie aprowizowana. Po zalogowaniu się do nowo aprowizowanej maszyny wirtualnej powinno być możliwe wyświetlenie danych wyjściowych usługi gotowej do obsługi raportów:
$ 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.
Pomoc techniczna
Jeśli zaimplementujesz własny kod/agent aprowizacji, wówczas jesteś właścicielem tego kodu, pomoc techniczna firmy Microsoft zbada tylko problemy związane z interfejsami aprowizacji, które nie są dostępne. Nieustannie wprowadzamy ulepszenia i zmiany w tym obszarze, dlatego należy monitorować zmiany w programie cloud-init i azure Linux Agent na potrzeby aprowizacji zmian interfejsu API.
Następne kroki
Aby uzyskać więcej informacji, zobacz Aprowizowanie systemu Linux.