ملفات Playbook
ملفات Playbook
إذا كانت الأوامر المباشرة (ad-hoc) هي مفك البراغي في Ansible، فملفات Playbook هي مخطط البنية المعمارية. الـ playbook هو ملف YAML يُعلن عن ما يجب أن يكون صحيحاً على مجموعة من المضيفين — أي الحزم مثبّتة، أي الخدمات تعمل، وأي ملفات الإعداد تحتوي على أي محتوى. تقرأ Ansible الـ playbook وتدفع المضيفين نحو تلك الحالة بشكل متكرر ومرتّب. في شركات مثل Stripe وCloudflare وShopify، تُرمّز الـ playbooks عقوداً من الخبرة التشغيلية ويمكنها إحضار أسطول كامل من الأجهزة الخام إلى حالة الإعداد الكاملة في دقائق.
يغطي هذا الدرس العناصر الأربعة البنيوية التي تُشكّل كل playbook احترافية: المسرحيات (plays) والمهام (tasks) والمعالجات (handlers) وتوجيه notify الذي يربطها. أتقن هذه العناصر الأربعة ولديك النموذج الذهني لكل ما تفعله Ansible.
تشريح الـ Playbook: الـ Play
الـ playbook هو قائمة من الـ plays. كل play يُعيّن مجموعة من المضيفين إلى مجموعة من المهام ويحدد سياق التنفيذ لتلك المهام. يمكن أن يحتوي الـ playbook على play واحد أو خمسين — كل play يعمل بالتسلسل، وجميع المضيفين في play واحد يعملون بالتوازي (حتى حد forks، الافتراضي 5).
المفاتيح الأساسية لأي play:
name— تسمية مقروءة بشرياً؛ تظهر في المخرجات وهي أفضل توثيق لما يفعله الـ play.hosts— نمط المخزون (اسم مجموعة، glob،all، أو قائمة مفصولة بفواصل).become— الترقي إلى root عبر sudo للـ play بأكمله. يمكن تجاوزه لكل مهمة.gather_facts— الافتراضيtrue؛ يجمع معلومات النظام (النظام، IP، الذاكرة) قبل تشغيل المهام. عطّله بـfalseعندما لا تُستخدم المعلومات وتحتاج السرعة.vars— متغيرات محدودة النطاق بالـ play (تُغطى بعمق في الدرس 5).tasks— قائمة مرتّبة من الإجراءات لتنفيذها على المضيفين المُعيّنين.handlers— مهام تُشغَّل فقط عند الإخطار بها (تُغطى أدناه).
ansible-playbook واحد.
المهام: وحدة العمل
المهمة هي استدعاء واحد لوحدة Ansible. كل مهمة لها name (مطلوب في الإنتاج — لا تتخطاه أبداً)، ومفتاح الوحدة، وحجج الوحدة. تعمل المهام من الأعلى للأسفل داخل الـ play. إذا فشلت مهمة، تتوقف Ansible عن الـ play على ذلك المضيف بشكل افتراضي (فشل سريع لكل مضيف؛ المضيفون الآخرون في الـ play يستمرون إلا إذا ضبطت any_errors_fatal: true).
إليك playbook كاملة وعالية الجودة للإنتاج تثبّت Nginx وتضع ملف إعداد وتضمن تشغيل الخدمة وتمكينها:
شغّلها بالأوامر التالية:
--check --diff قبل التشغيل على الإنتاج. وضع التحقق يشغّل الـ playbook دون إجراء تغييرات ويطبع ما سيتغيّر. --diff يُظهر الفرق قبل وبعد محتوى الملف. معاً هما تشغيلك الجاف. على نطاق واسع، أفرض هذا في CI: شغّل --check على كل PR، وطبّق فقط عند الدمج في main.
المعالجات و notify: التأثيرات الجانبية المُحرَّكة بالأحداث
المعالجات هي مهام تعمل في نهاية الـ play — لكن فقط إذا أخطرها على الأقل مهمة واحدة خلال تنفيذ الـ play. يحل هذا النمط مشكلة تشغيلية أساسية: تريد إعادة تشغيل خدمة فقط عندما يتغيّر إعدادها فعلاً، وليس في كل تشغيل.
الآليات:
- تُعلن مهمة عن
notify: <اسم المعالج>. يجب أن يطابق الاسم حقلnameللمعالج تماماً (حساس لحالة الأحرف). - إذا أبلغت تلك المهمة عن changed (لا متخطاة، لا ok — changed)، تُعلّم Ansible المعالج المُسمّى كمعلّق.
- بعد اكتمال جميع المهام، تُصرف Ansible المعالجات المعلّقة مرة واحدة، بالترتيب الذي تُعلن فيه في قسم
handlers(ليس بالترتيب الذي أُخطرت به). - حتى لو أخطرت عشر مهام نفس المعالج، فإنه يعمل مرة واحدة فقط.
أخطار المعالجات في الإنتاج
المعالجات أنيقة لكن لها حواف حادة تعثّر المهندسين في الإنتاج:
- لا تعمل المعالجات إذا فشل الـ play في منتصف الطريق. إذا أخطأت مهمة 3 قبل اكتمال المهام، تُتخطى المعالجات المعلّقة. استخدم
meta: flush_handlersكمهمة لإجبار المعالجات على العمل في نقطة محددة في الـ play — على سبيل المثال، مباشرةً بعد نشر ملف إعداد لكن قبل بدء الخدمات التابعة. - اسم المعالج يجب أن يتطابق تماماً. خطأ مطبعي في
notifyلا يفعل شيئاً بصمت — لا ترفع Ansible خطأ لمعالج لم يُشغَّل قط. اختبر دائماً بـ--checkوابحث عنNOTIFIEDفي المخرجات. - ترتيب المعالجات هو ترتيب الإعلان، وليس ترتيب الإخطار. إذا كان المعالج ب يعتمد على اكتمال المعالج أ أولاً، أعلن أ قبل ب.
- إعادة تحميل واحدة، وليس عشراً. يمكن لعشر مهام تغيّر أجزاء الإعداد أن تُخطر نفس المعالج — يعمل مرة واحدة في النهاية. هذا هو النمط الصحيح لتجميع الإعداد من مصادر متعددة.
نمط الـ Playbook متعدد الـ Plays الكامل
تُنسّق ملفات الـ playbook الاحترافية الحقيقية طبقات متعددة. إليك النمط الأساسي لنشر مكدس تطبيق ذي طبقتين — قواعد البيانات أولاً، ثم خوادم التطبيقات:
ansible.builtin.package، وليس فقط package. تجعل FQCNs واضحاً أي مجموعة تأتي منها الوحدة، وتصمد أمام ترقيات إصدارات المجموعة دون تظليل صامت، وتُفرضها أدوات الفحص مثل ansible-lint. Google وHashiCorp وكل متجر Ansible مؤسسي يُلزم باستخدام FQCNs في قواعد كود الـ playbook المشتركة.
الـ Idempotency: العقد الذي يجب أن تحافظ عليه
كل مهمة في playbook احترافية يجب أن تكون idempotent — تشغيل الـ playbook عشر مرات ينتج نفس النتيجة كتشغيله مرة واحدة. وحدات package وservice وtemplate هي idempotent بالتصميم. مهام Shell والأوامر ليست كذلك — استخدم حراس creates أو removes أو changed_when لجعلها idempotent أو لقمع تقارير التغيير الإيجابية الزائفة:
كسر الـ idempotency هو أحد أكثر أخطاء Ansible الإنتاجية شيوعاً. الـ playbook التي تقلّب الإعداد في كل تشغيل ستعيد تشغيل الخدمات باستمرار، وتولّد ضجيجاً في أحداث التغيير في CMDB الخاص بك، وتجعل من المستحيل معرفة ما إذا كان شيء ما قد تغيّر فعلاً من تقرير التشغيل. عامل كل سطر CHANGED في تشغيل الـ playbook كحدث حقيقي يتطلب تفسيراً.