إدارة الإعدادات مع Ansible

الأوامر الفورية والوحدات

18 دقيقة الدرس 3 من 30

الأوامر الفورية والوحدات

قبل أن تكتب أول playbook، تحتاج إلى فهم الوحدة الذرية للعمل في Ansible: الوحدة (module). كل ما تفعله Ansible — نسخ ملف، أو إعادة تشغيل خدمة، أو تثبيت حزمة، أو الاستعلام من API — يتم عبر تشغيل وحدة على عقدة مُدارة. تتيح لك الأوامر الفورية (Ad-hoc commands) استدعاء وحدة واحدة مباشرةً من طرفيتك دون الحاجة إلى playbook، مما يجعلها أسرع طريقة لاستكشاف مكتبة وحدات Ansible وتشخيص البنية التحتية الحية. على نطاق شركات التقنية الكبرى، تُعدّ الأوامر الفورية أيضاً الخطوة الأولى الآمنة عندما تحتاج إلى فحص أسطول خوادم قبل الالتزام بتغيير آلي.

نموذج الوحدة (Module)

الوحدة في Ansible هي برنامج مستقل بذاته — عادةً Python، لكن أيضاً PowerShell أو Ruby أو سكريبت shell — تُحوّله Ansible إلى العقدة المُدارة، تُنفّذه، ثم تحذفه. تقبل كل وحدة مجموعة من المعاملات المُسمّاة، وتنفّذ إجراءً منطقياً واحداً بالضبط، وتُعيد نتيجة JSON تتضمن علامة changed وأي بيانات ذات صلة. لا تُشغّل عقدة التحكم الوحدة بنفسها أبداً؛ بل تُنسّق فقط عملية النقل وتجمع النتيجة.

يعني هذا التصميم أن الوحدات تدرك المنصة. تكتشف وحدة package ما إذا كانت المضيفة تستخدم apt أو yum أو dnf أو homebrew وتُفوّض إلى الأداة الصحيحة تلقائياً. تكتب مهمة واحدة؛ وتتكيف Ansible مع عائلة نظام تشغيل كل عقدة. هذا التجريد هو ما يجعل playbooks في Ansible قابلةً للنقل عبر أساطيل غير متجانسة.

فكرة محورية — تشريح قيمة إعادة الوحدة: تُعيد كل وحدة على الأقل {"changed": false, "failed": false}. مفتاح changed هو ما يقود تقارير الأداء الخاملي وإشعارات المعالج (handler). إذا أعادت مهمة changed: false، تتخطى Ansible أي أهداف notify — لا إعادة تشغيل خدمات غير ضرورية عند التكرار.

الأداء الخامل: العقد التي يجب على كل وحدة الوفاء بها

الأداء الخامل (Idempotency) يعني أن تشغيل نفس مهمة Ansible مرتين ينتج عنه بالضبط نفس الحالة النهائية كتشغيلها مرةً واحدة، دون أي آثار جانبية في التشغيل الثاني. هذه ليست ميزة اختيارية — بل هي العقد الهندسي الذي يجعل Ansible آمنةً للتشغيل في خطوط الأنابيب الآلية، ومهام cron، وحلقات المعالجة دون إشراف بشري.

تفحص الوحدة المكتوبة جيداً الحالة الحالية قبل التصرف. تفحص وحدة file ما إذا كان الملف يمتلك فعلاً الأذونات المستهدفة قبل استدعاء chmod. وتفحص وحدة user ما إذا كان الحساب موجوداً بالفعل قبل استدعاء useradd. إذا كانت الحالة المطلوبة تتطابق بالفعل مع الواقع، تُعيد الوحدة changed: false وتخرج فوراً. عندما تبني وحداتك الخاصة أو مهام shell، أنت المسؤول عن تطبيق هذا الفحص بنفسك — الإخفاق في ذلك يحوّل Ansible إلى أداة تشغيل سكريبتات مدمّرة بدلاً من أداة تكوين تعريفية.

خطأ إنتاجي شائع — shell وcommand ليستا أبداً خاملتَي الأداء بشكل افتراضي. سيفشل ansible all -m shell -a "useradd deploy" في كل تشغيل لاحق لأن useradd يُعيد خطأً حين يكون الحساب موجوداً بالفعل. استخدم وحدة user بدلاً من ذلك. احتفظ بـshell/command للعمليات التي لا توجد لها وحدة مخصصة، وأضف دائماً وسيطة creates أو removes (أو شرط when مدعوماً بحقيقة) لاستعادة الأداء الخامل.

صيغة الأمر الفوري

الشكل العام للأمر الفوري هو:

