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

المتغيرات والحقائق والقوالب

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

المتغيرات والحقائق والقوالب

الأوامر المباشرة والدفاتر الثابتة لن تأخذك بعيداً. في اللحظة التي تحتاج فيها إلى نفس الدفتر لضبط جهاز التطوير بشكل مختلف عن جهاز الإنتاج — حدود ذاكرة مختلفة، ونقاط وصول مختلفة، وشهادات TLS مختلفة — تحتاج إلى متغيرات. وفي اللحظة التي تحتاج فيها إلى كتابة ملف إعداد يعتمد محتواه على مقدار الذاكرة الفعلية أو عنوان IP للمضيف المستهدف، تحتاج إلى قوالب Jinja2. وحين تكتشف Ansible تلقائياً طبيعة المضيف دون أن تُشفّر أي شيء يدوياً — هذه هي الحقائق.

هذه الآليات الثلاث هي محرك Ansible الإنتاجي في أي مؤسسة هندسية جادة. إتقان تفاعلها، وخاصة ترتيب الأولوية بينها، يمنع فئات كاملة من الأخطاء.

أولوية المتغيرات — المكدّس الكامل

تدمج Ansible المتغيرات من مصادر عديدة. عندما يظهر نفس اسم المتغير في أكثر من مكان، تطبّق Ansible سلسلة أولوية صارمة: المصدر الأعلى في القائمة يكسب. من أدنى إلى أعلى أولوية:

  1. الإعدادات الافتراضية للأدوار (roles/myrole/defaults/main.yml)
  2. متغيرات المجموعة في ملف الجرد أو السكريبت
  3. ملفات group_vars/all في الجرد
  4. ملفات group_vars/<groupname> في الجرد
  5. ملفات host_vars/<hostname> في الجرد
  6. حقائق المضيف (تُجمع تلقائياً)
  7. متغيرات اللعبة (مفتاح vars: في الدفتر)
  8. vars_files: وvars_prompt: في اللعبة
  9. متغيرات الأدوار (roles/myrole/vars/main.yml)
  10. متغيرات الكتل والمهام (vars: على مهمة)
  11. متغيرات الإدراج (include_vars)
  12. نتائج set_fact / register
  13. المتغيرات الإضافية (-e / --extra-vars على سطر الأوامر) — أعلى أولوية، تكسب دائماً
مزلق إنتاجي — متغيرات الأدوار مقابل الإعدادات الافتراضية: roles/myrole/vars/main.yml (الأولوية 9) يتجاوز بصمت كل شيء تحته، بما في ذلك group_vars. إذا ضبط دور ما قيمة nginx_worker_processes في vars/main.yml، سيُتجاهل إعدادك على مستوى الجرد بصمت. ضع دائماً القيم القابلة للتعديل من قِبل المشغل في defaults/main.yml؛ استخدم vars/main.yml فقط للثوابت الداخلية للدور التي لا يحتاج المشغلون لمسّها.

النموذج الذهني العملي على نطاق المؤسسات الكبيرة: الإعدادات الافتراضية هي "الخط الأساسي الآمن"، group_vars وhost_vars هي "طبقة البيئة"، و-e على سطر الأوامر هي "التجاوز الطارئ". يجب ألا تستخدم خطوط CI المتغيرات -e للترقية العادية؛ هذا مخرج بشري للطوارئ.

تعريف المتغيرات — الجرد وgroup_vars وhost_vars

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

# inventory/ # hosts.ini — قائمة المضيفين الثابتة # group_vars/ # all.yml — تنطبق على كل مضيف # web.yml — تنطبق على مضيفي مجموعة [web] # db.yml — تنطبق على مضيفي مجموعة [db] # host_vars/ # prod-web-01.yml — تنطبق على هذا المضيف فقط # inventory/group_vars/all.yml --- app_name: myapp app_port: 8080 log_level: info deploy_user: deploy # inventory/group_vars/web.yml --- nginx_worker_processes: auto nginx_keepalive_timeout: 75 # inventory/host_vars/prod-web-01.yml --- nginx_worker_processes: 16 # هذا المضيف يملك 16 نواة — تجاوز إعداد المجموعة

داخل الدفتر، تُشير إلى أي متغير بقوسين معقوفين مزدوجين: {{ app_port }}. تحل Ansible القيمة في وقت التشغيل بعد تطبيق سلسلة الأولوية الكاملة.

