SSD Nodes Learn
Panduan Matt ConnorOleh Matt Connor · Dikemas kini 2026-07-24

Cara guna Ansible pada VPS Ubuntu 24.04

Panduan pasang Ansible guna pipx pada Ubuntu 24.04. Belajar bina inventory dan playbook untuk hardening VPS serta cara atasi ralat Permission denied.

Apa yang anda bina

Satu mesin kawalan dengan Ansible yang telah dipasang, dan satu atau lebih VPS Ubuntu 24.04 baharu yang hanya mengandungi imej asal. Pada akhirnya, anda akan mempunyai fail inventori yang menamakan pelayan anda, ujian ping ad-hoc yang membuktikan pengesahan berfungsi sepenuhnya, dan satu playbook yang menjalankan keseluruhan senarai semak VPS baharu sebagai kod: pengguna deploy dengan kunci SSH anda, sshd yang telah diperkukuh, fail2ban, unattended upgrades, dan firewall yang membenarkan OpenSSH sebelum menyekat segala-galanya yang lain. Gunakan untuk satu pelayan atau dua puluh pelayan. Jalankan ia dua kali dan larian kedua tidak akan mengubah apa-apa — itulah matlamat utamanya.

Selepas lima belas tahun menyediakan VPS, saya boleh beritahu anda corak yang sebenar: semua orang menyediakan lima pelayan pertama secara manual, kemudian membazirkan masa hujung minggu pada pelayan keenam kerana tiada sesiapa ingat apa yang telah dilakukan pada lima pelayan pertama. Panduan ini memperincikan tinjauan dalam mengurus pelbagai pelayan Linux — mulakan panduan ini pada hari anda mendapati diri anda menaip apt install yang sama ke dalam tiga terminal.

Apa itu Ansible dalam satu perenggan

Ansible adalah tanpa ejen (agentless). Tiada daemon perlu dipasang pada pelayan yang diuruskan: mesin kawalan menyambung melalui SSH biasa, menyalin modul Python kecil ke sasaran, melaksanakannya, membaca output JSON, dan memadamnya. Satu-satunya keperluan sasaran ialah python3, yang sudah ada dalam setiap imej Ubuntu asal. Istilah yang penting ialah idempotent, yang bermaksud sesuatu yang mudah: sesuatu tugasan menerangkan satu keadaan (state), bukan satu tindakan. state: present untuk sesuatu pakej bermaksud "pastikan ia dipasang", bukan "jalankan pemasang". Jika keadaan tersebut sudah sedia ada, Ansible tidak akan mengubah apa-apa dan melaporkannya sebagai ok dan bukannya changed. Sifat tersebut adalah teras produk ini — ia menjadikan pelaksanaan semula playbook adalah selamat, dan pelaksanaan semula yang selamat menukarkan skrip shell menjadi infrastruktur.

Prasyarat, dan perkara penting yang perlu diperhatikan

  • Satu mesin kawalan: komputer riba anda atau VPS kecil. Saya mengandaikan Ubuntu 24.04; macOS berfungsi secara identik selepas pipx dipasang melalui Homebrew.
  • Satu atau lebih VPS sasaran yang menjalankan Ubuntu 24.04 pada KVM, boleh dicapai sebagai root. Tiada apa-apa dipasang pada VPS tersebut.
  • Pengesahan kunci SSH untuk setiap sasaran. Tahap pengesahan Ansible adalah sama dengan arahan ssh anda — jika ssh root@host meminta kata laluan, Ansible akan gagal.
  • Pada Ubuntu 24.04, pip install ansible gagal dengan error: externally-managed-environment. Ini adalah polisi distro yang disengajakan, bukan kerosakan. Gunakan pipx.
  • Ruang kosong YAML adalah sintaks. Indentasi yang salah akan menghasilkan mapping values are not allowed in this context, dan penggunaan aksara tab di mana-mana akan menyebabkan kegagalan.
  • Kekalkan sesi SSH yang aktif pada setiap sasaran semasa playbook memperkukuh sshd. Setiap kes terkunci yang saya bantu pulihkan melibatkan penutupan sesi terakhir "untuk ujian bersih".

