SSD Nodes Learn Hosting plans →
راهنماها Matt Connorتوسط Matt Connor · به‌روزرسانی شده 2026-08-07

آموزش Ansible: اجرای اولین Playbook روی VPS

نصب Ansible با pipx در Ubuntu 24.04 و نوشتن اولین Playbook برای ایمن‌سازی سرور. راهنمای عملی رفع خطاهای Permission denied و sudo برای مدیریت حرفه‌ای VPS.

آنچه می‌سازید

یک ماشین کنترل با Ansible نصب‌شده و یک یا چند VPS تازه با Ubuntu 24.04 که تنها شامل image پیش‌فرض هستند. در پایان، شما یک فایل inventory خواهید داشت که نام سرورهایتان را مشخص می‌کند، یک دستور ad-hoc ping که صحت احراز هویت را در کل مسیر تأیید می‌کند، و یک playbook که کل چک‌لیست VPS جدید را به صورت کد اجرا می‌کند: یک کاربر deploy با کلید SSH شما، sshd مقاوم‌سازی‌شده، fail2ban، به‌روزرسانی‌های خودکار (unattended upgrades) و یک فایروال که پیش از مسدود کردن همه ترافیک، دسترسی OpenSSH را مجاز می‌شمارد. این تنظیمات را روی یک سرور یا بیست سرور اعمال کنید. آن را دو بار اجرا کنید؛ اجرای دوم نباید هیچ تغییری ایجاد کند، این دقیقاً هدف اصلی است.

پس از 15 سال تأمین (provisioning) سرورهای VPS، می‌توانم الگوی صادقانه را به شما بگویم: همه افراد 5 سرور اول را به صورت دستی تنظیم می‌کنند، سپس در سرور ششم یک آخر هفته را از دست می‌دهند، چون هیچ‌کس به یاد نمی‌آورد که با 5 سرور اول چه کرده است. این راهنما بررسی عمیق‌تری در مدیریت چندین سرور لینوکسی ارائه می‌دهد؛ روزی که خود را در حال تایپ دستورات تکراری apt install در سه ترمینال مختلف یافتید، این راهنما را دنبال کنید.

Ansible در یک پاراگراف چیست

Ansible بدون نیاز به agent کار می‌کند. هیچ daemonای برای نصب روی سرورهای تحت مدیریت وجود ندارد: ماشین کنترل‌کننده از طریق SSH معمولی متصل می‌شود، یک ماژول کوچک Python را به مقصد کپی می‌کند، آن را اجرا کرده، خروجی JSON را می‌خواند و سپس آن را حذف می‌کند. تنها پیش‌نیاز مقصد python3 است که در تمامی ایمیج‌های پیش‌فرض Ubuntu وجود دارد. کلمه کلیدی در اینجا idempotent (تکرارپذیر) است و معنای ساده‌ای دارد: یک task وضعیت نهایی (state) را توصیف می‌کند، نه یک عملیات اجرایی را. state: present برای یک پکیج به این معناست که «اطمینان حاصل کن این پکیج نصب است»، نه اینکه «نصب‌کننده را اجرا کن». اگر وضعیت از قبل برقرار باشد، Ansible هیچ تغییری ایجاد نمی‌کند و به جای changed، وضعیت را ok گزارش می‌دهد. این ویژگی تمام ماهیت محصول است؛ همان چیزی که اجرای مجدد یک 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 استفاده کنید.
  • فضای خالی (whitespace) در YAML بخشی از سینتکس است. تورفتگی اشتباه منجر به mapping values are not allowed in this context می‌شود و وجود کاراکتر tab در هر جای فایل، باعث شکست عملیات خواهد شد.
  • هنگام اجرای playbook برای مقاوم‌سازی sshd، یک نشست SSH فعال روی هر مقصد باز نگه دارید. تمام مواردی که به مشتریان برای رفع مشکل قفل‌شدن کمک کرده‌ام، ناشی از بستن آخرین نشست «برای تست از وضعیت تمیز» بوده است.

گام 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 ansible
error: 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.

اوبونتو 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

