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

معمارية Ansible والمخزون

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

معمارية Ansible والمخزون

قبل أن تتمكن من إدارة خادم واحد بـ Ansible، تحتاج إلى فهم كيف يصل Ansible إلى ذلك الخادم، وماذا يعرف عن أسطولك، وكيف تنظم مئات أو آلاف الأجهزة في مجموعات منطقية. يغطي هذا الدرس الثلاثة معاً: معمارية SSH عديمة الوكيل، والمخازن الثابتة والديناميكية، ونظام متغيرات المضيف والمجموعة. افهمها صح وكل playbook تكتبه سيعمل. افهمها غلط وستقضي مسيرتك المهنية في تتبع أخطاء الاتصال وتعارضات المتغيرات.

المعمارية عديمة الوكيل: لماذا تهم

تقريباً كل نظام إدارة تهيئة آخر — Chef وPuppet وSaltStack في وضع الوكيل — يتطلب منك تثبيت وصيانة عميل (daemon) على كل مضيف مُدار. هذا العميل يستطلع خادماً مركزياً ويطبق التهيئة ويُبلّغ. العبء التشغيلي حقيقي: العميل نفسه يحتاج تحديثات، ويجب أن يبقى يعمل، ويمكن أن يفشل بطرق تعيق تطبيق التهيئة كلياً.

Ansible مختلف جوهرياً. لا وكيل. عقدة التحكم (حاسوبك المحمول أو مشغّل CI أو خادم بوابة) تتصل بكل عقدة مُدارة عبر SSH (أو WinRM لـ Windows)، تدفع سكريبت Python صغيراً يُسمى module، تنفذه، تجمع النتيجة، وتحذف السكريبت. العقدة المُدارة تحتاج فقط:

  • عميل SSH (sshd) يستمع على منفذ يمكن الوصول إليه
  • Python 3.x في المسار القياسي (Python 2.7 لا يزال يعمل لكنه وصل نهاية عمره)
  • حساب مستخدم يمكن لعقدة التحكم التحقق منه

هذا كل شيء. لا استثناءات جدار حماية لحركة الوكيل الصادرة، ولا تدوير شهادات، ولا حوادث "وكيلي عالق" الساعة الثانية فجراً.

الفكرة الأساسية: نموذج الدفع في Ansible يعني أن عقدة التحكم تبدأ كل اتصال. العقدة المُدارة دائماً سلبية. هذا يعني أيضاً أن Ansible لا تملك معرفة مستمرة بحالة المضيف بين التشغيلات — كل play تجمع حقائق جديدة. إن احتجت كشف الانجراف المستمر، اجمع Ansible مع أداة مثل AWS Config أو Open Policy Agent، بدلاً من محاولة الاستطلاع بـ cron.
Ansible Agentless SSH Architecture Control Node ansible / ansible-playbook Inventory + Playbooks SSH private key SSH :22 SSH :22 SSH :22 web-01 sshd + Python 3 no agent web-02 sshd + Python 3 no agent db-01 sshd + Python 3 no agent Module Execution 1. Copy Python module to /tmp 2. Execute + collect JSON result 3. Delete module from host result
يدفع Ansible وحدات Python عبر SSH، ينفذها، يجمع نتيجة JSON، وينظف — لا حاجة لعميل على العقد المُدارة.

ضبط اتصال SSH لنطاق الإنتاج

افتراضياً، يفتح Ansible اتصال SSH جديداً لكل مهمة على كل مضيف. عند عشرة مضيفين هذا جيد. عند خمسمئة يصبح عنق زجاجة. إعدادان يغيران هذا كلياً:

  • SSH multiplexing (ControlMaster): يعيد Ansible استخدام اتصال SSH موجود لمهام متعددة بدلاً من إعادة التحقق في كل مرة. يُفعَّل تلقائياً حين يتضمن ssh_args الأعلام الصحيحة.
  • Pipelining: يُزيل دورة كتابة-الوحدة-على-القرص، التنفيذ، الحذف. بدلاً من ذلك يمرر Ansible الوحدة مباشرة عبر اتصال SSH الموجود. يتطلب تعطيل requiretty في /etc/sudoers على الهدف (الوضع الافتراضي في Linux الحديث).
