SSD Nodes Learn Hosting plans →
Mga Gabay Matt ConnorNi Matt Connor · Na-update 2026-08-07

Ansible tutorial: unang playbook sa Ubuntu VPS

I-install ang Ansible gamit ang pipx sa Ubuntu 24.04, gumawa ng inventory at playbook para sa VPS hardening, at ayusin ang “Permission denied” at sudo errors.

Ang itinatayo mo

Isang control machine na may naka-install na Ansible, at isa o higit pang bagong Ubuntu 24.04 VPS na stock image lamang ang laman. Sa pagtatapos, magkakaroon ka ng inventory file na naglilista sa iyong mga server, isang ad-hoc ping na nagpapatunay na gumagana ang authentication mula dulo hanggang dulo, at isang playbook na nagpapatakbo bilang code ng buong checklist para sa bagong VPS: deploy user na may iyong SSH key, pinatibay na sshd, fail2ban, unattended upgrades, at firewall na nagpapahintulot sa OpenSSH bago tanggihan ang lahat ng iba pa. Ituro ito sa isang server o dalawampu. Patakbuhin ito nang dalawang beses. Sa ikalawang pagtakbo, walang magbabago. Iyan ang buong layunin.

Matapos ang labinlimang taon ng pag-provision ng mga VPS, masasabi ko ang karaniwang nangyayari: mano-manong kino-configure ng lahat ang unang limang server, pagkatapos ay nauubos ang isang weekend sa ikaanim dahil walang nakakaalala kung ano ang ginawa nila sa unang lima. Pinapalawak ng gabay na ito ang talakayan tungkol sa pamamahala ng maraming Linux server. Balikan ito kapag nahuli mong tina-type ang parehong apt install sa tatlong terminal.

Ano talaga ang Ansible, sa isang talata

Agentless ang Ansible. Walang daemon na kailangang i-install sa mga server na mina-manage nito: kumokonekta ang control machine gamit ang ordinaryong SSH, kumokopya ng maliit na Python module sa target, ine-execute ito, binabasa ang JSON na pini-print nito, at dine-delete ito. Ang tanging kailangan ng target ay python3, na mayroon na sa bawat stock Ubuntu image. Ang mahalagang salita ay idempotent, at simple ang ibig sabihin nito: inilalarawan ng isang task ang isang state, hindi isang action. Ang state: present para sa isang package ay nangangahulugang “tiyaking naka-install ito,” hindi “patakbuhin ang installer.” Kung nasa tamang state na ito, walang gagalawin ang Ansible at iuulat ito bilang ok sa halip na changed. Iyan ang buong halaga ng produkto: ginagawa nitong ligtas ang pag-rerun ng playbook, at ang ligtas na pag-rerun ang nagbabago sa shell script upang maging infrastructure.

Mga kinakailangan at mahahalagang paalala

  • Isang control machine: ang iyong laptop o isang maliit na VPS. Ipagpapalagay na Ubuntu 24.04 ang gamit; pareho ang paggana sa macOS kapag na-install na ang pipx mula sa Homebrew.
  • Isa o higit pang target VPS na nagpapatakbo ng Ubuntu 24.04 sa KVM at maaaring ma-access bilang root. Walang ii-install sa mga ito.
  • SSH key authentication sa bawat target. Ang authentication ng Ansible ay eksaktong kapareho ng sa iyong ssh command. Kung mag-prompt ang ssh root@host para sa password, mabibigo ang Ansible.
  • Sa Ubuntu 24.04, nag-e-error ang pip install ansible gamit ang error: externally-managed-environment. Sinasadya ito ng distro policy at hindi ito pagkasira. Gamitin ang pipx.
  • Syntax ang whitespace sa YAML. Ang maling indentation ay nagdudulot ng mapping values are not allowed in this context, at fatal ang anumang tab character.
  • Panatilihing bukas ang isang gumaganang SSH session sa bawat target habang hina-harden ng playbook ang sshd. Sa bawat lockout na tinulungan kong i-recover para sa isang customer, isinara ang huling session upang “subukan mula sa malinis na koneksyon.”

Hakbang 1: i-install ang Ansible sa control machine gamit ang pipx, hindi pip