Langkah 1: pasang Ansible pada mesin kawalan dengan pipx, bukan pip

Kebiasaan adalah menggunakan pip3 install ansible. Pada imej 24.04 yang benar-benar baharu, kegagalan berlaku pada peringkat awal — Command 'pip3' not found, but can be installed with: sudo apt install python3-pip — dan memasang pip hanya akan menyebabkan masalah sebenar:

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.

Ubuntu 24.04 menetapkan Python sistem sebagai diurus secara luaran (PEP 668) supaya pip tidak boleh bertindih dengan apt pada fail yang sama. Jangan gunakan --break-system-packages; nama flag tersebut sudah jelas maksudnya. Penyelesaian yang bersih adalah pipx, yang memberikan Ansible virtualenv terasing sendiri dan meletakkan binari pada PATH anda:

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

Buka shell baharu selepas pipx ensurepath supaya perubahan PATH berkesan. --include-deps bukan sekadar hiasan: pakej ansible tidak mempunyai skrip konsol sendiri — ansible, ansible-playbook, dan yang lain adalah titik masuk bagi ansible-core — jadi tanpa flag tersebut, pipx akan menolak pemasangan dengan No apps associated with package ansible or its dependencies. Pasang pakej ansible, bukan ansible-core sahaja — pakej penuh menyertakan koleksi komuniti, dan playbook ini menggunakan modul daripada dua daripadanya (ansible.posix dan community.general).

ansible --version

Hasil yang betul bermula dengan baris seperti ansible [core 2.19.x] dan menyatakan Python yang digunakan; mana-mana versi teras semasa adalah memadai untuk semua perkara di sini. ansible: command not found bermaksud ~/.local/bin belum ada pada PATH anda — gunakan shell baharu, atau source ~/.bashrc.

Itu sahaja proses pemasangan. Sasaran (targets) tidak menerima apa-apa.

Langkah 2: Akses kunci SSH ke setiap sasaran

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

Kemudian buktikan, sekali bagi setiap hos:

ssh root@10.0.0.10 true && echo ok

Satu baris tersebut melakukan dua tugas: ia mengesahkan pengesahan kunci berfungsi tanpa kata laluan, dan ia merekodkan kunci hos dalam known_hosts. Lakukan sekarang, kerana Ansible akan memaparkan kunci hos yang tidak direkodkan sebagai prom interaktif di tengah-tengah proses, yang kelihatan seolah-olah sistem terhenti.

Langkah 3: inventory — Guna INI dahulu, YAML apabila ia semakin besar

Inventory ialah fail teks yang menyenaraikan mesin yang boleh diakses oleh Ansible. Cipta inventory.ini dalam direktori projek baharu:

[vps]
web1 ansible_host=10.0.0.10
web2 ansible_host=10.0.0.20

[vps:vars]
ansible_user=root

web1 ialah alias yang anda pilih — ia akan muncul dalam output dan menjadi sasaran bagi --limit web1. ansible_host ialah alamat sebenar. [vps] ialah kumpulan, dan [vps:vars] menetapkan pemboleh ubah untuk setiap hos di dalamnya; ansible_user ialah pengguna yang digunakan oleh Ansible untuk log masuk. Di sebelahnya, letakkan ansible.cfg supaya anda tidak perlu menaip -i lagi:

[defaults]
inventory = inventory.ini

Ansible membaca ansible.cfg daripada direktori semasa. Inventory yang sama dalam format YAML — simpan sebagai inventory.yml dan halakan ansible.cfg ke nama tersebut — adalah format yang akan anda lebih gemari apabila setiap hos mempunyai banyak pemboleh ubah:

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

Kedua-duanya adalah setara. Format INI lebih mudah dilihat untuk dua pelayan; format YAML lebih skalabel untuk dua puluh pelayan. Pilih satu dan jangan ragu lagi.

Langkah 4: arahan ad-hoc — petanda hijau yang membuktikan segalanya

ansible all -m ping

Ini bukan ICMP. Modul ping adalah latihan penuh: log masuk SSH, salinan modul, pelaksanaan Python pada sasaran, dan pembersihan. Hasil yang betul adalah hijau, satu blok bagi setiap hos:

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