# ansible.cfg — ملف إعداد مستوى المشروع # ضعه في نفس دليل playbooks [defaults] inventory = ./inventory # مسار المخزون الافتراضي remote_user = ansible # مستخدم OS على العقد المُدارة private_key_file = ~/.ssh/id_ed25519 # مفتاح SSH للتحقق host_key_checking = False # عطّل للأجهزة السحابية المؤقتة forks = 20 # اتصالات متوازية (الافتراضي 5) gathering = smart # تخزين الحقائق؛ اجمعها عند الحاجة فقط fact_caching = jsonfile fact_caching_connection = /tmp/ansible_facts fact_caching_timeout = 3600 # بالثواني [ssh_connection] pipelining = True # أكبر مكسب أداء منفرد ssh_args = -o ControlMaster=auto -o ControlPersist=60s -o StrictHostKeyChecking=no
ممارسة احترافية: احرص دائماً على إيداع ansible.cfg في جذر مستودع Ansible. هذا يجعل المشروع مكتفياً بذاته — مهندس جديد يستنسخ المستودع ويشغل ansible-playbook site.yml دون الحاجة لمعرفة أي إعداد عام. يحمّل Ansible ملفات الإعداد بهذا الترتيب الأولوي: متغير البيئة ANSIBLE_CONFIG ثم ./ansible.cfg ثم ~/.ansible.cfg ثم /etc/ansible/ansible.cfg. الملف المحلي للمشروع يفوز دائماً.

المخازن: تعليم Ansible بأسطولك

المخزون هو مصدر الحقيقة الوحيد لـ Ansible بشأن المضيفين الموجودين وكيفية الوصول إليهم. إنه أهم ملف ستكتبه. كل أمر مخصص وكل playbook يبدأ بحل المخزون.

المخزون الثابت (INI و YAML)

أبسط مخزون هو ملف INI عادي. المضيفون يُسردون بعنوان IP أو اسم المضيف، مجمَّعون تحت رؤوس [اسم-المجموعة]. يمكن أن تحتوي المجموعات على مجموعات أخرى باستخدام اللاحقة :children.

# inventory/hosts.ini — مخزون ثابت واقعي للإنتاج [webservers] web-01.prod.example.com ansible_user=ec2-user ansible_port=22 web-02.prod.example.com ansible_user=ec2-user ansible_port=22 web-03.prod.example.com ansible_user=ec2-user ansible_port=22 [databases] db-primary.prod.example.com ansible_user=ec2-user ansible_port=2222 db-replica-1.prod.example.com db-replica-2.prod.example.com [cache] redis-01.prod.example.com redis-02.prod.example.com # مجموعة من المجموعات — تطابق جميع المضيفين أعلاه [prod:children] webservers databases cache # بيئة التجهيز [webservers_staging] web-01.staging.example.com web-02.staging.example.com [staging:children] webservers_staging # متغيرات تنطبق على كل مضيفين في مجموعة (يُفضَّل استخدام ملفات group_vars/ بدلاً من ذلك) [webservers:vars] http_port=80 max_clients=200

صيغة INI تعمل جيداً للأساطيل الصغيرة. للمخازن الأكبر والأكثر تعقيداً، صيغة YAML مفضلة لأنها تدعم هياكل متداخلة دون غموض:

# inventory/hosts.yml — مخزون YAML مكافئ all: children: prod: children: webservers: hosts: web-01.prod.example.com: ansible_user: ec2-user web-02.prod.example.com: ansible_user: ec2-user databases: hosts: db-primary.prod.example.com: ansible_port: 2222 db-replica-1.prod.example.com: {} staging: children: webservers_staging: hosts: web-01.staging.example.com: {}

المخزون الديناميكي: معيار الإنتاج

تفشل المخازن الثابتة فور أن تصبح بنيتك التحتية ديناميكية — مجموعات تحجيم تلقائي تطلق وتنهي نسخاً، مهام ECS، عقد Kubernetes. ملف ثابت كتبته الاثنين يكون خطأ يوم الثلاثاء.

