Tutorial Ansible: Playbook Pertama untuk VPS
Pasang Ansible dengan pipx pada Ubuntu 24.04, bina inventori dan playbook untuk mengukuhkan VPS baharu. Selesaikan ralat Permission denied serta sudo.
Perkara yang anda bina
Satu mesin kawalan dengan Ansible dipasang, serta satu atau lebih VPS Ubuntu 24.04 baharu yang hanya mengandungi imej standard. Pada akhir panduan ini, anda akan mempunyai fail inventori yang menamakan pelayan anda, arahan ping ad hoc yang membuktikan pengesahan berfungsi dari hujung ke hujung, dan playbook yang menjalankan keseluruhan senarai semak VPS baharu sebagai kod: pengguna deploy dengan kunci SSH anda, sshd yang diperkukuh, fail2ban, peningkatan tanpa pengawasan, serta tembok api yang membenarkan OpenSSH sebelum menafikan semua perkara lain. Halakan playbook ini kepada satu pelayan atau dua puluh pelayan. Jalankan dua kali dan pelaksanaan kedua tidak mengubah apa-apa. Itulah tujuan utamanya.
Selepas lima belas tahun menyediakan VPS, saya boleh nyatakan corak sebenar: semua orang menyediakan lima pelayan pertama secara manual, kemudian menghabiskan hujung minggu pada pelayan keenam kerana tiada siapa yang mengingati perkara yang dilakukan pada lima pelayan pertama. Panduan ini memperluas tinjauan dalam mengurus berbilang pelayan Linux. Rujuk panduan ini apabila anda mendapati diri anda menaip apt install yang sama ke dalam tiga terminal.
Apakah Ansible sebenarnya, dalam satu perenggan
Ansible tidak memerlukan ejen. Tiada daemon untuk dipasang pada pelayan yang diuruskannya: mesin kawalan bersambung melalui SSH biasa, menyalin modul Python kecil ke sasaran, melaksanakannya, membaca JSON yang dicetaknya, kemudian memadamkannya. Satu-satunya keperluan pada sasaran ialah python3, yang sudah tersedia dalam setiap imej Ubuntu standard. Istilah yang penting ialah idempotent, dan maksudnya mudah: tugas menerangkan keadaan, bukan tindakan. state: present untuk pakej bermaksud "pastikan pakej ini dipasang", bukan "jalankan pemasang". Jika keadaan itu sudah dipenuhi, Ansible tidak mengubah apa-apa dan melaporkannya sebagai ok, bukannya changed. Sifat ini ialah keseluruhan asas produk tersebut. Sifat inilah yang menjadikan playbook selamat untuk dijalankan semula, dan pelaksanaan semula yang selamat mengubah skrip shell menjadi infrastruktur.
Prasyarat dan perkara penting dari awal
- Satu mesin kawalan: komputer riba anda atau VPS kecil. Saya mengandaikan Ubuntu 24.04; macOS berfungsi dengan cara yang sama selepas pipx dipasang daripada Homebrew.
- Satu atau lebih VPS sasaran yang menjalankan Ubuntu 24.04 pada KVM dan boleh dicapai sebagai root. Tiada apa-apa dipasang pada VPS tersebut.
- Pengesahan menggunakan key SSH pada setiap sasaran. Tahap pengesahan Ansible adalah sama seperti perintah
sshanda. Jikassh root@hostmeminta kata laluan, Ansible akan gagal. - Pada Ubuntu 24.04,
pip install ansiblegagal denganerror: externally-managed-environment. Ini ialah dasar distro yang disengajakan, bukan kerosakan. Gunakan pipx. - Ruang kosong YAML ialah sintaks. Inden yang salah menghasilkan
mapping values are not allowed in this context, dan aksara tab di mana-mana lokasi akan menyebabkan kegagalan. - Kekalkan sesi SSH yang berfungsi dan terbuka pada setiap sasaran semasa playbook mengukuhkan sshd. Setiap kejadian terputus akses yang saya bantu pulihkan melibatkan penutupan sesi terakhir "untuk menguji dari persekitaran bersih".
Langkah 1: pasang Ansible pada mesin kawalan dengan pipx, bukan pip
Naluri biasa ialah pip3 install ansible. Pada imej 24.04 yang benar-benar baharu, langkah itu gagal lebih awal, Command 'pip3' not found, but can be installed with: sudo apt install python3-pip, dan pemasangan pip hanya membawa anda ke halangan sebenar:
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 menandakan Python sistem sebagai diurus secara luaran (PEP 668), jadi pip tidak boleh bersaing dengan apt untuk fail yang sama. Jangan gunakan --break-system-packages; nama flag itu tepat. Penyelesaian yang bersih ialah pipx, yang memberikan Ansible virtualenv terpencilnya sendiri dan meletakkan binari pada PATH anda:
sudo apt update && sudo apt install -y pipx
pipx ensurepath
pipx install --include-deps ansibleBuka shell baharu selepas pipx ensurepath supaya perubahan PATH berkuat kuasa. --include-deps bukan hiasan: pakej ansible tidak menyediakan skrip konsolnya sendiri, ansible, ansible-playbook dan selebihnya ialah titik masuk bagi kebergantungannya, ansible-core, jadi tanpa flag itu pipx menolak pemasangan dengan No apps associated with package ansible or its dependencies. Pasang pakej ansible, bukan ansible-core sahaja. Pakej penuh itu menggabungkan koleksi komuniti, dan playbook ini menggunakan modul daripada dua koleksi tersebut (ansible.posix dan community.general).
ansible --versionHasil yang betul bermula dengan baris seperti ansible [core 2.19.x] dan menyatakan Python yang digunakan; mana-mana keluaran core semasa sesuai untuk semua perkara di sini. ansible: command not found sebaliknya bermaksud ~/.local/bin belum berada dalam PATH anda, buka shell baharu atau source ~/.bashrc.
Itulah keseluruhan pemasangan. Mesin sasaran tidak menerima apa-apa.
Langkah 2: Akses kunci SSH kepada setiap sasaran
ssh-keygen -t ed25519 -C "ansible control"
ssh-copy-id root@10.0.0.10
ssh-copy-id root@10.0.0.20Kemudian sahkannya, sekali bagi setiap hos:
ssh root@10.0.0.10 true && echo okBaris itu melaksanakan dua tugas: mengesahkan bahawa pengesahan kunci berfungsi tanpa kata laluan, dan merekodkan kunci hos dalam known_hosts. Lakukan sekarang kerana Ansible memaparkan kunci hos yang belum direkodkan sebagai gesaan interaktif di tengah-tengah pelaksanaan. Keadaan ini kelihatan seperti proses yang tergantung.
Langkah 3: inventori, INI dahulu, YAML apabila berkembang
Inventori 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=rootweb1 ialah alias yang anda pilih. Alias ini dipaparkan dalam output dan digunakan sebagai sasaran dengan --limit web1. ansible_host ialah alamat sebenar. [vps] ialah kumpulan, manakala [vps:vars] menetapkan pemboleh ubah untuk setiap hos dalam kumpulan itu; ansible_user ialah akaun yang digunakan Ansible untuk log masuk. Di sebelahnya, tetapkan ansible.cfg supaya anda tidak perlu menaip -i lagi:
[defaults]
inventory = inventory.iniAnsible membaca ansible.cfg daripada direktori semasa. Untuk inventori yang sama dalam YAML, simpannya sebagai inventory.yml dan arahkan ansible.cfg kepada nama itu. Gunakan YAML apabila setiap hos mempunyai beberapa pemboleh ubah:
vps:
hosts:
web1:
ansible_host: 10.0.0.10
web2:
ansible_host: 10.0.0.20
vars:
ansible_user: rootKedua-duanya setara. INI lebih mudah diperiksa secara sepintas lalu untuk dua pelayan, manakala YAML lebih sesuai apabila terdapat dua puluh pelayan. Pilih satu dan jangan fikirkan lagi.
Langkah 4: arahan ad hoc, pong hijau yang membuktikan semuanya
ansible all -m pingIni bukan ICMP. Modul ping ialah latihan penuh: log masuk SSH, penyalinan modul, pelaksanaan Python pada sasaran dan pembersihan. Hasil yang betul berwarna hijau, dengan 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 pengangkutan semuanya berfungsi. Playbook juga akan berfungsi. UNREACHABLE! merah bermaksud pengangkutan gagal sebelum sebarang modul dijalankan. Rentetan tepat dan pembetulannya terdapat dalam bahagian mod kegagalan di bawah. Dua lagi arahan ad hoc yang wajar diketahui:
ansible all -a "uptime"
ansible all -m apt -a "update_cache=true upgrade=dist" --becomeAd hoc adalah untuk tugasan sekali sahaja dan pemeriksaan. Apa-apa yang akan anda jalankan dua kali hendaklah dimasukkan ke dalam playbook.
Langkah 5: playbook pertama, senarai semak VPS baharu sebagai kod
Ini ialah semua tindakan yang perlu 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: restartedBaris yang perlu difahami dan bukan sekadar disalin:
Pemboleh ubah berada di bawah vars: dan dirujuk menggunakan "{{ deploy_user }}". Letakkan keseluruhan ungkapan dalam tanda petikan apabila nilai bermula dengan kurungan kerinting. Jika tidak, penghurai YAML mungkin mentafsirkannya secara salah. lookup('file', ...) membaca kunci awam anda daripada mesin kawalan semasa masa jalan. Oleh itu, playbook ini tidak mengandungi bahan kunci.
Gelung. loop: "{{ baseline_services }}" menjalankan tugas perkhidmatan sekali bagi setiap item, dan output memaparkan setiap item pada baris berasingan. Perhatikan bahawa tugas apt menerima keseluruhan senarai pakej sekaligus. Satu transaksi apt lebih pantas dan merupakan corak yang disyorkan untuk pakej. Gelung digunakan untuk modul yang benar-benar bertindak terhadap satu perkara pada satu masa.
Pengendali ialah konsep yang perlu difahami. notify: Restart ssh tidak bermaksud "mulakan semula ssh sekarang". Ia menjadualkan pengendali, yang berjalan sekali pada akhir play, dan hanya jika tugas yang mencetuskannya benar-benar melaporkan changed. Jalankan semula playbook esok: fail drop-in sudah betul, tugas salinan melaporkan ok, dan sshd tidak dimulakan semula. Baris validate: ialah perlindungan pada pencetus. sshd menyemak fail itu sebelum menggantikan fail lama. Oleh itu, kesilapan menaip menyebabkan tugas gagal dan bukannya merosakkan daemon.
PermitRootLogin prohibit-password, bukan no, dengan sengaja. Playbook ini log masuk sebagai root menggunakan kunci. prohibit-password mematikan log masuk root menggunakan kata laluan sambil mengekalkan sesi anda. Selepas pengguna deploy disahkan (ssh deploy@10.0.0.10 sudo true, alamat biasa, kerana web1 hanyalah alias yang diketahui oleh Ansible), tukar ansible_user=deploy dalam inventori dan ketatkannya kepada no dalam pelaksanaan kemudian. Lakukan pengerasan mengikut urutan yang tidak akan menyebabkan anda terkunci di luar pelayan.
Awalan 00- penting. Bagi kebanyakan kata kunci, sshd menggunakan kemunculan pertama yang ditemuinya semasa menghuraikan fail. sshd_config Ubuntu memasukkan sshd_config.d/*.conf mengikut susunan leksikal sebelum bahagian utamanya. Imej awan Ubuntu 24.04 sudah menyediakan 60-cloudimg-settings.conf dalam direktori tersebut. Pembekal yang mengaktifkan log masuk menggunakan kata laluan melalui cloud-init menambahkan 50-cloud-init.conf dengan PasswordAuthentication yes. Menamakan fail kita sebagai 00-hardening.conf menjadikannya disusun dahulu dan mengatasi kedua-duanya.
Susunan tugas ialah perlindungan firewall. Allow OpenSSH berjalan sebelum Enable ufw dengan dasar deny. Ansible melaksanakan tugas tepat mengikut susunan yang disenaraikan. Oleh itu, laluan akses wujud sebelum dinding perlindungan diaktifkan. fail2ban tidak memerlukan konfigurasi untuk berguna di sini. Tetapan lalai Ubuntu memantau sshd secara automatik. Tindakan sebenar jail dan perkara yang perlu dilaraskan diterangkan dalam panduan fail2ban pada Ubuntu 24.04.
Langkah 6: lakukan dry run dengan --check, kemudian jalankannya secara sebenar
ansible-playbook site.yml --checkMod semakan membuat sambungan dan mengira tindakan yang akan dilakukan, tetapi tidak mengubah apa-apa. Baca kiraan changed= dalam PLAY RECAP di bahagian bawah. Itulah bilangan tugas yang akan mengubah setiap hos. Terdapat satu batasan penting: mod semakan mempunyai had struktur apabila tugas seterusnya bergantung pada perubahan yang dibuat oleh tugas terdahulu. Imej pelayan standard Ubuntu disertakan dengan ufw, jadi playbook ini menjalankan dry run dengan lancar. Namun, pada imej minimum yang tidak menyertakannya, tugas ufw gagal dalam mod semakan kerana mod semakan tidak pernah memasang pakej tersebut. Modul itu kemudian tidak mempunyai apa-apa untuk dipanggil. Ini ialah batasan dry run, bukannya pepijat dalam playbook anda. Apabila pelan kelihatan betul:
ansible-playbook site.ymlSetiap tugas mencetak satu baris bagi setiap hos, changed berwarna kuning, ok berwarna hijau, dan ringkasan sepatutnya berbunyi:
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=0Sepuluh ok ialah pengumpulan fakta ditambah lapan tugas serta handler. changed anda boleh berbeza daripada saya sebanyak satu atau dua: imej standard Ubuntu disertakan dengan ufw dan unattended-upgrades, dan fail2ban memulakan dirinya sebaik sahaja apt memasangnya. Oleh itu, tugas boleh melaporkan ok secara sah pada pelaksanaan pertama, iaitu keadaan yang diisytiharkannya sudah tersedia. Nombor yang mesti sifar ialah unreachable dan failed. Satu perkara tentang become: true: ia hanya formaliti semasa anda menyambung sebagai root. Namun, sebaik sahaja anda menukar ansible_user kepada deploy, sudo benar-benar digunakan, dan fail sudoers NOPASSWD yang dipasang oleh playbook ini menghalang -K daripada muncul pada baris perintah anda. Tanpanya, anda akan menerima Missing sudo password, yang diterangkan di bawah.
Langkah 7: jalankan dua kali, begini rupa idempotensi
Jalankan perintah yang sama sekali lagi dengan serta-merta:
web1 : ok=9 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0changed=0 dan ok berkurang satu kerana pengendali yang tidak dimaklumkan tidak pernah dijalankan. Tiada apa-apa dipasang semula, sshd tidak dimulakan semula dan ufw tidak disentuh. Inilah sebabnya playbook ini berfungsi sebagai audit dan juga sebagai penyediaan: tambahkan web3 pada inventori bulan depan dan jalankan semula; pelayan baharu akan dibina dan pelayan lama akan disahkan. Nilai changed bukan sifar pada pelayan yang belum anda ubah menunjukkan drift. Ini bermakna seseorang telah mengedit secara manual perkara yang sepatutnya diedit dalam playbook.
Selepas ini, corak tersebut berkembang. Playbook seterusnya yang wajar ditulis ialah yang memasang VPN WireGuard pada VPS yang sama dan mengetatkan peraturan ufw supaya SSH hanya memberi respons melalui terowong. Selepas itu, tulis playbook untuk memasang Docker dan Compose pada setiap pelayan aplikasi. Apabila site.yml melepasi tiga skrin, pecahkannya kepada role, tetapi jangan lakukannya sebelum itu.
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
}Pengangkutan SSH gagal sebelum mana-mana modul dijalankan: ansible_user tidak betul, kunci tidak pernah disalin ke hos tersebut, atau kunci yang salah sedang ditawarkan. Uji semula dengan ssh root@10.0.0.10 biasa, kemudian ssh -v untuk melihat kunci yang ditawarkan. Jika SSH dengan kata laluan berfungsi tetapi Ansible tidak berfungsi, anda terlepas ssh-copy-id.
Kata laluan sudo tiada.
web1 | FAILED! => {
"msg": "Missing sudo password"
}Anda menetapkan become: true, disambungkan sebagai pengguna bukan root, dan pengguna tersebut memerlukan kata laluan untuk sudo. Sama ada tambah -K (--ask-become-pass) pada baris perintah, atau berikan pengguna tersebut entri sudoers NOPASSWD. Atas sebab inilah playbook memasang entri untuk deploy sebelum anda bertukar kepada pengguna tersebut.
error: externally-managed-environment. Anda menjalankan pip terhadap Python sistem pada Ubuntu 24.04. Perkara ini dibincangkan 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 contextHampir selalu berpunca daripada inden: kunci berada pada aras yang salah atau tiada ruang selepas titik bertindih. Nombor baris yang dilaporkan menunjukkan lokasi berhampiran kesilapan, bukan semestinya kesilapan itu sendiri. Semak baris sebelumnya juga. Variannya, found character '\t' that cannot start any token, bermaksud aksara tab telah terselit; YAML melarangnya. Jadikan ansible-playbook site.yml --syntax-check sebagai langkah wajib sebelum setiap pelaksanaan, dan tetapkan editor anda kepada inden dua ruang untuk YAML.
/usr/bin/python3: not found. Keadaan ini jarang berlaku pada imej Ubuntu 24.04 standard, tetapi biasa pada imej minimal atau netboot. Pelaksanaan modul gagal kerana hos sasaran tiada Python. Bootstrap Python dengan modul raw, satu-satunya modul yang tidak memerlukan apa-apa pada hos jauh: 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 diuruskannya?
Tidak. Ansible tidak memerlukan ejen: mesin kawalan menolak modul Python kecil melalui SSH, menjalankannya dan mengalih keluarnya. Sasaran hanya memerlukan python3 dan akses SSH. Kedua-duanya sudah tersedia dalam imej Ubuntu standard. Satu-satunya pemasangan dalam panduan ini dilakukan pada mesin kawalan anda.
Mengapakah Ansible memaparkan "Permission denied (publickey)"?
Blok UNREACHABLE! dengan Permission denied (publickey) bermakna pengesahan SSH gagal sebelum Ansible menjalankan apa-apa. Semak bahawa ansible_user dalam inventori sepadan dengan akaun yang sebenarnya anda sediakan, bahawa anda menjalankan ssh-copy-id ke hos tersebut, dan bahawa ssh user@host biasa boleh log masuk tanpa kata laluan. Sebarang pembaikan pada perintah ssh biasa akan membaiki Ansible kerana kedua-duanya menggunakan pengangkutan yang sama.
Apakah maksud idempotent dalam Ansible?
Tugas mengisytiharkan keadaan yang dikehendaki, seperti "pakej ini tersedia" atau "baris ini terdapat dalam fail", bukannya tindakan yang perlu dilakukan. Jika keadaan itu sudah dipenuhi, Ansible tidak melakukan apa-apa dan melaporkan ok, bukannya changed. Oleh itu, menjalankan playbook dua kali memaparkan changed=0 pada kali kedua. Jalankan semula playbook ialah audit yang selamat, 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. Oleh itu, pip install ansible gagal dengan error: externally-managed-environment mengikut reka bentuk. pipx install --include-deps ansible meletakkan Ansible dalam virtualenv terpencil dan menyediakan ansible, ansible-playbook serta perintah lain pada PATH anda dengan teratur.
Apakah perbezaan antara pakej ansible dan ansible-core?
ansible-core ialah enjin yang hanya menyertakan modul ansible.builtin. Pakej ansible menggabungkan core dengan koleksi komuniti terpilih, termasuk ansible.posix (modul authorized_key) dan community.general (modul ufw), yang kedua-duanya digunakan dalam panduan ini. Mulakan dengan pakej penuh. Gunakan core bersama koleksi yang dipilih secara manual hanya apabila anda mempunyai sebab untuk berbuat demikian.