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 stát, že pro agenta zřizování nechcete použít některou z těchto aplikací, například:

  • Vaše distribuce nebo verze Linuxu nepodporuje agenta cloud-init/Linux.
  • Vyžadujete, aby byly nastaveny konkrétní vlastnosti virtuálního počítače, například název hostitele.

Poznámka

Pokud nevyžadujete, aby se nastavily žádné vlastnosti ani žádná 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 platformy Azure, a nastavit název hostitele bez instalace agenta zřizování.

Příprava sítí a vytváření sestav

Aby virtuální počítač s Linuxem mohl komunikovat se součástmi 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 předefinované. Mezi nástroje, které dodavatelé distribuce Linuxu testují v Azure, patří dhclient, network-managersystemd-networkd a další.

Poznámka

Aktuálně 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á". Azure tím sdělíte, že virtuální počítač byl úspěšně zřízen.

Důležité

Pokud se nenahlásíte připravený do Azure, bude se váš virtuální počítač restartovat.

Ukázka nebo ukázka

Nejjednodušším způsobem, jak Azure říct, že je virtuální počítač připravený, je existující image Marketplace (v tomto případě virtuální počítač Debian Buster) s odebraným agentem pro Linux (walinuxagent) a přidaným vlastním skriptem Pythonu.

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 pro Linux:

$ 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, který sestavu připraví.

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ý postup (pokud nepoužíváte Python nebo Bash)

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

  1. ContainerIdNačtěte , InstanceIda Incarnation 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 a vložte parsované 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. Publikuje tato data na 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

Automatizace spuš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 nativenějším způsobem, jak zajistit, aby se tento mechanismus připravený na sestavy spustil ve správný čas, je vytvořit jednotku systémové služby. Do souboru 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á pro základní zřizování tři věci:

  1. Sestavy připravené do Azure (označující, že se úspěšně objevily)
  2. Přejmenuje virtuální počítač na základě názvu virtuálního počítače zadaného uživatelem načtením těchto dat ze služby Azure Instance Metadata Service (IMDS). Poznámka IMDS také poskytuje další metadata instancí, jako jsou veřejné klíče SSH, takže můžete nastavit více než název hostitele.
  3. Zakáže sám sebe, takže se spustí 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ý ke generalizaci a má z něj vytvořenou image.

Dokončení přípravy obrázku

Na počítači pro vývoj spusťte následující příkaz, abyste se připravili 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 z image nový virtuální počítač. 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

Nastavení na hodnotu je důležité, --enable-agentfalse protože walinuxagent na tomto virtuálním počítači, který se vytvoří z image, neexistuje.

Virtuální počítač by se měl úspěšně zřídit. Po přihlášení k nově zřizovací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.

Podpora

Pokud implementujete vlastní zřizovací kód nebo agenta, pak vlastníte podporu tohoto kódu, podpora Microsoftu bude zkoumat pouze problémy související se zřizovacími rozhraními, která nejsou k dispozici. Neustále tuto oblast vylepšujeme a měníme, takže musíte monitorovat změny v cloud-init a v agentovi Azure pro Linux, abyste mohli zřizovat změny rozhraní API.

Další kroky

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