اذهب إلى المحتوى

أتمتة إعداد الخادم المسبق باستخدام ملفات البيان Manifests لأداة Puppet


Ola Abbas

تُعَد إدارة ضبط الخادم (يُشار إليها أيضًا باسم أتمتة تقانة المعلومات IT Automation) حلًا لتحويل إدارة بنيتك التحتية إلى الشيفرة البرمجية الأساسية، ولوصف جميع العمليات اللازمة لنشر خادم في مجموعة من سكربتات الإعداد المسبق Provisioning Scripts التي يمكن إصدارها وإعادة استخدامها بسهولة، ويمكنها تحسين التكامل لأيّ بنية خادم تحتية بصورة كبيرة بمرور الوقت.

تحدثنا في مقال سابق عن الفوائد الرئيسية لتنفيذ إستراتيجية إدارة الضبط لبنية الخادم التحتية، وكيفية عمل أدوات إدارة الضبط والعناصر المشتركة بين هذه الأدوات. سنوضح في هذا المقال عملية أتمتة إعداد الخادم المسبق باستخدام الأداة Puppet، وهي أداة شائعة لإدارة الضبط قادرة على إدارة البنية التحتية المعقدة بطريقة شفافة باستخدام خادم رئيسي Master لتنسيق ضبط العقد، وسنركز على مصطلحات اللغة والصياغة والميزات اللازمة لإنشاء مثال مبسط للأتمتة الكاملة لنشر خادم ويب Ubuntu 18.04 باستخدام أباتشي Apache.

تحتوي القائمة التالية على جميع الخطوات التي نحتاجها للأتمتة حتى الوصول إلى هدفنا:

  1. حدّث ذاكرة apt المخبئية.
  2. ثبّت خادم أباتشي Apache.
  3. أنشئ مجلد المستند الجذر المُخصَّص.
  4. ضع ملف index.html في المستند الجذر المخصص.
  5. طبّق قالبًا لإعداد المضيف الوهمي المخصص.
  6. أعِد تشغيل أباتشي.

سنبدأ بإلقاء نظرة على المصطلحات التي تستخدمها أداة Puppet، ثم سنتعرّف على ميزات اللغة الرئيسية التي يمكن استخدامها لكتابة ملفات البيان Manifests، وسنشاركك في النهاية المثال الكامل لتتمكّن من تجربته بنفسك.

ملاحظة: يهدف هذا المقال إلى تعريفك بلغة Puppet وكيفية كتابة ملفات البيان لأتمتة إعداد خادمك المسبق. اطّلع على تثبيت الأداة Puppet لإدارة بنية الخوادم التحتية لمعرفة الخطوات اللازمة لتثبيت أداة Puppet والبدء باستخدامها.

هذا المقال جزء من سلسلة حول إدارة ضبط الخوادم، وإليك روابط فصول السلسلة:

البدء باستخدام الأداة Puppet

يجب أن نتعرف أولًا على المصطلحات والمفاهيم المهمة التي قدمتها أداة Puppet قبل البدء بالعمل.

مصطلحات Puppet

تحتوي القائمة التالية على أهم المصطلحات التي تستخدمها أداة Puppet:

  • جهاز Puppet الرئيسي Master: الخادم الرئيسي الذي يتحكم في الضبط على العقد.
  • عقدة الوكيل الخاصة بالأداة Puppet: عقدة يتحكم فيها جهاز Puppet الرئيسي.
  • ملف البيان Manifest: ملف يحتوي على مجموعة من التعليمات لتنفيذها.
  • المورد Resource: جزء الشيفرة البرمجية الذي يصرّح عن عنصر في النظام وكيفية تغيير حالته، فمثلًا يجب لتثبيت حزمة تحديدُ المورد package والتأكد من ضبط حالته على أنه "مُثبَّت".
  • الوحدة Module: مجموعة من ملفات البيان والملفات الأخرى ذات الصلة المنظمة بطريقة مُعرَّفة مسبقًا لتسهيل مشاركة وإعادة استخدام أجزاء من الإعداد المسبق.
  • الصنف Class: تُستخدَم الأصناف في الأداة Puppet لتنظيم الإعداد المسبق بصورة أفضل وتسهيل إعادة استخدام أجزاء من الشيفرة البرمجية كما هو الحال مع لغات البرمجة العادية.
  • الحقائق Facts: المتغيرات العامة التي تحتوي على معلومات حول النظام مثل واجهات الشبكة ونظام التشغيل.
  • الخدمات Services: تُستخدَم لبدء تغييرات حالة الخدمة مثل إعادة تشغيل الخدمة أو إيقافها.