SUCCESS hijau bermaksud pengesahan, pentafsir Python, dan penghantaran semuanya berfungsi — playbook juga akan berfungsi. UNREACHABLE! merah bermaksud penghantaran gagal sebelum mana-mana modul dijalankan; rincian ralat dan cara penyelesaian ada dalam bahagian mod kegagalan di bawah. Dua lagi arahan ad-hoc yang perlu diketahui:

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

Ad-hoc adalah untuk tugasan sekali guna dan semakan. Apa-apa yang anda ingin jalankan sebanyak dua kali harus diletakkan dalam playbook.

Step 5: playbook pertama — senarai semak new-VPS sebagai kod

Ini adalah semua perkara yang akan anda lakukan secara manual dalam sepuluh minit pertama pada pelayan baharu. Simpan sebagai 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

Baris yang perlu difahami dan bukannya sekadar disalin:

Variables berada di bawah vars: dan dirujuk dengan "{{ deploy_user }}" — letakkan tanda petikan pada keseluruhan ekspresi jika nilai bermula dengan kurungan melengkung, jika tidak parser YAML akan tersalah baca. lookup('file', ...) membaca kunci awam anda dari mesin control semasa runtime, jadi playbook ini tidak mengandungi sebarang bahan kunci.

The loop. loop: "{{ baseline_services }}" menjalankan tugasan perkhidmatan sekali bagi setiap item, dan output akan menunjukkan setiap item pada barisnya sendiri. Perhatikan bahawa tugasan apt mengambil keseluruhan senarai pakej sekali gus — satu transaksi apt adalah lebih pantas dan merupakan corak pilihan untuk pakej; loop digunakan untuk modul yang memang bertindak pada satu perkara pada satu masa.

The handler adalah konsep yang perlu difahami. notify: Restart ssh tidak bermaksud "restart ssh sekarang". Ia menjadualkan handler, yang akan berjalan sekali pada akhir play, dan hanya jika tugasan yang memaklumkan (notifying task) melaporkan changed. Jalankan semula playbook esok: fail drop-in sudah sedia betul, tugasan salinan melaporkan ok, dan sshd tidak akan dihidupkan semula. Baris validate: adalah ciri keselamatan — sshd menyemak fail tersebut sebelum menggantikan fail lama, jadi kesilapan taip akan membatalkan tugasan tersebut dan bukannya merosakkan daemon.

PermitRootLogin prohibit-password, bukan no — secara sengaja. Playbook ini log masuk sebagai root dengan kunci. prohibit-password menutup log masuk root menggunakan kata laluan sambil mengekalkan akses anda. Setelah pengguna deploy disahkan (ssh deploy@10.0.0.10 sudo true — alamat biasa, kerana web1 hanyalah alias yang hanya diketahui oleh Ansible), tukar ansible_user=deploy dalam inventory dan ketatkan kepada no dalam larian seterusnya. Lakukan pengerasan (hardening) mengikut urutan supaya anda tidak terkunci di luar.

