دليل Ansible: أول playbook لك على VPS
ثبّت Ansible بواسطة pipx على Ubuntu 24.04، واكتب ملف جرد وأول playbook يحصّن خادم VPS جديدًا، مع حلول لأخطاء Permission denied وsudo.
ما الذي ستبنيه
جهاز تحكم واحد مثبَّت عليه Ansible، وخادم VPS واحد أو أكثر بنظام Ubuntu 24.04 جديد لا يحمل شيئًا سوى الصورة الأصلية للنظام. في النهاية سيكون لديك ملف جرد (inventory) يسمّي خوادمك، وأمر ping من نوع ad hoc يثبت أن المصادقة تعمل تمامًا من الطرف إلى الطرف، وplaybook ينفّذ كل قائمة تحقق الخادم الجديد في صورة كود: مستخدم deploy بمفتاح SSH الخاص بك، وsshd مُحصَّن، وfail2ban، والتحديثات الأمنية التلقائية (unattended-upgrades)، وجدار حماية يسمح بـ OpenSSH قبل أن يرفض كل ما عداه. وجّهه إلى خادم واحد أو إلى عشرين خادمًا. شغّله مرتين، والتشغيل الثاني لا يغيّر شيئًا — وهذا هو المطلوب بالضبط.
بعد خمسة عشر عامًا من تجهيز خوادم VPS، أستطيع أن أصف لك النمط الصادق: يجهّز الجميع الخوادم الخمسة الأولى يدويًا، ثم يخسر عطلة نهاية أسبوع كاملة مع الخادم السادس لأن لا أحد يتذكر ما فعله بها. يعمّق هذا الدليل الاستعراض الوارد في إدارة عدة خوادم Linux — انتقل إليه في اليوم الذي تجد فيه نفسك تكتب الأمر نفسه apt install في ثلاث نوافذ طرفية.
ما هو Ansible فعلًا، في فقرة واحدة
Ansible بلا عميل (agentless). لا يوجد أي برنامج خفي (daemon) يُثبَّت على الخوادم التي يديرها: يتصل جهاز التحكم عبر SSH عادي، وينسخ وحدة (module) بايثون صغيرة إلى الجهاز الهدف، وينفّذها، ويقرأ ما تطبعه من JSON، ثم يحذفها. الشيء الوحيد الذي يحتاجه الجهاز الهدف هو python3، وهو موجود مسبقًا في كل صورة Ubuntu أصلية. الكلمة المهمة هنا هي idempotent، وتعني شيئًا بسيطًا: المهمة (task) تصف حالة، لا فعلًا. فـstate: present لحزمة يعني «تأكد من أن هذه الحزمة مثبَّتة»، لا «شغّل المثبِّت». وإذا كانت الحالة متحققة أصلًا، فإن Ansible لا يمسّ شيئًا ويبلّغ عنها بـok بدلًا من changed. هذه الخاصية هي المنتج كله — فهي ما يجعل إعادة تشغيل playbook أمرًا آمنًا، والإعادة الآمنة هي ما يحوّل سكربت shell إلى بنية تحتية (infrastructure).
المتطلبات المسبقة، والمزالق منذ البداية
- جهاز التحكم: حاسوبك المحمول أو خادم 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 جزء من الصياغة نفسها. الإزاحة (indent) الخاطئة تنتج
mapping values are not allowed in this context، ووجود حرف tab في أي مكان قاتل للملف. - أبقِ جلسة 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 علامة على بايثون النظام بأنه مُدار خارجيًا (externally managed، وفق 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 وبقية الأوامر هي نقاط دخول (entry points) تابعة لاعتماديتها ansible-core — فمن دون هذا الخيار يرفض pipx التثبيت برسالة No apps associated with package ansible or its dependencies. وثبّت حزمة ansible نفسها، لا ansible-core وحدها — فالحزمة الكاملة تضم مجموعات (collections) المجتمع، ويستخدم هذا الـ playbook وحدات من اثنتين منها (ansible.posix وcommunity.general).
ansible --versionالنتيجة الصحيحة تبدأ بسطر يشبه ansible [core 2.19.x] ويذكر إصدار بايثون الذي يعمل تحته؛ وأي إصدار حالي من 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: الجرد (inventory) — 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، ونسخ الوحدة، وتنفيذ بايثون على الجهاز الهدف، ثم التنظيف. النتيجة الصحيحة تكون خضراء، كتلة واحدة لكل مضيف:
web1 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
},
"changed": false,
"ping": "pong"
}SUCCESS الخضراء تعني أن المصادقة ومفسِّر بايثون والنقل كلها تعمل — وسيعمل الـ 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الأسطر التي تستحق أن تُفهم لا أن تُنسَخ فحسب:
المتغيرات (variables) موجودة تحت vars: ويُشار إليها بـ"{{ deploy_user }}" — ضع علامات التنصيص حول التعبير كاملًا حين تبدأ القيمة بقوس معقوف، وإلا أساء مُحلِّل YAML قراءتها. وlookup('file', ...) يقرأ مفتاحك العام من جهاز التحكم وقت التشغيل، لذا لا يحمل الـ playbook أي مادة مفتاحية.
الحلقة (loop). يشغّل loop: "{{ baseline_services }}" مهمة الخدمة مرة واحدة لكل عنصر، وتُظهر المخرجات كل عنصر في سطره الخاص. لاحظ أن مهمة apt، على العكس، تأخذ قائمة الحزم كاملة دفعة واحدة — فمعاملة apt واحدة أسرع، وهي النمط المفضَّل للحزم؛ أما الحلقات فمخصصة للوحدات التي تعمل فعلًا على عنصر واحد في كل مرة.
المُعالِج (handler) هو المفهوم الذي يجب أن تستوعبه جيدًا. notify: Restart ssh لا يعني «أعد تشغيل ssh الآن». بل يضع المُعالِج (handler) في طابور الانتظار، وهذا يعمل مرة واحدة في نهاية الـ play، فقط إذا أبلغت المهمة المُخطِرة فعلًا عن changed. أعد تشغيل الـ playbook غدًا: ملف drop-in صحيح أصلًا، ومهمة copy تبلّغ عن ok، ولا يُعاد تشغيل sshd أبدًا. سطر validate: هو مزلاج الأمان على الزناد — يفحص sshd الملف قبل أن يستبدل به الملف القديم، فأي خطأ كتابي يُفشِل المهمة بدلًا من أن يعطِّل البرنامج الخفي.
PermitRootLogin prohibit-password، لا no — وذلك عن قصد. هذا الـ playbook يسجّل الدخول بحساب root بمفتاح. وprohibit-password يوقف تسجيل دخول root بكلمة مرور مع إبقاء دخولك أنت حيًّا. وبمجرد إثبات عمل مستخدم deploy (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 منذ البداية، وما تفعله السجون (jails) فعليًا — وما يمكن ضبطه — مشروح في دليل fail2ban على Ubuntu 24.04.
الخطوة 6: تشغيل تجريبي بالخيار --check، ثم التشغيل الفعلي
ansible-playbook site.yml --checkيتصل وضع الفحص (check mode)، ويحسب ما كان سيفعله، ولا يغيّر شيئًا. اقرأ عدّاد changed= في PLAY RECAP أسفل المخرجات — فهو عدد المهام التي كانت ستُعدِّل كل مضيف. وتحفّظ صادق واحد: لوضع الفحص حدّ بنيوي كلما اعتمدت مهمة لاحقة على تغييرات مهمة سابقة. صورة خادم Ubuntu القياسية تأتي بـ ufw مثبَّتًا مسبقًا، فيمر هذا الـ playbook بتشغيل تجريبي نظيف؛ أما على صورة مصغّرة لا يوجد فيها ufw، فإن مهام ufw تفشل في وضع الفحص، لأن وضع الفحص لم يثبّت الحزمة فعليًا في أي وقت، فلا تجد الوحدة (module) شيئًا لتستدعيه. هذا حدّ من حدود التشغيل التجريبي، لا خلل في 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الرقم 10 في ok هو مجموع جمع الحقائق (fact-gathering) زائد ثماني مهام زائد المُعالِج (handler). ويُسمح لقيمة changed لديك أن تختلف عن قيمتي بمهمة أو اثنتين: صورة Ubuntu القياسية تأتي مسبقًا بـufw وunattended-upgrades، ويبدأ fail2ban نفسه تلقائيًا لحظة أن يثبّته apt، لذا يمكن لمهمة أن تبلّغ بشرعية عن ok في أول تشغيل لها — لأن الحالة التي تعلنها كانت متحققة أصلًا. أما الأرقام التي يجب أن تكون صفرًا فهي unreachable وfailed. وملاحظة بخصوص become: true: إنه إجراء شكلي ما دمت متصلًا بحساب root، لكن بمجرد أن تحوّل ansible_user إلى deploy، يصبح sudo حقيقيًا — وملف sudoers بخيار NOPASSWD الذي يثبّته هذا الـ playbook هو بالضبط ما يبقي -K بعيدًا عن سطر أوامرك. ومن دونه ستحصل على Missing sudo password، وهو مشروح أدناه.
الخطوة 7: شغّله مرتين — كيف تبدو idempotence
شغّل الأمر نفسه مرة أخرى فورًا:
web1 : ok=9 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0changed=0، وانخفضت ok بمقدار واحد لأن المُعالِج (handler) غير المُخطَر لم يعمل أبدًا. لم يُعَد تثبيت شيء، ولم يُعَد تشغيل sshd، ولم يُمَس ufw. هذا ما يجعل الـ playbook تدقيقًا (audit) بقدر ما هو أداة تجهيز: أضف web3 إلى الجرد الشهر المقبل وأعد التشغيل — يُبنى الخادم الجديد، ويُعاد التحقق من الخوادم القديمة. وchanged غير الصفري على خادم لم تلمسه انحراف (drift)، ويخبرك أن أحدًا عدّل يدويًا ما كان يجب تعديله في الـ playbook.
من هنا يتراكم النمط. الـ playbook التالي الذي يستحق الكتابة يُقيم شبكة WireGuard VPN مستضافة ذاتيًا على الخادم نفسه ويشدّد قاعدة 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.
Missing sudo password.
web1 | FAILED! => {
"msg": "Missing sudo password"
}ضبطت become: true، واتصلت بحساب غير root، وهذا الحساب يحتاج إلى كلمة مرور لـ sudo. إما أن تضيف -K (أي --ask-become-pass) إلى سطر الأوامر، أو تمنح الحساب إدخال sudoers بخيار NOPASSWD — وهذا بالضبط سبب تثبيت الـ playbook إدخالًا من هذا النوع لحساب deploy قبل أن تنتقل إليه أصلًا.
error: externally-managed-environment. شغّلت pip على بايثون النظام في 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يكاد الأمر يكون دائمًا مسألة إزاحة (indentation): مفتاح في عمق خاطئ، أو مسافة ناقصة بعد نقطتين. رقم السطر المُبلَّغ عنه يشير إلى قرب الخطأ لا إلى موضعه بالضبط — راجع السطر الذي قبله أيضًا. أما نظيرتها found character '\t' that cannot start any token فتعني أن حرف tab تسلّل إلى الملف؛ وYAML يحظره تمامًا. اجعل ansible-playbook site.yml --syntax-check ردّة فعل تلقائية قبل كل تشغيل، واضبط محرِّرك على إزاحة بمسافتين لملفات YAML.
/usr/bin/python3: not found. نادر على صور Ubuntu 24.04 القياسية، وشائع على الصور المصغَّرة أو صور netboot: يفشل تنفيذ الوحدة لأن الجهاز الهدف لا يملك بايثون. جهّزه بوحدة raw، الوحدة الوحيدة التي لا تحتاج إلى شيء في الطرف الآخر: ansible all -m raw -a "apt-get update && apt-get install -y python3" --become، ثم أعد تشغيل الـ playbook.
FAQ
هل تحتاج إلى تثبيت Ansible على الخوادم التي يديرها؟
لا. Ansible بلا عميل (agentless): يدفع جهاز التحكم وحدات بايثون صغيرة عبر SSH، ويشغّلها، ثم يزيلها. لا يحتاج الجهاز الهدف إلا إلى python3 والوصول عبر SSH، وكلاهما موجود مسبقًا في صور Ubuntu الأصلية. والتثبيت الوحيد في هذا الدليل كله يحدث على جهاز التحكم لديك.
لماذا تظهر رسالة «Permission denied (publickey)» من Ansible؟
كتلة UNREACHABLE! مع Permission denied (publickey) تعني أن مصادقة SSH فشلت قبل أن يشغّل Ansible أي شيء. تحقق من أن ansible_user في الجرد يطابق الحساب الذي أعددته فعلًا، وأنك نفّذت ssh-copy-id إلى ذلك المضيف، وأن ssh user@host المباشر يسجّل الدخول من دون كلمة مرور. وأي إصلاح يعمل مع أمر ssh المباشر يصلح Ansible أيضًا، لأنهما يستخدمان النقل نفسه.
ما معنى idempotent في Ansible؟
المهمة (task) تعلن حالة مرغوبة — «هذه الحزمة موجودة»، «هذا السطر في هذا الملف» — لا فعلًا يُنفَّذ. وإذا كانت الحالة متحققة أصلًا، فإن Ansible لا يفعل شيئًا ويبلّغ عن ok بدلًا من changed. ولهذا يُظهر تشغيل الـ playbook مرتين قيمة changed=0 في المرة الثانية، ولهذا تكون إعادة التشغيل تدقيقًا آمنًا لا إعادة تثبيت محفوفة بالمخاطر.
هل تستخدم pip أم pipx لتثبيت Ansible على Ubuntu 24.04؟
pipx. يضع Ubuntu 24.04 علامة على بايثون النظام بأنه مُدار خارجيًا، لذا يفشل pip install ansible برسالة error: externally-managed-environment بتصميم مقصود. ويضع pipx install --include-deps ansible Ansible في بيئة افتراضية معزولة، ويعرض ansible وansible-playbook وبقية الأوامر على PATH لديك بنظافة تامة.
ما الفرق بين حزمتَي ansible وansible-core؟
ansible-core هو المحرك مضافًا إليه وحدات ansible.builtin فقط. أما حزمة ansible فتضم core مع مجموعات المجتمع (collections) المنتقاة — بما فيها ansible.posix (وحدة authorized_key) وcommunity.general (وحدة ufw)، وكلتاهما مستخدمتان في هذا الدليل. ابدأ بالحزمة الكاملة؛ واختصر إلى core مع مجموعات مختارة يدويًا فقط حين يكون لديك سبب لذلك.