پس از pipx ensurepath یک شل جدید باز کنید تا تغییرات PATH اعمال شود. --include-deps تزئینی نیست: بسته ansible هیچ اسکریپت کنسولی از خود ندارد، ansible، ansible-playbook و بقیه، نقاط ورود وابستگی ansible-core آن هستند، بنابراین بدون این فلگ، pipx با خطای No apps associated with package ansible or its dependencies از نصب خودداری می‌کند. همچنین بسته ansible را نصب کنید، نه فقط ansible-core؛ بسته کامل شامل کالکشن‌های کامیونیتی است و این پلی‌بوک از ماژول‌های دو مورد از آن‌ها (ansible.posix و community.general) استفاده می‌کند.

ansible --version

نتیجه صحیح با خطی مانند ansible [core 2.19.x] شروع می‌شود و پایتونی که تحت آن اجرا می‌شود را نام می‌برد؛ هر نسخه فعلی core برای تمام موارد اینجا مناسب است. در مقابل، ansible: command not found به این معنی است که ~/.local/bin هنوز در PATH شما نیست، شل جدید باز نشده است، یا source ~/.bashrc رخ داده است.

این تمام مراحل نصب است. ماشین‌های هدف هیچ چیزی دریافت نمی‌کنند.

گام 2: دسترسی SSH key به تمام میزبان‌های مقصد

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=root

web1 یک نام مستعار است که شما انتخاب می‌کنید؛ این نام در خروجی نمایش داده می‌شود و با --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، پینگ سبزی که همه‌چیز را تأیید می‌کند

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: اولین پلی‌بوک، چک‌لیست سرور مجازی جدید به صورت کد

این تمام کارهایی است که در ده دقیقه اول روی یک سرور جدید به صورت دستی انجام می‌دهید. آن را با نام 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', ...) کلید عمومی شما را در زمان اجرا از ماشین کنترل‌کننده می‌خواند، بنابراین پلی‌بوک هیچ دادهٔ حساسی از کلیدها را با خود حمل نمی‌کند.

حلقه (Loop). دستور loop: "{{ baseline_services }}" وظیفهٔ سرویس را برای هر آیتم یک‌بار اجرا می‌کند و خروجی، هر آیتم را در یک خط جداگانه نمایش می‌دهد. توجه کنید که وظیفهٔ apt کل لیست بسته‌ها را یک‌جا دریافت می‌کند؛ یک تراکنش apt سریع‌تر است و الگوی ترجیحی برای بسته‌ها محسوب می‌شود. حلقه‌ها برای ماژول‌هایی هستند که واقعاً در هر لحظه روی یک مورد خاص عمل می‌کنند.

هندلر (Handler) مفهومی است که باید درونی‌سازی شود. دستور notify: Restart ssh به معنای «همین الان ssh را ری‌استارت کن» نیست. این دستور هندلر را در صف قرار می‌دهد که فقط یک‌بار در پایان پلی‌بوک اجرا می‌شود، و آن هم فقط در صورتی که وظیفهٔ اطلاع‌رسان (notifying task) واقعاً وضعیت changed را گزارش کرده باشد. اگر فردا پلی‌بوک را دوباره اجرا کنید، فایل drop-in از قبل صحیح است، وظیفهٔ کپی وضعیت ok را گزارش می‌کند و sshd هرگز ری‌استارت نمی‌شود. خط validate: ضامن ایمنی روی ماشه است؛ sshd پیش از جایگزینی فایل قدیمی، آن را بررسی می‌کند، بنابراین یک غلط تایپی باعث شکست خوردن وظیفه می‌شود و دیمون را از کار نمی‌اندازد.

عمداً PermitRootLogin prohibit-password و نه no. این پلی‌بوک با کاربر root و از طریق کلید وارد می‌شود. دستور prohibit-password ورود با رمز عبور برای root را می‌بندد در حالی که دسترسی شما را باز نگه می‌دارد. هنگامی که کاربر deploy تأیید شد (ssh deploy@10.0.0.10 sudo true، آدرس ساده، زیرا web1 فقط یک نام مستعار است که فقط Ansible آن را می‌شناسد)، در اجرای بعدی ansible_user=deploy را در inventory تغییر دهید و آن را به no محدود کنید. سخت‌سازی (Hardening) را به ترتیبی انجام دهید که دسترسی شما قطع نشود.