Awalan 00- adalah penting. Bagi kebanyakan kata kunci, sshd mengutamakan kemunculan pertama yang dibaca, dan sshd_config Ubuntu menyertakan sshd_config.d/*.conf mengikut urutan leksikal sebelum badan failnya sendiri. Imej awan Ubuntu 24.04 sudah menyertakan 60-cloudimg-settings.conf dalam direktori tersebut, dan penyedia yang membolehkan log masuk kata laluan melalui cloud-init akan menambah 50-cloud-init.conf dengan PasswordAuthentication yes; menamakan fail kita sebagai 00-hardening.conf membolehkannya disusun paling atas dan mengatasi kedua-duanya.

Urutan tugasan adalah keselamatan firewall. Allow OpenSSH berjalan sebelum Enable ufw dengan polisi deny — Ansible melaksanakan tugasan mengikut urutan yang disenaraikan, jadi lubang keselamatan wujud sebelum dinding dibina. fail2ban tidak memerlukan konfigurasi untuk menjadi berguna di sini; tetapan lalai Ubuntu memantau sshd secara terus, dan apa yang dilakukan oleh jail — serta apa yang perlu dilaraskan — dibincangkan dalam panduan fail2ban pada Ubuntu 24.04.

Step 6: dry run dengan --check, kemudian jalankan secara sebenar

ansible-playbook site.yml --check

Mod semakan (check mode) menyambung ke hos, mengira apa yang akan dilakukan, dan tidak mengubah apa-apa. Baca jumlah changed= dalam PLAY RECAP di bahagian bawah — itu adalah bilangan tugasan yang akan mengubah setiap hos. Satu peringatan: mod semakan mempunyai had struktur jika tugasan kemudian bergantung pada perubahan daripada tugasan sebelumnya. Imej pelayan standard Ubuntu sudah mempunyai ufw, jadi playbook ini berjalan dry-run dengan lancar — tetapi pada imej minimal tanpa ufw, tugasan ufw akan gagal dalam mod semakan, kerana mod semakan tidak memasang pakej tersebut dan modul tidak mempunyai fungsi untuk dipanggil. Itu adalah had dry run, bukan pepijat dalam playbook anda. Apabila pelan kelihatan betul:

ansible-playbook site.yml

Setiap tugasan mencetak satu baris bagi setiap hos — kuning changed, hijau ok — dan ringkasan harus menunjukkan:

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

Sepuluh ok adalah pengumpulan fakta ditambah lapan tugasan ditambah handler. changed anda mungkin berbeza satu atau dua daripada saya: imej standard Ubuntu sudah mempunyai ufw dan unattended-upgrades, dan fail2ban bermula secara automatik sebaik sahaja apt memasangnya, jadi satu tugasan boleh melaporkan ok pada larian pertama — keadaan yang dinyatakan sudah sedia ada. Nombor yang mesti bernilai sifar ialah unreachable dan failed. Satu nota tentang become: true: ia hanyalah formaliti semasa anda menyambung sebagai root, tetapi sebaik sahaja anda menukar ansible_user kepada deploy, sudo adalah sebenar — dan fail sudoers NOPASSWD yang dipasang oleh playbook ini adalah sebab mengapa -K tidak muncul pada baris arahan anda. Tanpa fail tersebut, anda akan mendapat Missing sudo password, yang dijelaskan di bawah.

Langkah 7: jalankan ia dua kali — gambaran idempotence

Jalankan arahan yang sama dengan segera:

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

changed=0, dan ok berkurang satu kerana handler yang tidak dimaklumkan tidak pernah dijalankan. Tiada apa yang dipasang semula, sshd tidak dimulakan semula, dan ufw tidak diusik. Inilah yang menjadikan playbook ini sebagai satu audit selain daripada penyedia: tambah web3 ke dalam inventori bulan depan dan jalankan semula — pelayan baharu akan dibina, manakala pelayan lama akan disahkan. Nilai changed yang bukan sifar pada pelayan yang tidak anda usik adalah drift, dan ia memberitahu anda bahawa seseorang telah menyunting secara manual perkara yang sepatutnya disunting dalam playbook.

Dari sini, corak ini akan berkembang. Playbook seterusnya yang berbaloi untuk ditulis adalah untuk memasang WireGuard VPN pada VPS yang sama dan memperketat peraturan ufw supaya SSH hanya menjawab melalui terowong; selepas itu, satu playbook untuk memasang Docker dan Compose pada setiap pelayan aplikasi. Apabila site.yml melebihi tiga skrin, pecahkannya kepada peranan — tetapi jangan lakukan sebelum itu.

Mod kegagalan, dengan rentetan yang akan anda lihat

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

Transpor SSH gagal sebelum sebarang modul dijalankan: ansible_user adalah salah, kunci tidak pernah disalin ke hos tersebut, atau kunci yang salah telah ditawarkan. Ulangi dengan ssh root@10.0.0.10 biasa, kemudian ssh -v untuk melihat kunci yang ditawarkan. Jika SSH kata laluan berfungsi tetapi Ansible tidak, anda telah melangkau ssh-copy-id.

Missing sudo password.

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

Anda menetapkan become: true, menyambung sebagai pengguna bukan-root, dan pengguna tersebut memerlukan kata laluan untuk sudo. Tambah -K (--ask-become-pass) pada baris arahan, atau berikan pengguna entri sudoers NOPASSWD — inilah sebabnya playbook memasang entri tersebut untuk deploy sebelum anda bertukar kepadanya.

error: externally-managed-environment. Anda menjalankan pip terhadap Python sistem pada Ubuntu 24.04. Diterangkan dalam langkah 1: gunakan pipx, bukan pip, dan bukan --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

Hampir sentiasa disebabkan oleh inden: kunci pada kedalaman yang salah, atau ruang yang hilang selepas titik bertindih. Nombor baris yang dilaporkan menunjukkan berhampiran kesilapan, bukan pada kesilapan itu — semak baris di atas juga. Kesalahan berkaitan found character '\t' that cannot start any token bermaksud tab telah dimasukkan; YAML melarang penggunaan tab. Jadikan ansible-playbook site.yml --syntax-check sebagai rutin sebelum setiap larian, dan tetapkan editor anda kepada inden dua-ruang untuk YAML.

/usr/bin/python3: not found. Jarang berlaku pada imej Ubuntu 24.04 standard, biasa berlaku pada imej minimal atau netboot: pelaksanaan modul gagal kerana sasaran tidak mempunyai Python. Pasang Python menggunakan modul raw, satu-satunya modul yang tidak memerlukan apa-apa pada pihak sasaran: ansible all -m raw -a "apt-get update && apt-get install -y python3" --become, kemudian jalankan semula playbook.

FAQ

Adakah saya perlu memasang Ansible pada pelayan yang diurusnya?

Tidak. Ansible adalah tanpa ejen: mesin kawalan menghantar modul Python kecil melalui SSH, melaksanakannya, dan memadamkannya. Sasaran hanya memerlukan python3 dan akses SSH, di mana kedua-duanya sudah tersedia dalam imej Ubuntu sedia ada. Satu-satunya pemasangan dalam panduan ini adalah pada mesin kawalan anda.

Mengapa Ansible menyatakan "Permission denied (publickey)"?

Blok UNREACHABLE! dengan Permission denied (publickey) bermaksud pengesahan SSH gagal sebelum Ansible melaksanakan apa-apa. Pastikan ansible_user dalam inventori sepadan dengan akaun yang anda tetapkan, anda telah menjalankan ssh-copy-id ke hos tersebut, dan ssh user@host biasa boleh log masuk tanpa kata laluan. Apa sahaja yang membaiki arahan ssh biasa akan membaiki Ansible, kerana kedua-duanya menggunakan pengangkutan yang sama.

Apakah maksud idempotent dalam Ansible?

Sesuatu tugasan menyatakan keadaan yang diingini — "pakej ini ada", "baris ini ada dalam fail ini" — dan bukannya tindakan untuk dilakukan. Jika keadaan tersebut sudah sedia ada, Ansible tidak melakukan apa-apa dan melaporkan ok dan bukannya changed. Itulah sebabnya menjalankan playbook sebanyak dua kali menunjukkan changed=0 pada kali kedua, dan mengapa menjalankan semula adalah audit yang selamat dan bukannya pemasangan semula yang berisiko.

Patutkah saya menggunakan pip atau pipx untuk memasang Ansible pada Ubuntu 24.04?

pipx. Ubuntu 24.04 menandakan Python sistem sebagai diurus secara luaran, jadi pip install ansible gagal dengan error: externally-managed-environment secara reka bentuk. pipx install --include-deps ansible meletakkan Ansible dalam virtualenv yang terasing dan mendedahkan ansible, ansible-playbook, dan selebihnya pada PATH anda dengan kemas.

Apakah perbezaan antara pakej ansible dan ansible-core?

ansible-core adalah enjin berserta hanya modul ansible.builtin. Pakej ansible menggabungkan teras dengan koleksi komuniti yang dikurasi — termasuk ansible.posix (modul authorized_key) dan community.general (modul ufw), kedua-duanya digunakan dalam panduan ini. Mulakan dengan pakej penuh; kecilkan kepada teras berserta koleksi terpilih sahaja apabila anda mempunyai sebab yang kukuh.