Bagikan melalui


Membuat citra umum tanpa agen provisi

Berlaku untuk: ✔️ VM Linux ✔️ Set skala fleksibel

Microsoft Azure menyediakan agen provisi untuk VM Linux dalam bentuk walinuxagent atau cloud-init (disarankan). Tetapi mungkin ada skenario ketika Anda tidak ingin menggunakan salah satu aplikasi ini untuk agen provisi Anda, seperti:

  • Distro/versi Linux Anda tidak mendukung cloud-init/Linux Agent.
  • Anda memerlukan properti VM tertentu untuk di set, seperti nama host.

Catatan

Jika Anda tidak memerlukan properti apa pun untuk di set atau tidak ingin membuat semua bentuk provisi, Anda harus mempertimbangkan untuk membuat citra khusus.

Artikel ini memperlihatkan bagaimana Anda dapat menyiapkan gambar VM untuk memenuhi persyaratan platform Azure dan mengatur nama host, tanpa menginstal agen provisi.

Jaringan dan pelaporan siap

Agar VM Linux Anda berkomunikasi dengan komponen Azure, klien DHCP diperlukan. Klien digunakan untuk mengambil IP host, resolusi DNS, dan manajemen rute dari jaringan virtual. Sebagian besar distro dikirim dengan utilitas di luar kotaknya. Alat yang diuji di Azure oleh vendor distro Linux termasuk dhclient, , network-managersystemd-networkd dan lainnya.

Catatan

Saat ini membuat citra umum tanpa agen provisi dan hanya mendukung VM dengan DHCP yang diaktifkan.

Setelah jaringan disiapkan dan dikonfigurasi, pilih "laporan siap". Ini memberi tahu Azure bahwa VM telah berhasil disediakan.

Penting

Gagal melapor siap ke Azure akan mengakibatkan VM Anda di-boot ulang!

Demo/sampel

Gambar Marketplace yang ada (dalam hal ini, Debian Buster VM) dengan Agen Linux (walinuxagent) dihapus dan skrip python kustom yang ditambahkan adalah cara termampu untuk memberi tahu Azure bahwa VM "siap".

Membuat grup sumber daya dan basis VM:

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

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

Hapus Agen provisi citra

Setelah VM diprovisikan, Anda dapat menyambungkannya melalui SSH dan menghapus Agen Linux:

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

Tambahkan kode yang diperlukan ke VM

Kami juga telah menghapus Azure Linux Agent di dalam VM. Oleh karena itu, kami perlu menyediakan mekanisme untuk melapor siap.

Skrip 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()

Skrip 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

Langkah-langkah umum (jika tidak menggunakan Python atau Bash)

Jika VM Anda tidak memiliki Python yang terpasang atau tersedia, Anda dapat mereproduksi logika skrip di atas ini secara terprogram dengan langkah-langkah berikut:

  1. Ambil ContainerId, InstanceId, dan Incarnation dengan menguraikan respons dari WireServer: curl -X GET -H 'x-ms-version: 2012-11-30' http://168.63.129.16/machine?comp=goalstate.

  2. Buat data XML berikut, masukkan ContainerId, InstanceId, dan Incarnation yang terurai dari langkah di atas:

    <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. Posting data ini ke 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

Mengotomatiskan kode agar berjalan pada boot pertama

Demo ini menggunakan systemd, yang merupakan sistem init paling umum dalam distro Linux modern. Jadi cara termudah dan paling orisinal untuk memastikan mekanisme melapor siap ini berjalan pada waktu yang tepat adalah dengan membuat unit layanan systemd. Anda dapat menambahkan file unit berikut ke /etc/systemd/system (contoh ini menamai file unit 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

Layanan systemd ini melakukan tiga hal untuk provisi dasar:

  1. Melapor siap ke Azure (untuk menunjukkan bahwa laporan berhasil muncul).
  2. Mengganti nama VM berdasarkan nama VM yang disediakan pengguna dengan menarik data ini dari Azure Instance Metadata Service (IMDS). Catatan IMDS juga menyediakan metadata instanslainnya, seperti Kunci umum SSH, sehingga Anda dapat set lebih dari sekedar nama host.
  3. Menonaktifkan sendiri sehingga sistem hanya berjalan pada boot pertama dan tidak pada pengulangan boot berikutnya.

Dengan unit pada filesystem, jalankan beberapa hal berikut ini untuk mengaktifkannya:

$ sudo systemctl enable azure-provisioning.service

Sekarang VM siap untuk digenaralisasi dan memiliki citra yang terbuat dari pengaturan tersebut.

Menyelesaikan persiapan citra

Kembali pada mesin pengembangan Anda, jalankan hal berikut untuk mempersiapkan pembuatan citra dari VM dasar:

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

Dan buat citra dari VM ini:

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

Sekarang kita siap untuk membuat VM baru dari gambar. Ini juga dapat digunakan untuk membuat beberapa VM:

$ 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

Catatan

Pengaturan ini penting --enable-agent karena false walinuxagent tidak ada pada VM ini dan akan dibuat dari citra.

VM harus berhasil disediakan. Setelah Masuk ke VM yang baru diprovisikan, Anda akan dapat melihat output dari layanan systemd siap laporan:

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

Dukungan

Jika Anda menerapkan kode/agen provisi Anda sendiri, maka Anda memiliki dukungan kode ini, dukungan Microsoft hanya akan menginvestigasi masalah yang berkaitan dengan antarmuka provisi yang tidak tersedia. Kami terus melakukan penyempurnaan dan perubahan di area ini, jadi Anda harus memantau perubahan cloud-init dan Azure Linux Agent untuk memprovisikan perubahan API.

Langkah berikutnya

Untuk informasi selengkapnya, lihat Provisi Linux.