ansible <pattern> -m <module> -a "<module_args>" [options]

<pattern> هو أي مجموعة في الـ inventory، أو اسم مضيف، أو نمط glob. الخيارات الشائعة التي ستستخدمها يومياً:

  • -i — مسار ملف أو مجلد الـ inventory (الافتراضي: /etc/ansible/hosts)
  • -u — المستخدم البعيد (الافتراضي: المستخدم المحلي الحالي)
  • -b — become (الترقية إلى sudo)
  • --become-method — طريقة الترقية (sudo، su، doas)
  • -f — عدد العمليات المتوازية (الافتراضي: 5؛ اضبطها على 50+ للأساطيل الكبيرة)
  • -v / -vvv — مستوى التفصيل؛ -vvvv يُظهر مُخرجات تصحيح SSH
  • --check — وضع التشغيل الجاف؛ تُبلّغ الوحدات بما ستفعله دون إجراء تغييرات فعلية
  • --diff — يُظهر الفرق قبل وبعد للمهام التي تُعدّل الملفات

الوحدات التي ستستخدمها كل يوم

تُضمّن Ansible أكثر من 7,000 وحدة عبر مجموعاتها. من الناحية العملية، 90٪ من عمل البنية التحتية تغطيه اثنا عشر وحدة أساسية. فيما يلي كل واحدة مع أمر حقيقي يمثّل الاستخدام الإنتاجي:

# --- فحص الاتصال --- # ping ليست ICMP؛ تتحقق أن Python متاح وأن مصادقة SSH تعمل. # استخدمها كأول أمر على أي أسطول جديد. ansible webservers -i inventory/prod -m ping # --- إدارة الحزم --- # تثبيت nginx على جميع خوادم الويب (خامل الأداء: يتخطى إذا كان مثبتاً) ansible webservers -i inventory/prod -m package -a "name=nginx state=present" -b # تثبيت إصدار محدد (حرج في الإنتاج — لا تسمح بإصدارات متحركة أبداً) ansible webservers -i inventory/prod \ -m package -a "name=nginx-1.26.1 state=present" -b # إزالة حزمة ansible legacy -i inventory/prod -m package -a "name=telnetd state=absent" -b # --- إدارة الخدمات --- # التأكد من تشغيل nginx وتفعيله عند الإقلاع ansible webservers -i inventory/prod \ -m service -a "name=nginx state=started enabled=true" -b # إعادة تشغيل nginx (استخدم باعتدال في الإنتاج — يُفضَّل المعالجات في playbooks) ansible webservers -i inventory/prod -m service -a "name=nginx state=restarted" -b # --- عمليات الملفات --- # إنشاء مجلد بملكية وأذونات محددة ansible all -i inventory/prod \ -m file -a "path=/var/app/releases state=directory owner=deploy group=deploy mode=0755" -b # حذف ملف ansible all -i inventory/prod -m file -a "path=/tmp/old-config.conf state=absent" -b # --- نسخ ملف إلى المضيفات البعيدة --- # تنسخ الوحدة الملف وتحسب checksum SHA256 وتتخطى إذا كان متطابقاً ansible webservers -i inventory/prod \ -m copy -a "src=files/nginx.conf dest=/etc/nginx/nginx.conf owner=root mode=0644 backup=yes" -b # --- جلب ملف من المضيفات البعيدة --- # سحب /var/log/app/error.log من كل مضيف إلى ./fetched/<hostname>/ محلياً ansible webservers -i inventory/prod \ -m fetch -a "src=/var/log/app/error.log dest=fetched/ flat=no" -b # --- تشغيل أمر shell (مع حارس الأداء الخامل) --- # 'creates' يُخبر Ansible بالتخطي إذا كان المسار موجوداً بالفعل ansible dbservers -i inventory/prod \ -m shell -a "pg_basebackup -D /data/replica creates=/data/replica/PG_VERSION" -b # --- جمع الحقائق من جميع المضيفات --- # يُعيد JSON: IP، النظام، المعالج، الذاكرة، الأقراص، إصدار النواة، إلخ ansible all -i inventory/prod -m setup # تصفية لحقائق الشبكة فقط (أسرع للأساطيل الكبيرة) ansible all -i inventory/prod -m setup -a "filter=ansible_default_ipv4" # --- إدارة المستخدمين --- # إنشاء مستخدم نظام (خامل الأداء) ansible all -i inventory/prod \ -m user -a "name=deploy shell=/bin/bash groups=docker append=yes state=present" -b # --- lineinfile: التأكد من وجود سطر تكوين --- # خامل الأداء: يُضيف السطر فقط إذا لم يكن موجوداً ansible dbservers -i inventory/prod \ -m lineinfile -a "path=/etc/postgresql/16/main/postgresql.conf \ line='max_connections = 500' regexp='^max_connections' state=present" -b
Ansible ad-hoc command execution flow and idempotency check Control Node ansible -m copy ... inventory lookup SSH Managed Node Module (Python) uploaded + executed State Check current == desired? equal? yes changed: false skip, no action no Apply Change changed: true JSON result
دورة حياة تنفيذ وحدة Ansible: تُنقل الوحدة عبر SSH، تفحص الحالة الحالية، ولا تُطبّق تغييراً إلا حين تختلف الحالة المطلوبة — مُعيدةً نتيجة JSON مع علامة changed.