يحل Ansible هذا بـ إضافات المخزون الديناميكي. بدلاً من ملف، تشير Ansible إلى سكريبت أو إضافة تستعلم مزود البنية التحتية وتعيد قائمة المضيفين الحالية كـ JSON. كل سحابة كبرى لها إضافة رسمية:

  • amazon.aws.aws_ec2 — تستعلم EC2، تعيد النسخ مجمَّعة حسب العلامات والمنطقة والـ VPC وـASG إلخ.
  • google.cloud.gcp_compute — نسخ GCP Compute Engine
  • azure.azcollection.azure_rm — Azure VMs و VMSS
  • community.vmware.vmware_vm_inventory — VMware vSphere
# inventory/aws_ec2.yml — مخزون ديناميكي لـ AWS EC2 # يتطلب: pip install boto3 botocore # المجموعة: ansible-galaxy collection install amazon.aws plugin: amazon.aws.aws_ec2 regions: - us-east-1 - eu-west-1 filters: instance-state-name: running tag:Environment: production # نسخ الإنتاج فقط # تجميع المضيفين تلقائياً حسب هذه خصائص EC2 keyed_groups: - prefix: env key: tags.Environment # مجموعات: env_production, env_staging - prefix: role key: tags.Role # مجموعات: role_web, role_db, role_cache - prefix: az key: placement.availability_zone # مجموعات: az_us_east_1a, az_eu_west_1b # ما يُستخدم كعنوان المضيف hostnames: - private-ip-address # استخدم IP الخاص داخل VPC # - public-ip-address # استخدم IP العام عند الاتصال من خارج VPC compose: ansible_host: private_ip_address ansible_user: "'ec2-user'" ansible_ssh_private_key_file: "'~/.ssh/prod-key.pem'"

مع حفظ هذا الملف كـ inventory/aws_ec2.yml، يعيد تشغيل ansible-inventory -i inventory/ --list جميع نسخ EC2 الجارية في us-east-1 و eu-west-1 المُعلَّمة بـ Environment=production، مجمَّعة مسبقاً حسب علاماتها. لا تحديثات يدوية مطلوبة حين تضيف Auto Scaling عقدة أو تزيلها.

مصيدة إنتاجية: لا تخلط إعداد host_key_checking = False مع مضيفين معروفين ثابتين في pipelines CI — يُعطّل التحقق من SSH بصمت ويفتح الباب لهجمات MITM. النمط الإنتاجي الصحيح هو استخدام إضافة AWS EC2 مع أسماء مضيف private-ip-address وتشغيل عقدة تحكم Ansible داخل نفس الـ VPC (على خادم بوابة أو مشغّل CI في شبكة فرعية خاصة). عندها تُفعّل التحقق من مفتاح المضيف وتملأ ~/.ssh/known_hosts مسبقاً أثناء إعداد النسخة عبر user-data، أو تقبل المفاتيح عند الاتصال الأول عبر شبكة خاصة موثوقة.

المجموعات ومتغيرات المضيف والمجموعة

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

# تخطيط الدليل الموصى به — هذا هيكل "أفضل الممارسات" في Ansible # المستخدم على نطاق واسع داخل Google وSpotify وأغلب متاجر Ansible الكبيرة inventory/ hosts.yml # أو aws_ec2.yml للمخزون الديناميكي group_vars/ all.yml # متغيرات تنطبق على كل مضيف webservers.yml # متغيرات لمضيفي [webservers] فقط databases.yml prod.yml # متغيرات عند استهداف مجموعة [prod] staging.yml host_vars/ web-01.prod.example.com.yml # متغيرات لهذا المضيف المحدد فقط db-primary.prod.example.com.yml # group_vars/all.yml ansible_ssh_common_args: '-o StrictHostKeyChecking=no' ntp_servers: - 169.254.169.123 # خدمة AWS Time Sync (مفضلة داخل AWS) - time.cloudflare.com # group_vars/webservers.yml nginx_worker_processes: auto nginx_worker_connections: 4096 app_port: 8080 deploy_user: deploy # group_vars/databases.yml pg_max_connections: 500 pg_shared_buffers: "4GB" pg_work_mem: "64MB" backup_s3_bucket: "mycompany-db-backups-prod" # host_vars/db-primary.prod.example.com.yml pg_replication_role: primary pg_wal_level: replica pg_max_wal_senders: 10

