دليل Ansible: أنشئ أول Playbook لخادم VPS
ثبّت Ansible عبر pipx على Ubuntu 24.04، واكتب الجرد وPlaybook لتقوية VPS جديد. أصلح أخطاء Permission denied ومشكلات sudo بخطوات عملية.
ما الذي ستبنيه
جهاز تحكم واحد مثبّت عليه Ansible، وخادم VPS واحد أو أكثر يعمل بنظام Ubuntu 24.04 حديث، ولا يحتوي على شيء سوى الصورة الأساسية. في النهاية، سيكون لديك ملف جرد يعرّف خوادمك، واختبار ping مخصص يثبت نجاح المصادقة من الطرف إلى الطرف، ودفتر تشغيل ينفّذ قائمة التحقق الكاملة لإعداد خادم VPS جديد بصيغة تعليمات برمجية: مستخدم نشر مع مفتاح SSH الخاص بك، وsshd مقوّى، وfail2ban، وترقيات تلقائية غير مراقبة، وجدار ناري يسمح بـOpenSSH قبل أن يمنع كل شيء آخر. وجّه الدفتر إلى خادم واحد أو إلى عشرين خادماً. شغّله مرتين، ولن يغيّر التشغيل الثاني شيئاً. هذه هي الفكرة بالكامل.
بعد خمسة عشر عاماً من إعداد خوادم VPS، أستطيع وصف النمط الفعلي بصدق: يضبط الجميع الخوادم الخمسة الأولى يدوياً، ثم يهدرون عطلة نهاية أسبوع في الخادم السادس لأن أحداً لا يتذكر ما فعلوه في الخوادم الخمسة الأولى. يوسّع هذا الدليل ما ورد في إدارة عدة خوادم Linux. ابدأ به عندما تلاحظ أنك تكتب apt install نفسه في ثلاث وحدات طرفية.
ما هو Ansible فعلياً، في فقرة واحدة
Ansible أداة بلا وكلاء. لا يوجد daemon لتثبيته على الخوادم التي يديرها؛ إذ تتصل آلة التحكم عبر SSH العادي، وتنسخ وحدة Python صغيرة إلى الهدف، وتشغّلها، وتقرأ JSON الذي تطبعه، ثم تحذفها. الشيء الوحيد الذي يحتاج إليه الهدف هو python3، وهو موجود مسبقاً في كل صورة Ubuntu قياسية. الكلمة المهمة هي idempotent، ومعناها بسيط: تصف المهمة حالة، لا إجراءً. يعني state: present لحزمة ما «التأكد من تثبيتها»، وليس «تشغيل برنامج التثبيت». إذا كانت الحالة متحققة مسبقاً، فلا يغيّر Ansible شيئاً، ويبلغ عنها باعتبارها ok بدلاً من changed. هذه الخاصية هي جوهر المنتج كله. وهي التي تجعل إعادة تشغيل playbook آمنة. وإعادات التشغيل الآمنة هي ما يحوّل shell script إلى بنية تحتية.
المتطلبات الأساسية والمشكلات الشائعة منذ البداية
- جهاز تحكم: حاسوبك المحمول أو VPS صغير. أفترض استخدام Ubuntu 24.04؛ ويعمل macOS بالطريقة نفسها بعد تثبيت pipx من Homebrew.
- VPS واحد أو أكثر مستهدف يعمل بنظام Ubuntu 24.04 على KVM، ويمكن الوصول إليه باستخدام root. لن يُثبَّت أي شيء عليها.
- مصادقة SSH باستخدام مفتاح لكل هدف. تكون مصادقة Ansible مطابقة تماماً لمصادقة الأمر
ssh. إذا طلبssh root@hostكلمة مرور، يفشل Ansible. - في Ubuntu 24.04، يتوقف
pip install ansibleبسببerror: externally-managed-environment. هذه سياسة مقصودة للتوزيعة وليست عطلاً. استخدم pipx. - المسافات البيضاء في YAML جزء من البنية النحوية. ينتج عن المسافة البادئة الخاطئة
mapping values are not allowed in this context، ويؤدي وجود محرف جدولة واحد في أي موضع إلى فشل التنفيذ. - أبقِ جلسة SSH عاملة ومفتوحة على كل هدف أثناء قيام playbook بتقوية إعدادات sshd. كل حالة فقدان وصول ساعدت عميلاً على استعادتها كانت تتضمن إغلاق الجلسة الأخيرة «للاختبار من بيئة نظيفة».
الخطوة 1: ثبّت Ansible على جهاز التحكم باستخدام pipx، وليس pip
الطريقة المعتادة هي pip3 install ansible. لكن على صورة 24.04 جديدة تماماً، يفشل ذلك قبل خطوة واحدة، Command 'pip3' not found, but can be installed with: sudo apt install python3-pip، وتثبيت pip لا ينقلك إلا إلى العائق الفعلي:
pip3 install ansibleerror: externally-managed-environment
× This environment is externally managed
╰─> To install Python packages system-wide, try apt install
python3-xyz, where xyz is the package you are trying to
install.تضع Ubuntu 24.04 علامة على Python الخاص بالنظام باعتباره مُداراً خارجياً وفقاً لـ PEP 668، لذلك لا يستطيع pip منافسة apt على الملفات نفسها. لا تستخدم --break-system-packages؛ فاسم الخيار يوضح وظيفته بدقة. الحل الصحيح هو pipx، إذ يمنح Ansible virtualenv معزولاً خاصاً به ويضع الملفات التنفيذية في PATH:
sudo apt update && sudo apt install -y pipx
pipx ensurepath
pipx install --include-deps ansibleافتح shell جديداً بعد pipx ensurepath حتى يصبح تغيير PATH سارياً. إنّ --include-deps ليس للزينة: فالحزمة ansible لا توفّر أي console scripts خاصة بها، ansible، وansible-playbook وبقية الملفات هي نقاط إدخال للتبعية ansible-core، ولذلك يرفض pipx التثبيت من دون الخيار No apps associated with package ansible or its dependencies. وثبّت الحزمة ansible، وليس ansible-core مجرداً؛ فالحزمة الكاملة تتضمن community collections، ويستخدم هذا playbook وحدات من مجموعتين منهما، ansible.posix وcommunity.general.
ansible --versionتبدأ النتيجة الصحيحة بسطر مثل ansible [core 2.19.x] وتذكر إصدار Python الذي تعمل تحته؛ أي إصدار حالي من core مناسب لكل ما يرد هنا. أما ansible: command not found فتعني أن ~/.local/bin ليس موجوداً في PATH بعد؛ افتح shell جديداً، أو استخدم source ~/.bashrc.
هذا كل ما يلزم للتثبيت. لن يحصل المضيفون المستهدفون على أي شيء.
الخطوة 2: الوصول إلى كل هدف باستخدام مفتاح SSH
ssh-keygen -t ed25519 -C "ansible control"
ssh-copy-id root@10.0.0.10
ssh-copy-id root@10.0.0.20ثم تحقّق من ذلك مرة واحدة لكل مضيف:
ssh root@10.0.0.10 true && echo okينفّذ هذا السطر مهمتين: يؤكد نجاح المصادقة بالمفتاح من دون كلمة مرور، ويسجّل مفتاح المضيف في known_hosts. نفّذ ذلك الآن، لأن Ansible يعرض مفتاح المضيف غير المسجّل كمطالبة تفاعلية تظهر وسط التنفيذ، فتبدو تماماً كأن العملية متوقفة.
الخطوة 3: ملف الجرد، ابدأ بـINI واستخدم YAML عندما يكبر
ملف الجرد هو ملف نصي يسرد الأجهزة التي قد يتعامل معها Ansible. أنشئ inventory.ini في دليل مشروع جديد:
[vps]
web1 ansible_host=10.0.0.10
web2 ansible_host=10.0.0.20
[vps:vars]
ansible_user=rootweb1 هو اسم مستعار تختاره. يظهر هذا الاسم في المخرجات وتستهدفه باستخدام --limit web1. أما ansible_host فهو العنوان الفعلي. يشير [vps] إلى مجموعة، ويضبط [vps:vars] المتغيرات لكل مضيف فيها؛ وansible_user هو حساب تسجيل دخول Ansible. أضف بجانبه ansible.cfg حتى لا تضطر إلى كتابة -i مرة أخرى:
[defaults]
inventory = inventory.iniيقرأ Ansible ansible.cfg من الدليل الحالي. احفظ ملف الجرد نفسه بصيغة YAML باسم inventory.yml، ووجّه ansible.cfg إلى هذا الاسم بدلاً منه. ستفضّل ذلك عندما يحمل كل مضيف عدة متغيرات:
vps:
hosts:
web1:
ansible_host: 10.0.0.10
web2:
ansible_host: 10.0.0.20
vars:
ansible_user: rootالملفان متكافئان. يسهل فحص INI بصرياً عند وجود خادمين، بينما يتوسع YAML بصورة أفضل عند وجود عشرين خادماً. اختر أحدهما وتوقف عن التفكير في الأمر.
الخطوة 4: أوامر ad-hoc، واختبار pong الأخضر الذي يثبت عمل كل شيء
ansible all -m pingهذا ليس ICMP. تنفّذ الوحدة ping اختباراً كاملاً يشمل تسجيل الدخول عبر SSH، ونسخ الوحدة، وتشغيل Python على الهدف، والتنظيف. النتيجة الصحيحة هي اللون الأخضر، مع كتلة واحدة لكل مضيف:
web1 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
},
"changed": false,
"ping": "pong"
}يعني اللون الأخضر SUCCESS أن المصادقة ومفسّر Python ووسيلة النقل تعمل، وأن playbook سيعمل أيضاً. يعني اللون الأحمر UNREACHABLE! أن وسيلة النقل فشلت قبل تشغيل أي وحدة؛ وتجد السلسلة النصية الدقيقة والإصلاح في قسم أوضاع الفشل أدناه. هناك أمران إضافيان من أوامر ad-hoc يستحقان المعرفة:
ansible all -a "uptime"
ansible all -m apt -a "update_cache=true upgrade=dist" --becomeتُستخدم أوامر ad-hoc للمهام لمرة واحدة وللفحوصات. أي شيء ستشغّله مرتين ينتمي إلى playbook.
الخطوة 5: أول playbook، قائمة التحقق من VPS الجديد بصيغة تعليمات برمجية
هذا كل ما ستنفذه يدوياً خلال الدقائق العشر الأولى على خادم جديد. احفظه في site.yml:
---
- name: Baseline a fresh Ubuntu VPS
hosts: vps
become: true
vars:
deploy_user: deploy
deploy_pubkey: "{{ lookup('file', '~/.ssh/id_ed25519.pub') }}"
baseline_packages:
- fail2ban
- unattended-upgrades
- ufw
baseline_services:
- fail2ban
- unattended-upgrades
tasks:
- name: Create the deploy user
ansible.builtin.user:
name: "{{ deploy_user }}"
groups: sudo
append: true
shell: /bin/bash
- name: Install the deploy user's SSH key
ansible.posix.authorized_key:
user: "{{ deploy_user }}"
key: "{{ deploy_pubkey }}"
- name: Passwordless sudo for the deploy user
ansible.builtin.copy:
dest: /etc/sudoers.d/deploy
content: "{{ deploy_user }} ALL=(ALL) NOPASSWD:ALL\n"
mode: "0440"
validate: /usr/sbin/visudo -cf %s
- name: Install baseline packages
ansible.builtin.apt:
name: "{{ baseline_packages }}"
state: present
update_cache: true
- name: Enable and start baseline services
ansible.builtin.service:
name: "{{ item }}"
state: started
enabled: true
loop: "{{ baseline_services }}"
- name: Harden sshd with a drop-in
ansible.builtin.copy:
dest: /etc/ssh/sshd_config.d/00-hardening.conf
content: |
PasswordAuthentication no
KbdInteractiveAuthentication no
PermitRootLogin prohibit-password
X11Forwarding no
mode: "0644"
validate: /usr/sbin/sshd -t -f %s
notify: Restart ssh
- name: Allow OpenSSH through ufw
community.general.ufw:
rule: allow
name: OpenSSH
- name: Enable ufw with default deny
community.general.ufw:
state: enabled
policy: deny
handlers:
- name: Restart ssh
ansible.builtin.service:
name: ssh
state: restartedهذه هي الأسطر التي تستحق الفهم بدلاً من النسخ:
توجد المتغيرات ضمن vars:، ويُشار إليها باستخدام "{{ deploy_user }}". ضع التعبير بالكامل بين علامتي اقتباس عندما تبدأ قيمة ما بقوس معقوف، وإلا فسيفسره محلل YAML بشكل خاطئ. يقرأ lookup('file', ...) مفتاحك العام من جهاز التحكم وقت التشغيل، لذلك لا يحتوي playbook على أي مادة مفتاح.
الحلقة. ينفّذ loop: "{{ baseline_services }}" مهمة الخدمة مرة واحدة لكل عنصر، ويعرض الناتج كل عنصر في سطر مستقل. لاحظ أن مهمة apt تتلقى قائمة الحزم كاملة دفعة واحدة. تكون معاملة apt واحدة أسرع، وهي النمط المفضّل للحزم؛ أما الحلقات فتُستخدم مع الوحدات التي تعمل فعلياً على عنصر واحد في كل مرة.
المعالج هو المفهوم الذي يجب استيعابه. لا يعني notify: Restart ssh «أعد تشغيل ssh الآن». بل يضع المعالج في قائمة الانتظار، ثم يُشغّله مرة واحدة في نهاية play، وفقط إذا أبلغت المهمة التي أرسلته عن changed فعلياً. أعد تشغيل playbook غداً: سيكون ملف drop-in صحيحاً مسبقاً، وستبلغ مهمة النسخ عن ok، ولن يُعاد تشغيل sshd. يوفّر السطر validate: إجراء الأمان عند التشغيل. يفحص sshd الملف قبل استبدال الملف القديم، لذلك يؤدي الخطأ المطبعي إلى فشل المهمة بدلاً من تعطيل الخدمة.
PermitRootLogin prohibit-password، وليس no، عن قصد. يسجّل هذا playbook الدخول بصفته root باستخدام مفتاح. يعطّل prohibit-password عمليات تسجيل دخول root باستخدام كلمة المرور، مع إبقاء جلستك قيد التشغيل. بعد التأكد من أن مستخدم النشر يعمل، استخدم ssh deploy@10.0.0.10 sudo true، أي العنوان العادي، لأن web1 اسم مستعار لا يعرفه سوى Ansible. بدّل ansible_user=deploy في ملف الجرد، ثم شدّد الإعداد إلى no في تشغيل لاحق. نفّذ إجراءات التحصين بترتيب لا يؤدي إلى فقدان الوصول.
بادئة 00- مهمة. بالنسبة إلى معظم الكلمات المفتاحية، يعتمد sshd أول ظهور يقرأه. ويتضمن sshd_config في Ubuntu ملف sshd_config.d/*.conf بترتيب معجمي قبل جسم إعداداته. تأتي صور السحابة لـUbuntu 24.04 مزوّدة مسبقاً بملف 60-cloudimg-settings.conf في ذلك الدليل، كما يضيف المزوّدون الذين يفعّلون تسجيل الدخول بكلمة المرور عبر cloud-init ملف 50-cloud-init.conf يحتوي على PasswordAuthentication yes. تسمية ملفنا 00-hardening.conf تجعله يأتي أولاً، فيتغلب على الملفين.
ترتيب المهام هو إجراء أمان الجدار الناري. ينفّذ Allow OpenSSH قبل Enable ufw ذي سياسة المنع. ينفّذ Ansible المهام بالترتيب المدرج بدقة، لذلك توجد الفتحة قبل تفعيل الجدار. لا يحتاج fail2ban إلى إعداد ليكون مفيداً هنا؛ إذ تراقب إعداداته الافتراضية في Ubuntu خدمة sshd مباشرة. وتتناول دليل fail2ban على Ubuntu 24.04 ما تفعله السجون فعلياً وما الذي يمكن ضبطه.
الخطوة 6: نفّذ التشغيل التجريبي باستخدام --check، ثم شغّل الخطة فعلياً
ansible-playbook site.yml --checkيتصل وضع التحقق، ويحسب ما كان سينفذه، ولا يغيّر شيئاً. اقرأ عدد changed= في PLAY RECAP في الأسفل؛ فهذا هو عدد المهام التي كانت ستعدّل كل مضيف. توجد ملاحظة مهمة: يفرض وضع التحقق حداً بنيوياً عندما تعتمد مهمة لاحقة على تغييرات مهمة سابقة. تتضمن صورة Ubuntu القياسية للخادم الحزمة ufw مسبقاً، لذلك ينفذ هذا الـplaybook التشغيل التجريبي بنجاح. لكن في صورة مصغّرة لا تتضمنها، تفشل مهام ufw في وضع التحقق، لأن هذا الوضع لم يثبّت الحزمة فعلياً، ولا تملك الوحدة بعد ذلك شيئاً تستدعيه. هذا حد من حدود التشغيل التجريبي، وليس خطأً في الـplaybook. عندما تبدو الخطة صحيحة:
ansible-playbook site.ymlتطبع كل مهمة سطراً لكل مضيف: changed باللون الأصفر، وok باللون الأخضر، ويجب أن يظهر الملخص كما يلي:
PLAY RECAP *********************************************************************
web1 : ok=10 changed=9 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
web2 : ok=10 changed=9 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0يتكوّن العدد عشرة ok من جمع الحقائق، وثماني مهام، والمعالج. يُسمح بأن يختلف changed لديك عن نتيجتي بمقدار واحد أو اثنين: تتضمن صورة Ubuntu القياسية مسبقاً ufw وunattended-upgrades، كما تبدأ fail2ban تلقائياً فور تثبيتها بواسطة apt. لذلك يمكن لمهمة أن تعرض ok بشكل صحيح في تشغيلها الأول، لأن الحالة التي تعلنها متحققة مسبقاً. يجب أن يكون الرقمان unreachable وfailed صفراً. ملاحظة بشأن become: true: لا يتجاوز كونه إجراءً شكلياً أثناء اتصالك بحساب root، لكن بمجرد تغيير ansible_user إلى deploy، يصبح sudo مستخدماً فعلياً، ويضمن ملف sudoers الذي يثبته هذا الـplaybook باستخدام NOPASSWD عدم ظهور -K في سطر أوامرك. من دونه، ستظهر Missing sudo password، كما هو موضح أدناه.
الخطوة 7: شغّله مرتين لترى معنى التنفيذ المتكرر الآمن
شغّل الأمر نفسه مرة أخرى فوراً:
web1 : ok=9 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0انخفضت changed=0 وok بمقدار واحد لأن المعالج الذي لم يُخطر به لم يُشغَّل. لم يُعَد تثبيت أي شيء، ولم تتم إعادة تشغيل sshd، ولم يتم تعديل ufw. هذا ما يجعل الـplaybook أداة تدقيق بقدر ما هو أداة إعداد: أضف web3 إلى الجرد في الشهر المقبل وأعد التشغيل، وسيُجهَّز الخادم الجديد، بينما تُتحقَّق الخوادم القديمة. تشير قيمة changed غير الصفرية على خادم لم تلمسه إلى حدوث انحراف عن الإعداد، وتخبرك بأن شخصاً عدّل يدوياً ما كان ينبغي تعديله في الـplaybook.
من هنا يتوسع هذا النمط. الـplaybook التالي الجدير بالكتابة ينشئ شبكة WireGuard VPN على VPS نفسه ويشدّد قاعدة ufw بحيث لا يستجيب SSH إلا عبر النفق؛ وبعد ذلك اكتب playbook يثبّت Docker وCompose على كل خادم تطبيقات. عندما يتجاوز site.yml ثلاث شاشات، قسّمه إلى roles، ولكن ليس قبل ذلك.
أنماط الفشل، مع النصوص التي ستظهر لك
UNREACHABLE مع Permission denied.
web1 | UNREACHABLE! => {
"changed": false,
"msg": "Failed to connect to the host via ssh: root@10.0.0.10: Permission denied (publickey).",
"unreachable": true
}فشل نقل SSH قبل تشغيل أي وحدة: إعداد ansible_user غير صحيح، أو لم يُنسخ المفتاح إلى ذلك المضيف، أو يجري عرض مفتاح غير صحيح. أعد الاختبار باستخدام ssh root@10.0.0.10 العادي، ثم استخدم ssh -v لمعرفة المفاتيح التي عُرضت. إذا نجح اتصال SSH باستخدام كلمة مرور، ولم ينجح Ansible، فقد تخطيت ssh-copy-id.
كلمة مرور sudo مفقودة.
web1 | FAILED! => {
"msg": "Missing sudo password"
}عيّنت become: true، واتصلت كمستخدم غير root، ويحتاج ذلك المستخدم إلى كلمة مرور لاستخدام sudo. أضف -K (--ask-become-pass) إلى سطر الأوامر، أو امنح المستخدم إدخال NOPASSWD في sudoers. ولهذا السبب تحديداً يثبّت playbook هذا الإدخال للمستخدم deploy قبل أن تنتقل إليه.
error: externally-managed-environment. شغّلت pip على Python النظام في Ubuntu 24.04. عولج ذلك في الخطوة 1: استخدم pipx، وليس pip، ولا تستخدم --break-system-packages.
mapping values are not allowed in this context.
ERROR! Syntax Error while loading YAML.
mapping values are not allowed in this contextالسبب في الغالب هو المسافة البادئة: مفتاح موجود في مستوى غير صحيح، أو عدم وجود مسافة بعد النقطتين. يشير رقم السطر المبلّغ عنه إلى موضع قريب من الخطأ، وليس بالضرورة إلى الخطأ نفسه؛ فتحقق من السطر السابق أيضاً. أما الخطأ المشابه found character '\t' that cannot start any token فيعني دخول علامة تبويب؛ إذ يمنع YAML استخدامها. اجعل ansible-playbook site.yml --syntax-check خطوة تلقائية قبل كل تشغيل، واضبط المحرر على استخدام مسافتين للمسافة البادئة في YAML.
/usr/bin/python3: not found. نادراً ما يظهر هذا الخطأ في صور Ubuntu 24.04 القياسية، لكنه شائع في الصور المصغّرة أو صور netboot: يفشل تنفيذ الوحدة لأن الهدف لا يحتوي على Python. ثبّته باستخدام الوحدة raw، وهي الوحدة الوحيدة التي لا تحتاج إلى شيء في الطرف الآخر: ansible all -m raw -a "apt-get update && apt-get install -y python3" --become، ثم أعد تشغيل playbook.
FAQ
هل أحتاج إلى تثبيت Ansible على الخوادم التي يديرها؟
لا. يعمل Ansible دون وكيل: تدفع آلة التحكم وحدات Python صغيرة عبر SSH، وتشغّلها، ثم تزيلها. لا يحتاج الخادم الهدف إلا إلى python3 وإمكانية الوصول عبر SSH، وهما متوفران في صور Ubuntu القياسية. التثبيت الوحيد في هذا الدليل بالكامل يتم على آلة التحكم.
لماذا يعرض Ansible الرسالة "Permission denied (publickey)"؟
تعني كتلة UNREACHABLE! مع Permission denied (publickey) أن مصادقة SSH فشلت قبل أن يشغّل Ansible أي شيء. تحقق من أن ansible_user في ملف الجرد يطابق الحساب الذي أعددته فعلياً، وأنك نفذت ssh-copy-id للوصول إلى ذلك المضيف، وأن ssh user@host العادي يسجّل الدخول دون كلمة مرور. أي إصلاح للأمر ssh العادي سيصلح Ansible، لأنهما يستخدمان وسيلة النقل نفسها.
ماذا يعني idempotent في Ansible؟
تعلن المهمة عن حالة مطلوبة، مثل "هذه الحزمة موجودة" أو "هذا السطر موجود في هذا الملف"، بدلاً من إعلان إجراء لتنفيذه. إذا كانت الحالة متحققة بالفعل، فلا ينفذ Ansible أي شيء ويعرض ok بدلاً من changed. لذلك يعرض تشغيل playbook مرتين changed=0 في المرة الثانية، ولأن إعادة التشغيل تمثل تدقيقاً آمناً وليست إعادة تثبيت محفوفة بالمخاطر.
هل أستخدم pip أم pipx لتثبيت Ansible على Ubuntu 24.04؟
استخدم pipx. يضع Ubuntu 24.04 علامة على Python الخاص بالنظام باعتباره مُداراً خارجياً، ولذلك يفشل pip install ansible مع error: externally-managed-environment وفق التصميم. يضع pipx install --include-deps ansible Ansible في virtualenv معزول، ويتيح ansible وansible-playbook وبقية الأدوات في PATH بطريقة سليمة.
ما الفرق بين الحزمتين ansible وansible-core؟
تحتوي ansible-core على المحرك، إضافة إلى وحدات ansible.builtin فقط. أما حزمة ansible فتضم النواة مع مجموعات المجتمع المنسقة، بما في ذلك ansible.posix (وحدة authorized_key) وcommunity.general (وحدة ufw)، وتُستخدم كلتاهما في هذا الدليل. ابدأ بالحزمة الكاملة، ثم انتقل إلى core مع مجموعات مختارة يدوياً فقط عندما يكون لديك سبب لذلك.