كل ما بنيته في هذا الدرس التعليمي — تصميم المخزون، والأوامر العشوائية، والـ playbooks، والمتغيرات وقوالب Jinja2، والشروط والحلقات، والأدوار، وAnsible Vault، واستراتيجيات التوسع — تتقاطع في هذا المشروع الختامي. ستصمم وتنشر قاعدة كود Ansible كاملة وجاهزة للإنتاج تُهيّئ ثلاث مجموعات خوادم مختلفة: الويب (NGINX كـ reverse proxy)، والتطبيق (Node.js)، وقاعدة البيانات (PostgreSQL). هذا هو الهيكل الذي ستصادفه في الشركات الحقيقية التي تشغّل عشرات إلى مئات العقد.
الهدف ليس مجرد تهيئة تعمل — بل كود قابل للصيانة وقابل للتدقيق وقابل لإعادة الاستخدام يستطيع عضو فريق جديد فهمه في ثلاثين دقيقة ويصمد أمام فوضى الإنتاج الحقيقي: تدوير الأسرار، وإضافة العقد، وترقيات نظام التشغيل، ومعالجة الأمن تحت الضغط.
هيكل المشروع: بنية الأدوار أولاً
الأساطيل الحقيقية منظمة حول الأدوار، لا الـ playbooks المسطحة. الهيكل الأساسي أدناه يفصل الاهتمامات بشكل نظيف: site.yml على مستوى الموقع هو نقطة الدخول؛ مخزونات لكل بيئة تعزل الإنتاج عن التجربة؛ الأدوار القابلة لإعادة الاستخدام تعيش تحت roles/؛ الأسرار مُشفّرة في group_vars.
لماذا inventories/prod/group_vars/all/vault.yml بدلاً من vault على مستوى الجذر؟ الاحتفاظ بالـ vault داخل مجلد المخزون يعني أن تشغيل ansible-playbook -i inventories/prod site.yml يحمّل تلقائياً الأسرار الصحيحة لتلك البيئة. ملف vault واحد على مستوى الجذر سيكون مشتركاً عبر جميع البيئات، مما يجعل التعرض العرضي للأسرار عبر البيئات خطراً حقيقياً. ملفات vault لكل مخزون تُلغي الخطأ على المستوى الهيكلي.
المخزون: تجميع الأسطول
يُعرّف ملف hosts.ini ثلاث مجموعات خوادم بالإضافة إلى مجموعة مركّبة fleet تمتد عبر جميعها. المجموعة المركّبة يستخدمها دور base — مطبّق على كل عقدة بصرف النظر عن وظيفتها.
# inventories/prod/hosts.ini
[web]
web-01.prod.example.com ansible_user=deploy
web-02.prod.example.com ansible_user=deploy
[app]
app-01.prod.example.com ansible_user=deploy
app-02.prod.example.com ansible_user=deploy
app-03.prod.example.com ansible_user=deploy
[db]
db-primary.prod.example.com ansible_user=deploy db_role=primary
db-replica-01.prod.example.com ansible_user=deploy db_role=replica
db-replica-02.prod.example.com ansible_user=deploy db_role=replica
# مجموعة مركّبة: جميع المضيفين المدارين
[fleet:children]
web
app
db
دور Base: تصليب كل عقدة
يفرض دور base خط الأساس على مستوى نظام التشغيل الذي يجب أن تشترك فيه كل عقدة في الأسطول — بصرف النظر عن تشغيل NGINX أو Node.js أو PostgreSQL. هنا تفرض تصليب SSH وauditd وضبط sysctl وNTP وعميل المراقبة المشترك. تطبيق خط أساس شامل عبر مجموعة مركّبة يمنع فئة الحادثة التي تكون فيها قاعدة البيانات الإنتاجية ذات إعداد SSH أضعف من طبقة الويب لأن شخصاً ما نسي تطبيق playbook التصليب عليها.
معامل validate في قالب sshd_config غير قابل للتفاوض في الإنتاج. بدونه، خطأ في Jinja2 أو خطأ إملائي في القالب ينشر sshd_config غير صحيح نحوياً إلى القرص، ينبّه الـ handler لإعادة تشغيل sshd، يرفض sshd البدء، وأنت محظور من الدخول إلى العقدة. السطر validate: /usr/sbin/sshd -t -f %s يشغّل sshd -t (اختبار الإعداد) على الملف المُقدَّم قبل نقله إلى /etc/ssh/sshd_config. إذا فشل الاختبار، تفشل المهمة ويبقى الإعداد القديم في مكانه. هذا السطر الواحد أنقذ لا حصر لها من عمليات حظر SSH الإنتاجية.
دور Web: NGINX كـ Reverse Proxy
يثبّت دور الويب NGINX وينشر إعداد Virtual Host مُقدَّراً باستخدام Jinja2. يستخدم القالب متغيرات المجموعة لملء عناوين خوادم التطبيق الأمامية بشكل ديناميكي — لا عناوين IP مُشفَّرة في أي قالب.
يبني قالب Jinja2 المقابل كتلة upstream بشكل ديناميكي من المخزون الحي — هذا هو النمط الأقوى في إدارة إعدادات الأسطول. يُعاد إعداد NGINX تلقائياً عند إضافة أو إزالة خادم تطبيق؛ لا تلمس القالب أبداً.
يثبّت دور التطبيق Node.js، وينشر التطبيق من أرتفاكت مُصدَّر بإصدار، ويدير وحدة systemd التي تُبقيه يعمل. يتم حقن أسرار التطبيق (كلمة مرور قاعدة البيانات، JWT secret) في وقت النشر من Ansible Vault إلى ملف .env لا يُودَع أبداً في التحكم بالمصدر.
يسحب قالب .env من المتغيرات المشفّرة بـ Vault. وضع الملف 0600 يضمن أن عملية app_user فقط هي من تستطيع قراءته — انضباط يمنع تسرب الأسرار إلى السجلات المقروءة للجميع أو العمليات الأخرى على نفس المضيف.
دور DB: إعداد PostgreSQL بصرياً
دور قاعدة البيانات هو الأكثر حساسية. يجب أن يتعامل مع التمييز بين الأساسي والنسخة الاحتياطية (التي تم التقاطها كمتغير مضيف في المخزون)، ويُهيّئ PostgreSQL مرة واحدة فقط (لا في كل تشغيل playbook)، ويُعدّ pg_hba.conf للسماح باتصالات طبقة التطبيق مع حجب كل شيء آخر.
يُنسّق site.yml كل شيء. يطبق دور base على مجموعة fleet بالكامل أولاً، ثم يطبق أدوار خاصة بالطبقات في مسرحيات منفصلة. الترتيب مهم: يجب أن يكون التصليب الأساسي في مكانه قبل تثبيت أي خدمة، ويجب أن تكون قاعدة البيانات قابلة للوصول قبل أن تبدأ خوادم التطبيق.
# site.yml
---
- name: Apply baseline hardening to all fleet nodes
hosts: fleet
become: true
gather_facts: true
roles:
- base
- name: Configure web tier (NGINX reverse proxy)
hosts: web
become: true
gather_facts: true
roles:
- web
- name: Configure app tier (Node.js application)
hosts: app
become: true
gather_facts: true
roles:
- app
- name: Configure database tier (PostgreSQL)
hosts: db
become: true
gather_facts: true
roles:
- db
معمارية الأسطول: يطبّق site.yml الواحد دور base على جميع العقد، ثم يفوّض أدوار خاصة بالطبقة إلى مجموعات web وapp وdb بالتسلسل.
تشغيل الأسطول: سير عمل النشر الكامل
مع اكتمال قاعدة الكود، يتبع سير عمل النشر نمطاً صارماً من ثلاث مراحل: المعاينة، والتحقق، والتطبيق. لا تشغّل site.yml على الإنتاج أبداً بدون تمريرة جافة أولاً.
# 1. تثبيت تبعيات مجموعات Galaxy أولاً
ansible-galaxy collection install -r requirements.yml
# 2. فحص البنية — يكشف أخطاء YAML والمتغيرات غير المعرّفة قبل لمس المضيفين
ansible-playbook -i inventories/prod site.yml --syntax-check
# 3. تشغيل جاف على جميع مضيفي الإنتاج — راجع كل مهمة "changed"
ansible-playbook -i inventories/prod site.yml \
--ask-vault-pass \
--check --diff
# 4. التشغيل الحي — طبّق على staging أولاً، ثم الإنتاج
ansible-playbook -i inventories/staging site.yml --ask-vault-pass
ansible-playbook -i inventories/prod site.yml --ask-vault-pass
# 5. استهداف طبقة واحدة للتغييرات التدريجية (مثل تحديث إعداد NGINX)
ansible-playbook -i inventories/prod site.yml \
--ask-vault-pass \
--limit web \
--tags web
# 6. حد الطوارئ: استهداف مضيف واحد أثناء فرز الحوادث
ansible-playbook -i inventories/prod site.yml \
--ask-vault-pass \
--limit web-01.prod.example.com \
--tags web \
--check --diff
أنماط فشل الإنتاج وكيفية منعها
تكشف تشغيلات Ansible على نطاق الأسطول أنماط فشل لن تصادفها في اختبارات المضيف الواحد. كل ما يلي أدناه هو فئة حادثة إنتاجية حقيقية مع التخفيف المدمج في هيكل هذا المشروع.
التطبيق الجزئي للأسطول عند خطأ الشبكة. يطبّق Ansible المهام مضيفاً تلو الآخر في دفعات متوازية. انقطاع SSH العابر على ثلاث عقد يضعها في UNREACHABLE، لكن العقد الأخرى تُهيَّأ. استخدم serial: "20%" في المسرحيات عالية نطاق التأثير حتى تكون الإخفاقات محدودة، وتحقق من عدد failed_hosts في AWX قبل المتابعة إلى الدفعة التالية.
عدم تطابق كلمة مرور Vault بين البيئات. vault التجربة والإنتاج يستخدمان كلمات مرور مختلفة. إذا استخدمت --vault-password-file مشيراً إلى الملف الخاطئ، تفكّ تشفير vault التجربة بمفتاح الإنتاج وتحصل على خطأ قيمة خاطئة صامت (المتغير يُحلّ لكن يحتوي على بيانات غير صالحة). استخدم علامات --vault-id منفصلة: --vault-id prod@~/.vault-prod يجعل مصدر كلمة المرور صريحاً والتعارض صاخباً.
تخطّي القالب لكتلة بصمت أثناء التقدير. حلقة Jinja2 {% for host in groups['app'] %} التي تشير إلى hostvars[host]['ansible_default_ipv4']['address'] تُولّد بصمت كتلة upstream فارغة إذا كان جمع الحقائق معطّلاً (gather_facts: false) للمجموعة app. شغّل دائماً gather_facts: true لأي مسرحية تشير قوالبها إلى hostvars، وأضف مهمة assert تتحقق من أن الملف المُقدَّر غير فارغ.
عدم إطلاق الـ handler بعد مهمة فاشلة. تُطلَق الـ handlers فقط إذا اكتملت المسرحية بدون فشل على ذلك المضيف. فشل مهمة في منتصف المسرحية يُكتم جميع الـ handlers المعلّقة — مما يعني نشر قالب لكن عدم إعادة تحميل الخدمة أبداً. استخدم force_handlers: true على مستوى المسرحية لـ handlers إعادة تحميل الخدمة لضمان إعادة تحميل بذل الجهود الكامل حتى عند الفشل الجزئي، وراقب صحة الخدمة بشكل منفصل.
ضع علامات على كل مهمة ودور للاستهداف الجراحي. ضع علامة على مهام دور base بـ tags: [base, hardening]، ومهام الويب بـ tags: [web, nginx]، وهكذا. في الإنتاج، لن تُعيد تشغيل site.yml الكامل أبداً تقريباً — ستشغّل --tags nginx لدفع تغيير إعداد NGINX، أو --tags hardening للاستجابة لـ CVE. وضع العلامات هو ما يفصل قاعدة كود Ansible يمكنك استخدامها بثقة في الإنتاج عن واحدة تُرهب فريقك لأن كل تغيير يتطلب تشغيل كل شيء.
التطبيق المستمر: الجدولة عبر AWX
في الإنتاج، تشغيلات ansible-playbook اليدوية مخصصة للتغييرات الفردية والاستجابة للحوادث. تطبيق خط الأساس المستمر يُعالَج بجدولة تشغيل site.yml الكامل كل 30 دقيقة في AWX. كل تشغيل مُسجَّل وناتجه قابل للبحث، والمضيفون الفاشلون يُطلقون تنبيهات PagerDuty عبر نظام إخطار AWX. يُقارب هذا ضمانات تصحيح الانجراف لأداة مبنية على Pull — بدون التعقيد التشغيلي لإدارة بنية Puppet.
الـ playbook الذي بنيته في هذا المشروع هو النمط الكامل الذي تستخدمه فرق البنية التحتية على نطاق واسع. من هذا الأساس يمكنك التوسع: إضافة دور monitoring ينشر مُصدِّر عقدة Prometheus، ودور logship يُعدّ Fluent Bit، ودور certs يُدوّر شهادات TLS عبر Vault PKI — كلها تتبع نفس هيكل الدور، ونفس اتفاقية المخزون، ونفس انضباط التشغيل الجاف أولاً.
نستخدم ملفات تعريف الارتباط لتشغيل هذا الموقع وتحليل الزيارات وعرض إعلانات مخصّصة. يمكنك قبول كل ملفات تعريف الارتباط أو رفض غير الأساسية منها.
سياسة الخصوصية