Ang karaniwang unang hakbang ay pip3 install ansible. Sa isang talagang bagong 24.04 image, mabibigo ito isang hakbang bago matapos, Command 'pip3' not found, but can be installed with: sudo apt install python3-pip, at ang pag-install ng pip ay magdadala lamang sa iyo sa aktuwal na hadlang:

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.

Itinuturing ng Ubuntu 24.04 na externally managed ang system Python (PEP 668), kaya hindi maaaring makipag-agawan ang pip sa apt para sa parehong files. Huwag gamitin ang --break-system-packages; malinaw ang ibig sabihin ng flag na iyon. Ang tamang solusyon ay pipx, na nagbibigay sa Ansible ng sarili nitong isolated virtualenv at inilalagay ang binaries sa iyong PATH:

sudo apt update && sudo apt install -y pipx
pipx ensurepath
pipx install --include-deps ansible

Magbukas ng bagong shell pagkatapos ng pipx ensurepath para mailapat ang pagbabago sa PATH. Hindi palamuti ang --include-deps: walang sariling console scripts ang ansible package, ansible, ansible-playbook, at ang iba pa ay entry points ng dependency nitong ansible-core, kaya tatanggihan ng pipx ang installation nang walang flag na ito at ipapakita ang No apps associated with package ansible or its dependencies. I-install ang ansible package, hindi ang bare na ansible-core. Kasama sa buong package ang community collections, at gumagamit ang playbook na ito ng modules mula sa dalawa sa mga ito (ansible.posix at community.general).

ansible --version

Ang tamang resulta ay nagsisimula sa linyang tulad ng ansible [core 2.19.x] at tinutukoy ang Python na ginagamit nito; sapat na ang anumang kasalukuyang core release para sa lahat ng gagawin dito. Ang ansible: command not found naman ay nangangahulugang wala pa sa PATH mo ang ~/.local/bin; magbukas ng bagong shell, o source ~/.bashrc.

Iyon na ang buong installation. Walang mai-install sa mga target.

Hakbang 2: SSH key access sa bawat target

ssh-keygen -t ed25519 -C "ansible control"
ssh-copy-id root@10.0.0.10
ssh-copy-id root@10.0.0.20

Pagkatapos, patunayan ito nang isang beses sa bawat host:

ssh root@10.0.0.10 true && echo ok

Dalawang bagay ang ginagawa ng linyang iyon: kinukumpirma nitong gumagana ang key authentication nang walang password, at nire-record nito ang host key sa known_hosts. Gawin ito ngayon, dahil ipinapakita ng Ansible ang hindi pa nare-record na host key bilang interactive prompt na nakabaon sa gitna ng isang run. Para itong nag-hang.

Hakbang 3: ang inventory, INI muna, YAML kapag lumaki na

Ang inventory ay isang text file na naglilista ng mga machine na maaaring galawin ng Ansible. Gumawa ng inventory.ini sa isang bagong project directory:

[vps]
web1 ansible_host=10.0.0.10
web2 ansible_host=10.0.0.20

[vps:vars]
ansible_user=root

Ang web1 ay alias na pipiliin mo. Ito ang lalabas sa output at ito ang ita-target mo gamit ang --limit web1. Ang ansible_host ang aktuwal na address. Ang [vps] ay isang group, at ang [vps:vars] ay nagse-set ng mga variable para sa bawat host dito; ang ansible_user ang account na gagamitin ng Ansible sa pag-login. Sa tabi nito, maglagay ng ansible.cfg para hindi mo na kailangang i-type ang -i:

[defaults]
inventory = inventory.ini

Binabasa ng Ansible ang ansible.cfg mula sa kasalukuyang directory. Para sa kaparehong inventory sa YAML, i-save ito bilang inventory.yml at ituro ang ansible.cfg sa pangalan na iyon. Ito ang mas praktikal kapag maraming variable ang bawat host:

vps:
  hosts:
    web1:
      ansible_host: 10.0.0.10
    web2:
      ansible_host: 10.0.0.20
  vars:
    ansible_user: root

Magkapareho ang resulta ng mga ito. Mas madaling basahin nang mabilis ang INI kapag dalawang server lang; mas mahusay mag-scale ang YAML kapag dalawampu na. Pumili ng isa at huwag na itong pag-isipan pa.

Hakbang 4: mga ad-hoc command, ang berdeng pong na nagpapatunay na gumagana ang lahat