تُكتَب عمليات الإعداد المسبق الخاصة بالأداة Puppet باستخدام لغة المجال المحدَّد Domain Specific Language -أو DSL اختصارًا- المخصَّصة والتي تعتمد على لغة روبي Ruby.

الموارد Resources

تُعرَّف المهام أو الخطوات باستخدام الأداة Puppet من خلال التصريح عن الموارد، ويمكن أن تمثل الموارد الحزم والملفات والخدمات والمستخدمين والأوامر، ويكون لها حالة تؤدي إلى بدء تغيير النظام في حالة اختلاف حالة المورد المُصرَّح عنه عن حالته الحالية في النظام، فمثلًا سيؤدي ضبط المورد package على القيمة installed في ملف البيان إلى بدء تثبيت الحزمة على النظام إن لم مُثبَّتةً مسبقًا.

يكون مورد الحزمة package كما يلي:

package { 'nginx':
    ensure  => 'installed'
}

يمكنك تنفيذ أيّ أمر عشوائي من خلال التصريح عن المورد exec كما يلي:

exec { 'apt-get update':
    command => '/usr/bin/apt-get update'
}

لاحظ أن الجزء apt-get update في السطر الأول ليس التصريح عن الأمر الفعلي، بل هو معرِّفٌ لهذا المورد الفريد، إذ يجب في أغلب الأحيان الإشارة إلى موارد أخرى من موردٍ ما، ونستخدم معرّفها لذلك، حيث يكون المعرّف في حالتنا هو apt-get update، ولكن يمكن أن يكون أيّ سلسلة نصية أخرى.

اعتمادية الموارد Resource Dependency

يجب أن تضع في بالك أن الأداة Puppet لا تقيّم الموارد بترتيب تعريفها نفسه عند كتابة ملفات البيان، ويُعَد ذلك مصدر ارتباك شائع للأشخاص المبتدئين في استخدام الأداة Puppet، إذ يجب أن تعرِّف الموارد اعتماديتها بين بعضها البعض صراحةً، وإلّا فلن يكون هناك ضمان بشأن المورد الذي يجب تقييمه وتنفيذه أولًا.

لنفترض أنك تريد تنفيذ أمرٍ ما، ولكن يجب التأكد من تثبيت الاعتمادية أولًا كما يلي:

package { 'python-software-properties':
    ensure => 'installed'
}

exec { 'add-repository':
    command => '/usr/bin/add-apt-repository ppa:ondrej/php5 -y'
    require => Package['python-software-properties']
}

يأخذ الخيار require مرجعًا لمورد آخر بوصفه وسيطًا له، ونشير في هذه الحالة إلى مورد الحزمة Package المُعرَّف بوصفه python-software-properties. لاحظ أننا نستخدم exec و package وغير ذلك للتصريح عن الموارد (بأحرف صغيرة)، بينما نستخدم Exec و Package وغير ذلك (بأحرف كبيرة) عند الإشارة إلى الموارد المُعرَّفة مسبقًا.

لنفترض الآن أنه يجب التأكد من تنفيذ مهمةٍ قبل مهمة أخرى، حيث يمكننا استخدام الخيار before في هذه الحالة كما يلي:

package { 'curl':
    ensure => 'installed'
    before => Exec['install script']
}