وحدة setup — المخزون الحي لأسطولك

تستحق وحدة setup (المعروفة أيضاً بـجمع الحقائق) اهتماماً خاصاً. عند تشغيل أي playbook تستدعي Ansible وحدة setup أولاً (ما لم تضبط gather_facts: false) لبناء مخزون JSON غني لكل عقدة مُدارة: إصدار النواة، وجميع عناوين IP والواجهات، وتوپولوجيا الذاكرة والمعالج، ونقاط تحميل الأقراص، ونوع مدير الحزم، ومسار مترجم Python، وعشرات غيرها. استعلامات الحقائق الفورية لا غنى عنها في الإنتاج:

# إيجاد جميع المضيفات التي تعمل بنواة أقدم من 6.1 (مفيد قبل تصحيح ثغرة على مستوى النواة) ansible all -i inventory/prod -m setup -a "filter=ansible_kernel" -o \ | grep -v " 6\." # فحص الذاكرة الحرة عبر طبقة قواعد البيانات قبل ترحيل يستهلك ذاكرة كبيرة ansible dbservers -i inventory/prod -m setup \ -a "filter=ansible_memfree_mb" -o # سطر واحد لعرض IPv4 الأساسية وعائلة نظام التشغيل لكل مضيف ansible all -i inventory/prod -m setup \ -a "filter=ansible_default_ipv4,ansible_os_family" -o
نصيحة احترافية — تخزين الحقائق مؤقتاً للأساطيل الكبيرة: يضيف استدعاء setup عبر 500 مضيف في بداية كل playbook 20-60 ثانية من تكاليف SSH المتسلسلة. فعّل تخزين الحقائق مؤقتاً في ansible.cfg (fact_caching = redis أو jsonfile) لإبقاء الحقائق بين التشغيلات. تعمل دائماً عمليات نشر Ansible على نطاق Google بتخزين الحقائق مؤقتاً؛ تُجبَر الحقائق الجديدة فقط للمضيفات التي انضمت حديثاً إلى الأسطول أو أجرت تحديثاً للنواة مؤخراً.

التوازي وخيار -f

عدد الشعب الافتراضي في Ansible هو 5 — تُشغّل المهام على 5 مضيفات كحد أقصى في آنٍ واحد. بالنسبة لأسطول مكوّن من 200 عقدة، تستغرق مهمة مدتها 30 ثانية دقيقتين. ارفع -f ليتناسب مع اتصالات عقدة التحكم المتاحة (عادةً 50–100 لحاسوب Ansible مخصص) وإعداد MaxSessions في SSH daemon:

# إعادة تشغيل خدمة التطبيق عبر 200 عقدة، 50 في كل مرة ansible appservers -i inventory/prod -m service \ -a "name=myapp state=restarted" -b -f 50 # ضبط افتراضي دائم في ansible.cfg (مُودَع في مستودع العمليات) # [defaults] # forks = 50
فكرة محورية — وضع الفحص هو شبكة الأمان. قبل تشغيل أي أمر فوري مدمّر على الإنتاج، نفّذ دائماً تشغيلاً بـ--check --diff أولاً. تحترم معظم الوحدات وضع الفحص وتُبلّغ بما ستُغيّره بالضبط دون لمس أي شيء. هذا يعادل terraform plan في عالم الأوامر الفورية — وتجاهله هو كيف يُعيد المهندسون تشغيل 200 خدمة إنتاجية في آنٍ واحد في الثانية الثانية من الليل.

الأوامر الفورية هي أسرع حلقة تعلم في Ansible: نفّذ وحدة، اقرأ JSON، عدّل المعاملات، نفّذ مجدداً. ببناء ذاكرة عضلية مع هذه الأوامر، ستكتب playbooks أكثر ذكاءً — لأن الـ playbook ليست سوى تسلسل قابل للتكرار ومُصدَّر بالإصدار من نفس استدعاءات الوحدات التي أتقنتها هنا.