پیشوند 00- اهمیت دارد. برای اکثر کلمات کلیدی، sshd اولین موردی را که تجزیه می‌کند می‌پذیرد و فایل sshd_config در اوبونتو شامل sshd_config.d/*.conf به ترتیب الفبایی پیش از بدنهٔ اصلی خود است. ایمیج‌های ابری اوبونتو 24.04 از قبل یک فایل 60-cloudimg-settings.conf در آن دایرکتوری دارند و ارائه‌دهندگانی که ورود با رمز عبور را از طریق cloud-init فعال می‌کنند، یک فایل 50-cloud-init.conf با PasswordAuthentication yes اضافه می‌کنند؛ نام‌گذاری فایل ما به صورت 00-hardening.conf باعث می‌شود که اول مرتب شده و بر هر دو پیروز شود.

ترتیب وظایف برای ایمنی فایروال حیاتی است. دستور Allow OpenSSH پیش از Enable ufw با سیاست deny اجرا می‌شود. Ansible وظایف را دقیقاً به ترتیبی که لیست شده‌اند اجرا می‌کند، بنابراین حفره پیش از بالا رفتن دیوار وجود دارد. fail2ban برای مفید بودن در اینجا نیازی به پیکربندی ندارد؛ تنظیمات پیش‌فرض آن در اوبونتو به صورت خودکار sshd را زیر نظر می‌گیرند و جزئیات عملکرد jailها و نحوهٔ تنظیم آن‌ها در راهنمای fail2ban در اوبونتو 24.04 پوشش داده شده است.

گام 6: اجرای آزمایشی با --check و سپس اجرای نهایی

ansible-playbook site.yml --check

حالت check متصل می‌شود، محاسبه می‌کند که چه کاری انجام خواهد شد و هیچ تغییری اعمال نمی‌کند. تعداد changed= را در PLAY RECAP در پایین صفحه بخوانید؛ این عدد نشان‌دهنده تعداد تسک‌هایی است که روی هر میزبان تغییر ایجاد می‌کنند. یک نکته مهم: حالت check محدودیت ساختاری دارد، هر جا که یک تسک بعدی به تغییرات یک تسک قبلی وابسته باشد. ایمیج استاندارد سرور Ubuntu به‌صورت پیش‌فرض ufw را دارد، بنابراین این playbook در حالت آزمایشی بدون مشکل اجرا می‌شود، اما در یک ایمیج حداقلی که فاقد آن است، تسک‌های ufw در حالت check با خطا مواجه می‌شوند، زیرا حالت check هرگز بسته را نصب نمی‌کند و ماژول چیزی برای فراخوانی ندارد. این محدودیت اجرای آزمایشی است، نه باگ در 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 با تنظیم NOPASSWD که این playbook نصب می‌کند، دقیقاً همان چیزی است که -K را از خط فرمان شما دور نگه می‌دارد. بدون آن، شما با Missing sudo password مواجه می‌شوید که در ادامه توضیح داده شده است.

گام 7: اجرای مجدد، مفهوم ایدمپوتنس (Idempotence)

همان دستور را بلافاصله دوباره اجرا کنید:

web1 : ok=9  changed=0  unreachable=0  failed=0  skipped=0  rescued=0  ignored=0

changed=0، و ok یک واحد کاهش می‌یابد زیرا هندلرِ بدون اعلان، هرگز اجرا نشد. هیچ‌چیز دوباره نصب نشد، sshd ری‌استارت نشد و ufw تغییر نکرد. این همان ویژگی است که باعث می‌شود پلی‌بوک (playbook) علاوه بر ابزار پیکربندی، یک ابزار حسابرسی نیز باشد: ماه آینده web3 را به اینونتوری اضافه کنید و دوباره اجرا کنید؛ سرور جدید ساخته می‌شود و سرورهای قدیمی تأیید می‌شوند. مقدار غیرصفر changed روی سروری که به آن دست نزده‌اید، نشان‌دهنده انحراف (drift) است و به شما می‌گوید که شخصی به‌صورت دستی چیزی را تغییر داده که باید در پلی‌بوک ویرایش می‌شد.

از اینجا به بعد، الگوها ترکیب می‌شوند. پلی‌بوک بعدی که ارزش نوشتن دارد، یک WireGuard VPN روی همان VPS راه‌اندازی می‌کند و قوانین ufw را محدود می‌کند تا SSH فقط روی تونل پاسخ دهد؛ پس از آن، پلی‌بوکی که 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 را روی پایتون سیستمی در 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) است: یک کلید در عمق اشتباه قرار گرفته یا یک فضای خالی پس از دو نقطه (colon) فراموش شده است. شماره خط گزارش‌شده نزدیک به محل اشتباه را نشان می‌دهد، نه دقیقاً خود آن را؛ خط قبلی را نیز بررسی کنید. خطای مشابه found character '\t' that cannot start any token به این معناست که یک کاراکتر tab وارد فایل شده است؛ YAML استفاده از tab را ممنوع کرده است. اجرای ansible-playbook site.yml --syntax-check را پیش از هر بار اجرا به یک عادت تبدیل کنید و ویرایشگر خود را برای YAML روی دو فضای خالی (two-space) تنظیم نمایید.

/usr/bin/python3: not found. این خطا در ایمیج‌های استاندارد Ubuntu 24.04 نادر است، اما در نسخه‌های minimal یا netboot رایج است: اجرای ماژول شکست می‌خورد زیرا مقصد پایتون ندارد. آن را با ماژول raw بوت‌استرپ کنید؛ این تنها ماژولی است که در سمت مقصد به هیچ پیش‌نیازی نیاز ندارد: ansible all -m raw -a "apt-get update && apt-get install -y python3" --become، سپس playbook را دوباره اجرا کنید.

FAQ

آیا نیاز دارم Ansible را روی سرورهایی که مدیریت می‌کند نصب کنم؟

خیر. Ansible بدون نیاز به agent کار می‌کند: ماشین کنترل‌کننده ماژول‌های کوچک Python را از طریق SSH ارسال و اجرا کرده و سپس آن‌ها را حذف می‌کند. مقصد فقط به python3 و دسترسی SSH نیاز دارد که هر دو در ایمیج‌های استاندارد Ubuntu موجود هستند. تنها نصب در کل این راهنما، روی ماشین کنترل‌کننده شما انجام می‌شود.

چرا Ansible خطای "Permission denied (publickey)" می‌دهد؟

بلاک UNREACHABLE! همراه با Permission denied (publickey) به این معناست که احراز هویت SSH پیش از اجرای هر کاری توسط Ansible شکست خورده است. بررسی کنید که ansible_user در inventory با حسابی که واقعاً تنظیم کرده‌اید مطابقت داشته باشد، دستور ssh-copy-id را برای آن میزبان اجرا کرده باشید و دستور ساده ssh user@host بدون نیاز به رمز عبور وارد شود. هر چیزی که دستور ساده ssh را اصلاح کند، مشکل Ansible را نیز حل می‌کند، زیرا هر دو از یک بستر انتقال استفاده می‌کنند.

مفهوم idempotent در Ansible چیست؟

یک task به جای توصیف یک عمل برای انجام دادن، وضعیت مطلوب را اعلام می‌کند؛ مثلاً "این بسته باید نصب باشد" یا "این خط باید در این فایل وجود داشته باشد". اگر وضعیت از قبل برقرار باشد، Ansible هیچ کاری انجام نمی‌دهد و به جای changed، وضعیت ok را گزارش می‌دهد. به همین دلیل است که اجرای دوباره یک playbook در بار دوم وضعیت changed=0 را نشان می‌دهد و اجرای مجدد آن به جای یک نصب مجدد پرخطر، یک بررسی ایمن محسوب می‌شود.

آیا برای نصب Ansible روی Ubuntu 24.04 باید از pip استفاده کنم یا pipx؟

از pipx استفاده کنید. Ubuntu 24.04 پایتون سیستم را به عنوان externally managed علامت‌گذاری کرده است، بنابراین دستور 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، هسته اصلی را با مجموعه‌های منتخب جامعه (community collections) ترکیب می‌کند؛ از جمله ansible.posix (ماژول authorized_key) و community.general (ماژول ufw) که هر دو در این راهنما استفاده شده‌اند. با بسته کامل شروع کنید؛ تنها زمانی که دلیل خاصی دارید، به نسخه سبک‌تر (core) به همراه مجموعه‌های دست‌چین شده مهاجرت کنید.