exec { 'install script':
    command => '/usr/bin/curl http://example.com/some-script.sh'

تنسيق ملف البيان

تُعَد ملفات البيان مجموعة من تصريحات عن الموارد باستخدام الامتداد ‎.pp. يمكنك العثور فيما يلي على مثال لدليل تشغيل Playbook بسيط يؤدي مهمتين هما: تحديث ذاكرة apt المخبئية ثم تثبيت vim:

exec { 'apt-get update':
    command => '/usr/bin/apt-get update'
}

package { 'vim':
    ensure => 'installed'
    require => Exec['apt-get update']
}

سنرى لاحقًا مثالًا واقعيًا أكثر عن ملف البيان بالتفصيل، ولكن سيعطيك القسم التالي نظرة عامة على أهم العناصر والميزات التي يمكن استخدامها لكتابة ملفات بيان Puppet.

كتابة ملفات البيان

أصبحت الآن على دراية بالمصطلحات الأساسية والتنسيق العام لملف البيان في Puppet، لذا سنتعرف على بعض ميزات ملف البيان.

التعامل مع المتغيرات

يمكن تعريف المتغيرات في أي وقت في ملف البيان، وأكثر أنواع المتغيرات شيوعًا هي السلاسل النصية ومصفوفات السلاسل النصية، والأنواع الأخرى مدعومة أيضًا مثل القيم المنطقية والقيم المُعمَّاة المختصرة Hashes.

يعرّف المثال التالي متغير سلسلة نصية يُستخدَم لاحقًا ضمن موردٍ ما:

$package = "vim"

package { $package:
   ensure => "installed"
}

استخدام الحلقات Loops

تُستخدم الحلقات عادةً لتكرار مهمة باستخدام قيم دخل مختلفة، فمثلًا يمكنك إنشاء مهمة واحدة واستخدام حلقة لتكرار المهمة مع جميع الحزم المختلفة التي تريد تثبيتها بدلًا من إنشاء 10 مهام لتثبيت 10 حزم مختلفة. أبسط طريقة لتكرار مهمة بقيم مختلفة في Puppet هي باستخدام المصفوفات كما في المثال التالي:

$packages = ['vim', 'git', 'curl']

package { $packages:
   ensure => "installed"
}

تدعم الأداة Puppet من الإصدار 4 طرقًا إضافية لتكرار المهام، فمثلًا يطبّق المثال التالي الشيء نفسه الذي طبّقه المثال السابق، ولكن باستخدام المكرِّر each، حيث يمنحك هذا الخيار مزيدًا من المرونة لتكرار تعريفات الموارد:

$packages.each |String $package| {
  package { $package:
    ensure => "installed"
  }
}

استخدام التعليمات الشرطية

يمكن استخدام التعليمات الشرطية لتحديد ما إذا كان يجب تنفيذ كتلة من الشيفرة البرمجية أم لا ديناميكيًا بناءً على متغير أو خرج أمرٍ ما. تدعم الأداة Puppet معظم البنى الشرطية التي يمكنك العثور عليها باستخدام لغات البرمجة التقليدية مثل تعليمات if/else و case، وتدعم بعض الموارد مثل exec السمات التي تعمل بوصفها تعليمة شرطية، ولكنها تقبل فقط خرج الأمر بوصفه شرطًا.

لنفترض أنك تريد تنفيذ أمر بناءً على حقيقة Fact، إذ يجب في هذه الحالة استخدام أحد البنى الشرطية المدعومة مثل if/else عندما تريد اختبار قيمة متغير كما يلي:

if $osfamily != 'Debian' {
 warning('This manifest is not supported on this OS.')
}
else {
 notify { 'Good to go!': }
}

يوجد موقف شائع آخر عندما تريد وضع شرط لتنفيذ أمرٍ ما بناءً على خرج أمرٍ آخر، حيث يمكنك في هذه الحالة استخدام التعليمة onlyif أو unless كما في المثال الآتي، إذ لن يُنفَّذ هذا الأمر إلّا عندما يكون خرج ‎/bin/which php ناجحًا، أي أنّ الأمر ينتهي مع الحالة 0:

exec { "Test":
 command => "/bin/echo PHP is installed here > /tmp/test.txt",
 onlyif => "/bin/which php"
}

وبالمثل، سينفَّذ الأمر التعليمة unless في جميع الأوقات، إلّا عندما ينتهي الأمر الموجود ضمنها بنجاح:

exec { "Test":
 command => "/bin/echo PHP is NOT installed here > /tmp/test.txt",
 unless => "/bin/which php"
}

التعامل مع القوالب

تُستخدَم القوالب لإعداد ملفات الضبط، مما يسمح باستخدام المتغيرات والميزات الأخرى التي تهدف إلى جعل هذه الملفات أكثر تنوعًا وقابلية لإعادة الاستخدام. تدعم الأداة Puppet تنسيقين مختلفين للقوالب هما: قوالب Puppet المُضمَّنة Embedded Puppet -أو EPP اختصارًا- وقوالب روبي المُضمَّنة Embedded Ruby -أو ERB اختصارًا، ولكن يعمل تنسيق EPP فقط مع الإصدارات الحديثة من الأداة Puppet (بدءًا من الإصدار 4.0).

يوجد فيما يلي مثال على قالب ERB لإعداد مضيف أباتشي Apache الوهمي باستخدام متغير لإعداد المستند الجذر لهذا المضيف:

<VirtualHost *:80>
    ServerAdmin webmaster@localhost
    DocumentRoot <%= @doc_root %>

    <Directory <%= @doc_root %>>
        AllowOverride All
        Require all granted
    </Directory>
</VirtualHost>

يجب لتطبيق القالب إنشاء المورد file الذي يعرض محتوى القالب باستخدام التابع template، وإليك طريقة تطبيق هذا القالب لاستبدال مضيف أباتشي الوهمي الافتراضي:

file { "/etc/apache2/sites-available/000-default.conf":
    ensure => "present",
    content => template("apache/vhost.erb") 
} 

تضع الأداة Puppet بعض الافتراضات عند التعامل مع الملفات المحلية لفرض التنظيم والتقسيم إلى وحدات Modularity، حيث ستبحث الأداة Puppet عن ملف قالب vhost.erb ضمن المجلد apache/templates في مجلد وحداتك.

تعريف وبدء الخدمات

تُستخدَم موارد الخدمة service للتأكد من تهيئة الخدمات وتفعيلها، وتُستخدَم لبدء إعادة تشغيل الخدمة. لنأخذ مثالنا السابق لاستخدام القالب، حيث ضبطنا مضيف أباتشي الوهمي. إذا أردتَ التأكد من إعادة تشغيل أباتشي بعد تغيير المضيف الوهمي، فيجب أولًا إنشاء مورد خدمة service لخدمة أباتشي. إليك طريقة تعريف هذا المورد في Puppet:

service { 'apache2':
    ensure => running,
    enable => true
}

يجب الآن تضمين الخيار notify لبدء إعادة التشغيل عند تعريف المورد كما يلي:

file { "/etc/apache2/sites-available/000-default.conf":
    ensure => "present",
    content => template("vhost.erb"),
    notify => Service['apache2'] 
}

مثال عن ملف البيان

لنلقِ الآن نظرة على ملف البيان الذي سيؤدي إلى أتمتة عملية تثبيت خادم ويب أباتشي على نظام لينكس أوبنتو Ubuntu كما ناقشنا سابقًا. يمكنك العثور على المثال الكامل بما في ذلك ملف القالب لإعداد أباتشي وملف HTML ليخدّمه خادم الويب على Github، ويحتوي المجلد على الملف Vagrantfile الذي يتيح لك اختبار ملف البيان في إعداد مبسط باستخدام آلة افتراضية تديرها الأداة Vagrant.

إليك ملف البيان الكامل:

$doc_root = "/var/www/example"

exec { 'apt-get update':
  command => '/usr/bin/apt-get update'
}

package { 'apache2':
  ensure  => "installed",
  require => Exec['apt-get update']
}

file { $doc_root:
  ensure => "directory",
  owner => "www-data",
  group => "www-data",
  mode => 644
}

file { "$doc_root/index.html":
  ensure => "present",
  source => "puppet:///modules/main/index.html",
  require => File[$doc_root]
}

file { "/etc/apache2/sites-available/000-default.conf":
  ensure => "present",
  content => template("main/vhost.erb"),
  notify => Service['apache2'],
  require => Package['apache2']
}

service { 'apache2':
  ensure => running,
  enable => true
}

لنتعرّف على كل جزء من ملف البيان السابق بمزيد من التفصيل:

  • يبدأ ملف البيان في السطر الأول بتعريف المتغير ‎$doc_root الذي يُستخدَم لاحقًا في تعريف المورد.
  • ينفّذ المورد exec في الأسطر 3-5 الأمر apt-get update.
  • يثبّت مورد الحزمة package في الأسطر 7-10 الحزمة apache2 محدّدًا أن مورد apt-get update مطلوب، مما يعني أنه لن يُنفَّذ إلا بعد تقييم المورد المطلوب.
  • نستخدم مورد الملف file في الأسطر 12-17 لإنشاء مجلد جديد سيكون بمثابة المستند الجذر. يمكن استخدام المورد file لإنشاء مجلدات وملفات، ويُستخدَم لتطبيق القوالب ونسخ الملفات المحلية إلى الخادم البعيد، حيث يمكن تنفيذ هذه المهمة في أيّ مرحلة من الإعداد المسبق، لذلك لم نكن بحاجة إلى ضبط أيّ خيار require هنا.
  • نستخدم مورد ملف file آخر في الأسطر 19-23 لنسخ ملف index.html المحلي إلى المستند الجذر ضمن الخادم، حيث نستخدم المعامل source للسماح لأداة Puppet بمعرفة مكان العثور على الملف الأصلي. تعتمد هذه التسمية على الطريقة التي تتعامل بها Puppet مع الملفات المحلية، فإذا ألقيت نظرة على مستودع المثال على Github، فسترى كيفية إنشاء بنية المجلد للسماح لأداة Puppet بالعثور على هذا المورد، ويجب إنشاء مجلد المستند الجذر قبل تنفيذ هذا المورد، لذا ضمّنا الخيار require الذي يشير إلى المورد السابق.
  • يُستخدَم مورد ملف جديد في الأسطر 25-30 لتطبيق قالب أباتشي وإعلام الخدمة بإعادة التشغيل. نُظِّمت عملية الإعداد المسبق في وحدة تسمى main، لذا يكون مورد القالب هو main/vhost.erb. نستخدم التعليمة require للتأكد من أن مورد القالب template لا يُنفَّذ إلّا بعد تثبيت الحزمة apache2، وإلّا فلن تكون بنية المجلد التي يستخدمها أباتشي موجودة بعد.ص
  • يصرّح مورد الخدمة service في الأسطر 32-35 عن الخدمة apache2، والتي نعلِمها بإعادة التشغيل من المورد الذي يطبّق قالب المضيف الوهمي.

الخلاصة

تُعَد الأداة Puppet أداة قوية لإدارة الضبط وتستخدم لغة DSL مُخصَّصة ومعبّرة لإدارة موارد الخادم وأتمتة المهام، وتقدّم لغتها مواردًا متقدمة يمكن أن توفّر مرونة إضافية لضبط إعدادك المسبق، إذ يجب أن تتذكر أن الموارد لا تُقيَّم بترتيب تعريفها نفسه، لذا يجب توخي الحذر عند تحديد الاعتماديات بين الموارد لإنشاء سلسلة التنفيذ الصحيحة.

سنلقي نظرة على الأداة Chef في المقال التالي، وهي أداة قوية أخرى لإدارة الضبط وتستفيد من لغة برمجة روبي Ruby لأتمتة إدارة البنية التحتية والإعداد المسبق.

ترجمة -وبتصرُّف- للمقال Configuration Management 101: Writing Puppet Manifests لصاحبته Erika Heidi.

اقرأ أيضًا


تفاعل الأعضاء

أفضل التعليقات

لا توجد أية تعليقات بعد



انضم إلى النقاش

يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.

زائر
أضف تعليق

×   لقد أضفت محتوى بخط أو تنسيق مختلف.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   جرى استعادة المحتوى السابق..   امسح المحرر

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • أضف...