أولوية المتغيرات: الترتيب الذي ينقذك الساعة الثالثة فجراً

Ansible لديها 22 مستوى لأولوية المتغيرات. لا تحتاج حفظها كلها، لكن تحتاج معرفة أكثر نقاط التعارض شيوعاً:

  1. الأدنى: الافتراضيات في الدور (roles/<الاسم>/defaults/main.yml) — مقصودة للتجاوز
  2. متغيرات مجموعة المخزون (group_vars/all.yml ثم المجموعات المحددة)
  3. متغيرات مضيف المخزون (host_vars/<اسم_المضيف>.yml)
  4. متغيرات المجموعة والمضيف في الـ playbook
  5. متغيرات الدور (roles/<الاسم>/vars/main.yml) — يصعب تجاوزها
  6. متغيرات المهمة (مفتاح vars: في play)
  7. الأعلى: المتغيرات الإضافية المُمررة بـ -e في سطر الأوامر — تفوز دائماً

أكثر خطأ إنتاجي شيوعاً هو أن vars/main.yml في الدور يتجاوز بصمت ملفات group_vars/ لأن متغيرات الدور تقع أعلى من متغيرات المخزون في سلسلة الأولوية. ضع التهيئة التي يجب أن يتمكن المشغّلون من تخصيصها في defaults/main.yml، وليس في vars/main.yml.

# تحقق من مخزونك وحل المتغيرات قبل تشغيل أي playbook # سرد جميع المضيفين في المخزون ansible-inventory -i inventory/ --list # إظهار ما تراه Ansible لمضيف محدد (كل المتغيرات مدمجة ومحلولة) ansible-inventory -i inventory/ --host web-01.prod.example.com # اختبار الاتصال بمجموعة (ping مخصص) ansible webservers -i inventory/ -m ping # اختبار الاتصال بمجموعة EC2 ديناميكية (حسب العلامة) ansible role_web -i inventory/aws_ec2.yml -m ping # معرفة المجموعات التي ينتمي إليها مضيف ansible-inventory -i inventory/ --host web-01.prod.example.com | python3 -m json.tool
ممارسة احترافية: في المؤسسات الكبيرة، المخزون هو خدمة بحد ذاته. أدوات مثل Netbox (مصدر حقيقة الشبكة) أو Rundeck تتكامل مع Ansible كمصادر مخزون ديناميكي، فيصبح المخزون مُصاناً من قِبَل فريق الشبكات في Netbox ويقرأه Ansible مباشرة. بروتوكول سكريبت المخزون المخصص في Ansible بسيط: نفّذ --list (يعيد كل المجموعات والمضيفين كـ JSON) و--host <اسم_المضيف> (يعيد متغيرات المضيف). أي سكريبت يتحدث هذه الواجهة يعمل كمخزون لـ Ansible.

ملخص

يُزيل تصميم SSH عديم الوكيل في Ansible فئة كاملة من التعقيد التشغيلي — لا دورة حياة وكيل لإدارتها، ولا مستوى تحكم منفصل لتأمينه. المخزون هو مصدر الحقيقة الوحيد لـ Ansible بشأن أسطولك: ملفات INI/YAML ثابتة للبنية التحتية الثابتة، وإضافات ديناميكية للبيئات السحابية حيث تأتي المضيفون وتذهب. ملفات متغيرات المجموعة والمضيف تُبقي التهيئة خارج المخزون وفي YAML مقروء من البشر تحت إدارة الإصدارات. وفهم أولوية المتغيرات يمنع التجاوزات الصامتة التي تتسبب في حوادث إنتاجية. مع توطيد هذه الأسس، أنت جاهز لكتابة أوامرك المخصصة الأولى والـ playbooks في الدروس التالية.