أبعد الأسرار عن group_vars: لا تضع كلمات المرور أو مفاتيح API أو المفاتيح الخاصة في ملفات متغيرات نصية صريحة. استخدم Ansible Vault (الدرس 8) لتشفير تلك القيم — لكن بنية المتغيرات وأسماء المفاتيح تبقى في group_vars كما هو موضح، لذا يظل النمط متسقاً.

الحقائق المجمّعة — ذكاء مجاني عن المضيف

عندما تتصل Ansible بمضيف وgather_facts: true (الإعداد الافتراضي)، تُشغّل وحدة setup التي تجمع مئات الحقائق عن الهدف: عائلة نظام التشغيل، إصدار النواة، إجمالي الذاكرة، عدد المعالجات، جميع عناوين IP، أنظمة الملفات المركّبة، نوع الافتراضية، والمزيد. كل هذه المعلومات متاحة كمتغيرات بادئتها ansible_.

# تشغيل ad-hoc لفحص الحقائق على مضيف واحد ansible web -i inventory/ -m setup | grep -E 'ansible_(os_family|memtotal_mb|processor_vcpus|default_ipv4)' # مثال على المخرجات: # "ansible_os_family": "RedHat", # "ansible_memtotal_mb": 15954, # "ansible_processor_vcpus": 8, # "ansible_default_ipv4": { # "address": "10.0.1.42", # ... # }

الحقائق الأساسية التي ستستخدمها باستمرار في دفاتر الإنتاج:

  • ansible_os_family — "Debian" أو "RedHat" أو "Archlinux" — استخدمها للتثبيت الشرطي للحزم
  • ansible_memtotal_mb — إجمالي الذاكرة؛ اشتق منه حدود كومة JVM أو عمال Nginx
  • ansible_processor_vcpus — عدد المعالجات؛ ضع nginx_worker_processes تلقائياً
  • ansible_default_ipv4.address — عنوان IP الأساسي؛ استخدمه في ملفات الإعداد وقواعد الجدار الناري
  • ansible_hostname / ansible_fqdn — هوية المضيف
  • ansible_distribution / ansible_distribution_version — نظام التشغيل الدقيق وإصداره
الحقائق المخصصة: يمكنك دفع حقائقك الخاصة إلى المضيفين عن طريق وضع ملفات JSON أو INI في /etc/ansible/facts.d/ على العقدة المُدارة (اسم الملف ينتهي بـ.fact). تظهر تحت ansible_local. تستخدم الفرق هذا لتسجيل طوابع زمنية للنشر وإصدارات التطبيقات ومعرّفات البيئات — كلها قابلة للاستعلام في تشغيلات الدفتر اللاحقة.

يضيف جمع الحقائق نحو 0.5 إلى 2 ثانية لكل مضيف. على نطاق 1,000 مضيف هذا ملحوظ. يمكنك تعطيل الجمع كلياً بـgather_facts: false في رأس اللعبة للدفاتر السريعة جداً التي لا تحتاج معلومات النظام، أو تخزين الحقائق مؤقتاً في Redis أو ملف JSON بـfact_caching في ansible.cfg.

قوالب Jinja2 — ملفات الإعداد التي تعرف مضيفها

وحدة template تنسخ ملفاً من المتحكم إلى العقدة المُدارة، لكنها قبل النسخ تُمرّر المحتوى عبر Jinja2. كل تعبير {{ متغير }} يُوسَّع، وكل كتلة {% if %} أو {% for %} تُقيَّم. النتيجة ملف نصي مُصيَّر بالكامل — إعداد Nginx، أو ملف خيارات JVM، أو وحدة systemd، أو إعداد استخلاص Prometheus — صحيح تماماً لذلك المضيف بعينه.

ملفات مصدر القوالب تعيش في مجلد templates/ للدور وتحمل اصطلاحاً امتداد .j2. مهمة الدفتر تبدو هكذا:

# roles/nginx/tasks/main.yml --- - name: نشر إعداد nginx ansible.builtin.template: src: nginx.conf.j2 dest: /etc/nginx/nginx.conf owner: root group: root mode: '0644' notify: Reload nginx # roles/nginx/templates/nginx.conf.j2 worker_processes {{ ansible_processor_vcpus }}; worker_rlimit_nofile 65535; events { worker_connections 4096; } http { keepalive_timeout {{ nginx_keepalive_timeout | default(75) }}; server_tokens off; upstream {{ app_name }}_backend { {% for host in groups['app'] %} server {{ hostvars[host]['ansible_default_ipv4']['address'] }}:{{ app_port }}; {% endfor %} } server { listen 80; server_name {{ ansible_fqdn }}; location / { proxy_pass http://{{ app_name }}_backend; } } }

