Vytváření generalizovaných imagí bez agenta zřizování

Platí pro: ✔️ Flexibilní škálovací sady virtuálních počítačů s Linuxem ✔️

Microsoft Azure poskytuje agenty zřizování pro virtuální počítače s Linuxem ve formě walinuxagent nebo cloud-init (doporučeno). Může se ale jednat o scénář, kdy nechcete pro svého zřizovacího agenta používat některou z těchto aplikací, například:

  • Distribuce nebo verze Linuxu nepodporuje cloud-init nebo linuxový agent.
  • Vyžadujete, aby se nastavily konkrétní vlastnosti virtuálního počítače, například název hostitele.

Poznámka:

Pokud nevyžadujete, aby se nastavily žádné vlastnosti nebo aby se stala nějaká forma zřizování, měli byste zvážit vytvoření specializované image.

Tento článek ukazuje, jak můžete nastavit image virtuálního počítače tak, aby splňovala požadavky na platformu Azure, a nastavit název hostitele bez instalace agenta zřizování.

Sítě a vytváření sestav připravené

Aby mohl virtuální počítač s Linuxem komunikovat s komponentami Azure, vyžaduje se klient DHCP. Klient se používá k načtení IP adresy hostitele, překladu DNS a správy tras z virtuální sítě. Většina distribucí se dodává s těmito nástroji, které jsou před sebou. Mezi nástroje, které jsou testovány v Azure dodavateli distribucí Linuxu, systemd-networkd patří dhclient, network-managera další.

Poznámka:

V současné době vytváření generalizovaných imagí bez agenta zřizování podporuje pouze virtuální počítače s podporou DHCP.

Po nastavení a konfiguraci sítě vyberte "Sestava připravená". To říká Azure, že se virtuální počítač úspěšně zřídil.

Důležité

Pokud se nepodaří nahlásit sestavu připravenou do Azure, dojde k restartování virtuálního počítače.

Ukázka nebo ukázka

Existující image Marketplace (v tomto případě virtuální počítač Debian Buster) s odebraným agentem Linuxu (walinuxagent) a přidaným vlastním skriptem Pythonu je nejjednodušší způsob, jak azure říct, že je virtuální počítač připravený.

Vytvořte skupinu prostředků a základní virtuální počítač:

$ az group create --location eastus --name demo1

Vytvořte základní virtuální počítač:

$ 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"

Odebrání agenta zřizování imagí

Po zřízení virtuálního počítače se k němu můžete připojit přes SSH a odebrat agenta Linuxu:

$ sudo apt purge -y waagent
$ sudo rm -rf /var/lib/waagent /etc/waagent.conf /var/log/waagent.log

Přidání požadovaného kódu do virtuálního počítače

Také uvnitř virtuálního počítače, protože jsme odebrali agenta Azure Pro Linux, potřebujeme poskytnout mechanismus pro vytvoření sestavy.

Skript jazyka 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()

Skript 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

Obecné kroky (pokud nepoužíváte Python nebo Bash)

Pokud váš virtuální počítač nemá nainstalovaný nebo dostupný Python, můžete tuto logiku skriptu programově reprodukovat pomocí následujících kroků:

  1. ContainerIdNačtěte a InstanceIdIncarnation parsováním odpovědi z WireServeru: curl -X GET -H 'x-ms-version: 2012-11-30' http://168.63.129.16/machine?comp=goalstate.

  2. Vytvořte následující data XML, vložení analyzované ContainerId, InstanceIda Incarnation z výše uvedeného 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>
    
  3. Publikujte tato data do WireServeru: 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

Automatizace spouštění kódu při prvním spuštění

Tato ukázka používá systemd, což je nejběžnější inicializační systém v moderních distribucích Linuxu. Nejjednodušším a nej nativním způsobem, jak zajistit, aby se tento mechanismus připravený k sestavě spustil ve správný čas, je vytvořit systémovou jednotku služby. Do souboru jednotky můžete přidat následující soubor /etc/systemd/system jednotky (tento příklad pojmenuje soubor azure-provisioning.servicejednotky):

[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

Tato systémová služba dělá tři věci pro základní zřizování:

  1. Sestavy připravené do Azure (aby bylo možné označit, že se úspěšně objevilo).
  2. Přejmenuje virtuální počítač na základě názvu virtuálního počítače zadaného uživatelem tak, že načte tato data ze služby Azure Instance Metadata Service (IMDS). Poznámka IMDS také poskytuje další metadata instance, jako jsou veřejné klíče SSH, takže můžete nastavit více než název hostitele.
  3. Zakáže se tak, aby běžel pouze při prvním spuštění, a ne při následných restartováních.

S jednotkou v systému souborů ji povolte spuštěním následujícího příkazu:

$ sudo systemctl enable azure-provisioning.service

Teď je virtuální počítač připravený k generalizaci a vytvoří se z ní image.

Dokončení přípravy image

Zpátky na vývojovém počítači spusťte následující kroky a připravte se na vytvoření image ze základního virtuálního počítače:

$ az vm deallocate --resource-group demo1 --name demo1
$ az vm generalize --resource-group demo1 --name demo1

A vytvořte image z tohoto virtuálního počítače:

$ az image create \
    --resource-group demo1 \
    --source demo1 \
    --location eastus \
    --name demo1img

Teď jsme připraveni vytvořit nový virtuální počítač z image. Můžete ho také použít k vytvoření několika virtuálních počítačů:

$ 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

Poznámka:

Je důležité nastavit --enable-agent false , aby na tomto virtuálním počítači, který se vytvoří z image, neexistuje walinuxagent.

Virtuální počítač by se měl úspěšně zřídit. Po přihlášení k nově zřízenému virtuálnímu počítači byste měli být schopni zobrazit výstup systémové služby připravené pro sestavy:

$ 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.

Technická podpora

Pokud implementujete vlastní zřizovací kód nebo agenta, vlastníte podporu tohoto kódu, podpora Microsoftu prošetří pouze problémy související s nedostupnou zřizovacím rozhraním. V této oblasti neustále vylepšujeme a měníme, takže je nutné monitorovat změny v cloud-init a agenta Azure Linuxu pro zřizování změn rozhraní API.

Další kroky

Další informace najdete v tématu Zřizování Linuxu.