ansible all -m ping

Hindi ito ICMP. Ang ping module ay isang buong rehearsal: SSH login, pagkopya ng module, pag-execute ng Python sa target, at cleanup. Ang tamang resulta ay berde, na may isang block para sa bawat host:

web1 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python3"
    },
    "changed": false,
    "ping": "pong"
}

Ibig sabihin ng berdeng SUCCESS ay gumagana ang authentication, Python interpreter, at transport; gagana rin ang playbook. Ibig sabihin ng pulang UNREACHABLE! ay nabigo ang transport bago tumakbo ang anumang module. Makikita sa failure modes section sa ibaba ang eksaktong string at kaukulang fix. Narito ang dalawa pang ad-hoc command na dapat malaman:

ansible all -a "uptime"
ansible all -m apt -a "update_cache=true upgrade=dist" --become

Ang ad-hoc ay para sa mga one-off task at check. Anumang command na tatakbuhin mo nang dalawang beses ay dapat ilagay sa playbook.

Hakbang 5: ang unang playbook, ang checklist ng bagong VPS bilang code

Ito ang lahat ng gagawin mo nang mano-mano sa unang sampung minuto sa isang bagong server. I-save ito bilang 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

Ang mga linyang dapat maunawaan sa halip na basta kopyahin:

Ang mga variable ay nasa ilalim ng vars: at ginagamit sa pamamagitan ng "{{ deploy_user }}". I-quote ang buong expression kapag nagsisimula sa brace ang value; kung hindi, maaaring mali ang pagbasa rito ng YAML parser. Binabasa ng lookup('file', ...) ang iyong public key mula sa control machine habang tumatakbo ang playbook, kaya walang key material na kasama sa playbook.

Ang loop. Pinapatakbo ng loop: "{{ baseline_services }}" ang service task nang isang beses para sa bawat item, at ipinapakita ng output ang bawat item sa sarili nitong linya. Tandaan na ginagamit ng apt task ang buong package list nang sabay. Mas mabilis ang isang apt transaction, at ito ang preferred pattern para sa mga package. Gamitin ang mga loop sa mga module na talagang tig-iisang bagay ang pinoproseso.

Ang handler ang konseptong dapat maunawaan nang mabuti. Hindi ibig sabihin ng notify: Restart ssh na “i-restart agad ang ssh”. Ila-line up nito ang handler, na isang beses tatakbo sa dulo ng play, at kung nag-ulat lamang ang task na nag-notify rito ng changed. Patakbuhin muli ang playbook bukas: tama na ang drop-in file, mag-uulat ang copy task ng ok, at hindi ire-restart ang sshd. Ang linyang validate: ang safety check sa trigger. Sinusuri ng sshd ang file bago nito palitan ang luma, kaya magfa-fail ang task kapag may typo sa halip na masira ang daemon.

PermitRootLogin prohibit-password, hindi no, nang sadya. Nagla-login ang playbook na ito bilang root gamit ang key. Isinasara ng prohibit-password ang password-based root login habang pinananatiling aktibo ang session mo. Kapag napatunayang gumagana na ang deploy user (ssh deploy@10.0.0.10 sudo true, ang plain address, dahil alias lamang na alam ng Ansible ang web1), palitan ang ansible_user=deploy sa inventory at higpitan ito sa no sa susunod na run. Isagawa ang hardening sa pagkakasunod-sunod na hindi magla-lock out sa iyo.

