مشاركة عبر


إنشاء صور معممة دون عامل تزويد

ينطبق على: ✔️ أجهزة Linux الظاهرية ✔️ مجموعات مقياس مرنة

يوفر Microsoft Azure عامل تزويد لأجهزة Linux الظاهرية في شكل walinuxagent أو cloud-init (مستحسن). ولكن قد يكون هناك سيناريو لا تريد فيه استخدام أي من هذه التطبيقات لعامل التزويد الخاص بك، مثل:

  • لا يدعم توزيعة/إصدار Linux cloud-init/Linux Agent.
  • تحتاج إلى تعيين خصائص VM محددة، مثل اسم المضيف.

إشعار

إذا كنت لا تحتاج إلى تعيين أي خصائص أو حدوث أي شكل من أشكال التزويد، فيجب عليك التفكير في إنشاء صورة مخصصة.

توضح هذه المقالة كيف يمكنك إعداد صورة الجهاز الظاهري لتلبية متطلبات النظام الأساسي ل Azure وتعيين اسم المضيف، دون تثبيت عامل توفير.

الشبكات وإبلاغ الجاهزية

لكي يتصل جهاز Linux الظاهري بمكونات Azure، يلزم وجود عميل DHCP. يتم استخدام العميل لاسترداد IP مضيف وتحليل DNS وإدارة المسار من الشبكة الظاهرية. تأتي معظم التوزيعات مع هذه الأدوات المساعدة غير التقليدية. تتضمن dhclientالأدوات التي يتم اختبارها على Azure من قبل موردي توزيعات Linux ، network-managersystemd-networkd و، وغيرها.

إشعار

حالياً، لا يدعم إنشاء صور معممة بدون عامل تزويد سوى الأجهزة الظاهرية التي تدعم DHCP.

بعد إعداد الشبكات وتكوينها، حدد "report ready". يخبر هذا Azure أنه تم توفير الجهاز الظاهري بنجاح.

هام

سيؤدي الفشل في إبلاغ الجاهزية إلى Azure إلى إعادة تشغيل الجهاز الظاهري!

عرض توضيحي/عينة

صورة Marketplace الموجودة (في هذه الحالة، Debian Buster VM) مع إزالة عامل Linux (walinuxagent) وإضافة برنامج نصي python مخصص هو أسهل طريقة لإخبار Azure أن الجهاز الظاهري "جاهز".

إنشاء مجموعة موارد وجهاز ظاهري أساسي:

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

إنشاء الجهاز الظاهري الأساسي:

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

إزالة عامل تزويد الصور

بمجرد توفير الجهاز الظاهري، يمكنك الاتصال به عبر SSH وإزالة عامل Linux:

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

إضافة التعليمة البرمجية المطلوبة إلى الجهاز الظاهري

أيضاً داخل الجهاز الظاهري، نظراً لأننا أزلنا عامل Azure Linux، فإننا نحتاج إلى توفير آلية لإبلاغ الجاهزية.

البرنامج النصي 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()

برنامج 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

خطوات عامة (إذا لم تكن تستخدم Python أو Bash)

إذا لم يكن Python مثبتاً أو متوفراً على الجهاز الظاهري الخاص بك، فيمكنك إعادة إنتاج منطق البرنامج النصي أعلاه برمجياً من خلال الخطوات التالية:

  1. استرداد ContainerId، وInstanceId، وIncarnation عن طريق تحليل الاستجابة من WireServer: curl -X GET -H 'x-ms-version: 2012-11-30' http://168.63.129.16/machine?comp=goalstate.

  2. قم ببناء بيانات XML التالية، وإدخال ContainerId وInstanceId وIncarnation التي تم تحليلها من الخطوة أعلاه:

    <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. انشر هذه البيانات إلى 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

أتمتة تشغيل التعليمات البرمجية عند التمهيد الأول

يستخدم هذا العرض التوضيحي systemd، وهو نظام init الأكثر شيوعاً في توزيعات Linux الحديثة. لذا فإن الطريقة الأسهل والأكثر واقعية لضمان تشغيل آلية إبلاغ الجاهزية في الوقت المناسب هي إنشاء وحدة خدمة systemd. يمكنك إضافة ملف الوحدة التالي إلى /etc/systemd/system (يسمى هذا المثال ملف الوحدة 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

تقوم الخدمة systemd بثلاثة أشياء للتزويد الأساسي:

  1. إبلاغ الجاهزية لـ Azure (للإشارة إلى ظهورها بنجاح).
  2. يعيد تسمية الجهاز الظاهري استناداً إلى اسم الجهاز الظاهري الذي يوفره المستخدم عن طريق سحب هذه البيانات من خدمة Azure Instance Metadata (IMDS). ملاحظة يوفر IMDS أيضاً بيانات تعريف مثيل أخرى، مثل المفاتيح العامة لـ SSH؛ حتى تتمكن من تعيين أكثر من اسم المضيف.
  3. يعطل نفسه بحيث يعمل فقط على التشغيل الأول لا على عمليات إعادة التشغيل اللاحقة.

باستخدام الوحدة الموجودة على نظام الملفات، قم بتشغيل ما يلي لتمكينها:

$ sudo systemctl enable azure-provisioning.service

الآن الجهاز الظاهري جاهز للتعميم والحصول على صورة تم إنشاؤها منه.

استكمال إعداد الصورة

مرة أخرى على جهاز التطوير الخاص بك، قم بتشغيل ما يلي للتحضير لإنشاء صورة من الجهاز الظاهري الأساسي:

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

وإنشاء الصورة من هذا الجهاز الظاهري:

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

الآن نحن مستعدون لإنشاء جهاز ظاهري جديد من الصورة. يمكن استخدام هذا أيضا لإنشاء أجهزة ظاهرية متعددة:

$ 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

إشعار

من المهم تعيين --enable-agent على false؛ لأن walinuxagent غير موجود على الجهاز الظاهري الذي سيتم إنشاؤه من الصورة.

يجب توفير الجهاز الظاهري بنجاح. بعد تسجيل الدخول إلى الجهاز الظاهري للتوفير حديثا، يجب أن تكون قادرا على رؤية إخراج الخدمة النظامية الجاهزة للتقرير:

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

يدعم

إذا قمت بتنفيذ التعليمة البرمجية/عامل التزويد الخاص بك، فأنت تملك دعم هذه التعليمة البرمجية، ولن يقوم دعم Microsoft إلا بالتحقيق في المشكلات المتعلقة بعدم توفر واجهات التزويد. نحن نعمل باستمرار على إجراء تحسينات وتغييرات في هذا المجال، لذلك يجب عليك مراقبة التغييرات في cloud-init وAzure Linux Agent لتوفير تغييرات واجهة برمجة التطبيقات.

الخطوات التالية

لمزيد من المعلومات، راجع تزويد Linux.