هذا القالب الواحد ينتج إعداد Nginx مُضبوطاً بدقة على كل مضيف ويب: عدد العمال يتطابق مع المعالج الفعلي، كتلة upstream تُبنى من القائمة الحية لمضيفي مجموعة التطبيق، واسم الخادم هو FQDN الحقيقي — كل هذا دون أن ينظر أي إنسان لتلك القيم.

Ansible variable precedence and template rendering flow مصادر المتغيرات (من أدنى إلى أعلى) role defaults/main.yml group_vars / host_vars Gathered Facts (setup) Play vars / vars_files role vars/main.yml set_fact / register -e extra-vars (الأعلى دائماً) Jinja2 محرك القوالب {{ var }}, {% if %}, {% for %} nginx.conf.j2 مصدر القالب الإعداد المُصيَّر (لكل مضيف) prod-web-01 worker_processes 16; upstream … prod-web-02 worker_processes 8; upstream … staging-web-01 worker_processes 4; قيم التجهيز حقائق مجمّعة متغيرات عالية الأولوية إعدادات افتراضية مخرج مُصيَّر لكل مضيف
مكدّس أولوية المتغيرات يُغذّي محرك Jinja2 الذي يُصيّر ملف إعداد فريد لكل مضيف هدف.

مرشّحات Jinja2 — تحويل القيم

يأتي Jinja2 مع مكتبة غنية من المرشّحات تتيح لك تحويل قيم المتغيرات بشكل مضمّن. تضيف Ansible عشرات المرشّحات الإضافية. الأكثر فائدة في الإنتاج:

  • {{ nginx_worker_processes | default(4) }} — قيمة افتراضية آمنة عند احتمال عدم تعريف المتغير
  • {{ app_name | upper }} — معالجة النصوص
  • {{ ansible_memtotal_mb * 0.75 | int }} — حسابات؛ اشتق -Xmx لـJVM من الذاكرة الفعلية
  • {{ groups['app'] | length }} — عدّ المضيفين في مجموعة
  • {{ my_list | join(',') }} — دمج قائمة لقيمة إعداد مفصولة بفواصل
  • {{ secret_value | b64encode }} — ترميز base64 لبيان Kubernetes secret

set_fact — متغيرات ديناميكية في وقت التشغيل

أحياناً تحتاج إلى حساب متغير من مزيج من الحقائق ثم إعادة استخدامه عبر مهام متعددة. ansible.builtin.set_fact يُسند متغيراً في وقت التشغيل بنفس أولوية نتيجة register:

- name: حساب حجم كومة JVM (75 بالمئة من الذاكرة) ansible.builtin.set_fact: jvm_heap_mb: "{{ (ansible_memtotal_mb * 0.75) | int }}" - name: نشر خيارات JVM ansible.builtin.template: src: jvm.options.j2 dest: /etc/elasticsearch/jvm.options # jvm.options.j2 -Xms{{ jvm_heap_mb }}m -Xmx{{ jvm_heap_mb }}m -XX:+UseG1GC
قوْلب ملف الإعداد كاملاً، لا الأجزاء المتحركة فقط. نمط مضادّ شائع هو استخدام lineinfile لتعديل قيمة واحدة في ملف إعداد نصّبته حزمة. هذا هشّ — ترقية الحزمة قد تعيد الملف الأصلي وتتراجع عن تعديلك بصمت. النمط الإنتاجي: انشر ملف الإعداد كاملاً من قالب Jinja2 حتى تكون كل قيمة تحت سيطرتك مُتتبَّعة بالإصدار ومُطبَّقة بشكل صرفي في كل تشغيل.

الخلاصة الإنتاجية

في أي مؤسسة هندسية جادة تُشغّل Ansible على نطاق واسع، هذه الممارسات غير قابلة للتفاوض: ضع القيم القابلة للتعديل في defaults/ الأدوار حتى يتمكن المشغلون من تجاوزها؛ ضع تراكبات البيئة في group_vars/ وhost_vars/؛ اشتق قيم الإعداد من الحقائق المجمّعة حتى تتكيف دفاترك تلقائياً مع الأجهزة الفعلية؛ اكتب قوالب Jinja2 تمتلك ملف الإعداد بالكامل بدلاً من تعديل الأسطر الفردية؛ وتحقق دائماً من القالب المُصيَّر مقارنةً بمرجع معروف قبل النشر في الإنتاج.