Mahalaga ang prefix na 00-. Para sa karamihan ng keyword, ginagamit ng sshd ang unang occurrence na nababasa nito. Kasama sa sshd_config ng Ubuntu ang sshd_config.d/*.conf ayon sa lexical order bago ang sarili nitong body. May kasama nang 60-cloudimg-settings.conf ang Ubuntu 24.04 cloud images sa directory na iyon. Ang mga provider na nag-e-enable ng password login sa pamamagitan ng cloud-init ay nagdaragdag ng 50-cloud-init.conf na may PasswordAuthentication yes. Kapag pinangalanan ang atin bilang 00-hardening.conf, mauuna ito sa sorting at mananaig laban sa dalawa.

Ang pagkakasunod-sunod ng task ang safety ng firewall. Tumatakbo ang Allow OpenSSH bago ang Enable ufw na may deny policy. Isinasagawa ng Ansible ang mga task sa eksaktong pagkakasunod na nakalista, kaya umiiral muna ang butas bago maipatupad ang harang. Hindi kailangan ng fail2ban ng karagdagang configuration para maging kapaki-pakinabang dito. Awtomatikong mino-monitor ng default settings nito sa Ubuntu ang sshd. Ang aktuwal na ginagawa ng mga jail at ang mga dapat i-tune ay tinalakay sa guide sa fail2ban sa Ubuntu 24.04.

Hakbang 6: mag-dry run gamit ang --check, pagkatapos ay aktuwal na patakbuhin

ansible-playbook site.yml --check

Kumokonekta ang check mode, kinakalkula kung ano ang gagawin nito, at walang binabago. Basahin ang bilang ng changed= sa PLAY RECAP sa ibaba. Iyan ang bilang ng mga task na magbabago sa bawat host. May isang mahalagang limitasyon ang check mode: may mga sitwasyong umaasa ang isang kasunod na task sa mga pagbabagong ginawa ng naunang task. Kasama na sa standard server image ng Ubuntu ang ufw, kaya malinis na natatapos ang dry run ng playbook na ito. Ngunit sa minimal image na walang ufw, magfa-fail ang mga ufw task sa check mode dahil hindi aktuwal na na-install ng check mode ang package at wala itong maipapatawag sa module. Limitasyon ito ng dry run, hindi bug sa playbook mo. Kapag tama na ang plano:

ansible-playbook site.yml

Nagpi-print ang bawat task ng isang line para sa bawat host: yellow na changed, green na ok, at dapat ganito ang recap:

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

Ang sampung ok ay binubuo ng fact-gathering, walong task, at handler. Maaaring magkaiba nang isa o dalawa ang iyong changed kumpara sa akin. Kasama na sa standard image ng Ubuntu ang ufw at unattended-upgrades, at awtomatikong sinisimulan ng fail2ban ang sarili nito kapag na-install ito ng apt. Kaya maaaring lehitimong mag-ulat ang isang task ng ok sa unang run nito, dahil ipinapakitang nasa tamang state na ito. Kailangang zero ang unreachable at failed. Tungkol naman sa become: true, pormalidad lamang ito habang kumokonekta ka bilang root. Ngunit kapag binago mo ang ansible_user sa deploy, aktuwal nang ginagamit ang sudo. Ang NOPASSWD sudoers file na ini-install ng playbook na ito ang pumipigil sa paglitaw ng -K sa command line mo. Kung wala ito, makukuha mo ang Missing sudo password, na tatalakayin sa ibaba.

Hakbang 7: patakbuhin ito nang dalawang beses para makita ang idempotence

Patakbuhin agad muli ang parehong command:

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

changed=0 at ok ay bumaba ng isa dahil hindi kailanman tumakbo ang handler na hindi na-notify. Walang na-reinstall, hindi ni-restart ang sshd, at hindi ginalaw ang ufw. Ito ang dahilan kung bakit ang playbook ay isang audit pati na rin provisioner: idagdag ang web3 sa inventory sa susunod na buwan at patakbuhing muli; maibubuo ang bagong box, at mabe-verify ang mga lumang box. Ang nonzero na changed sa isang box na hindi mo ginalaw ay drift. Ipinapakita nito na may mano-manong nag-edit ng dapat sana ay in-edit sa playbook.

Mula rito, lumalawak ang pattern. Ang susunod na playbook na sulit isulat ay ang nagse-set up ng WireGuard VPN sa parehong VPS at naghihigpit sa ufw rule para SSH lamang ang sumagot sa tunnel. Pagkatapos nito, gumawa ng playbook na nag-i-install ng Docker at Compose sa bawat app server. Kapag lumampas ang site.yml sa tatlong screen, hatiin ito sa roles, pero huwag itong gawin bago iyon.

Mga failure mode at ang mga string na makikita mo

UNREACHABLE with 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
}

Nabigo ang SSH transport bago tumakbo ang anumang module: mali ang ansible_user, hindi kailanman nakopya ang key sa host na iyon, o maling key ang ino-offer. I-reproduce gamit ang plain ssh root@10.0.0.10, pagkatapos ay ssh -v para makita kung aling mga key ang in-offer. Kung gumagana ang password SSH pero hindi ang Ansible, hindi mo isinama ang ssh-copy-id.

Missing sudo password.

web1 | FAILED! => {
    "msg": "Missing sudo password"
}

Itinakda mo ang become: true, kumonekta bilang non-root user, at kailangan ng password ng user na iyon para sa sudo. Idagdag ang -K (--ask-become-pass) sa command line, o bigyan ang user ng NOPASSWD sudoers entry. Ito ang dahilan kung bakit nag-i-install ang playbook ng entry para sa deploy bago ka lumipat dito.

error: externally-managed-environment. Pinatakbo mo ang pip laban sa system Python sa Ubuntu 24.04. Tinalakay ito sa step 1: pipx, hindi pip, at hindi --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

Halos palaging indentation ang sanhi: nasa maling depth ang isang key, o walang space pagkatapos ng colon. Ang naiulat na line number ay karaniwang malapit sa error, hindi mismo sa error, kaya suriin din ang linya bago nito. Ang katumbas nitong error na found character '\t' that cannot start any token ay nangangahulugang may tab na naisama; ipinagbabawal ng YAML ang mga tab. Gawing reflex ang ansible-playbook site.yml --syntax-check bago ang bawat run, at itakda ang editor mo sa two-space indentation para sa YAML.

/usr/bin/python3: not found. Bihira ito sa mga standard Ubuntu 24.04 image, pero karaniwan sa mga minimal o netboot image. Nabibigo ang module execution dahil walang Python ang target. I-bootstrap ito gamit ang raw module, ang tanging module na walang kailangang anuman sa remote side: ansible all -m raw -a "apt-get update && apt-get install -y python3" --become, pagkatapos ay patakbuhin muli ang playbook.

FAQ

Kailangan ko bang mag-install ng Ansible sa mga server na mina-manage nito?

Hindi. Agentless ang Ansible: ang control machine ang nagpu-push ng maliliit na Python module sa pamamagitan ng SSH, nagpapatakbo ng mga ito, at nag-aalis pagkatapos. Kailangan lamang ng target ang python3 at SSH access, na mayroon na sa mga karaniwang Ubuntu image. Ang tanging installation sa buong guide na ito ay ginagawa sa control machine.

Bakit sinasabi ng Ansible na "Permission denied (publickey)"?

Ipinapakita ng UNREACHABLE! block na may Permission denied (publickey) na nabigo ang SSH authentication bago pa magpatakbo ng anuman ang Ansible. Tiyaking tumutugma ang ansible_user sa inventory sa account na aktuwal mong na-set up, na pinatakbo mo ang ssh-copy-id papunta sa host na iyon, at na nakakapag-login ang plain ssh user@host nang walang password. Anumang makapag-aayos sa plain ssh command ay makapag-aayos din sa Ansible dahil pareho ang transport na ginagamit nila.

Ano ang ibig sabihin ng idempotent sa Ansible?

Nagtatakda ang isang task ng gustong state, gaya ng "narito ang package" o "narito ang linyang ito sa file", sa halip na isang action na dapat isagawa. Kung umiiral na ang state, walang ginagawa ang Ansible at iniuulat nito ang ok sa halip na changed. Kaya ipinapakita ng playbook ang changed=0 sa ikalawang pagpapatakbo, at ligtas na audit ang rerun sa halip na mapanganib na re-installation.

Dapat ko bang gamitin ang pip o pipx para mag-install ng Ansible sa Ubuntu 24.04?

pipx. Itinuturing ng Ubuntu 24.04 na externally managed ang system Python, kaya sadyang nagfa-fail ang pip install ansible na may error: externally-managed-environment. Inilalagay ng pipx install --include-deps ansible ang Ansible sa isang isolated virtualenv at malinis nitong inilalagay sa iyong PATH ang ansible, ansible-playbook, at iba pa.

Ano ang pagkakaiba ng ansible at ansible-core packages?

Ang ansible-core ay ang engine kasama lamang ang ansible.builtin modules. Ang ansible package ay nagbu-bundle ng core kasama ang curated community collections, kabilang ang ansible.posix (ang authorized_key module) at community.general (ang ufw module), na parehong ginagamit sa guide na ito. Magsimula sa full package; magbawas sa core kasama ang mga piniling collection lamang kapag may malinaw kang dahilan.