Aman membatasi buku pedoman yang dimungkinkan untuk satu mesin?

227

Saya menggunakan Ansible untuk beberapa tugas manajemen pengguna sederhana dengan sekelompok kecil komputer. Saat ini, saya sudah mengatur buku pedoman saya hosts: alldan file host saya hanyalah satu grup dengan semua mesin terdaftar:

# file: hosts
[office]
imac-1.local
imac-2.local
imac-3.local

Saya sering menemukan diri saya harus menargetkan satu mesin. The ansible-playbookbatas perintah bisa bermain seperti ini:

ansible-playbook --limit imac-2.local user.yml

Tapi itu agak rapuh, terutama untuk buku pedoman yang berpotensi merusak. Mengibarkan limitbendera berarti playbook akan dijalankan di mana-mana. Karena alat-alat ini hanya digunakan sesekali, sepertinya layak untuk mengambil langkah-langkah untuk memutar ulang sangat mudah sehingga kami tidak secara tidak sengaja mengeluarkan sesuatu beberapa bulan dari sekarang.

Apakah ada praktik terbaik untuk membatasi menjalankan buku pedoman ke satu mesin? Idealnya, buku pedoman harus tidak berbahaya jika beberapa detail penting ditinggalkan.

joemaller
sumber

Jawaban:

209

Ternyata adalah mungkin untuk memasukkan nama host langsung ke playbook, jadi menjalankan playbook dengan hosts: imac-2.localakan berfungsi dengan baik. Tapi itu agak kikuk.

Solusi yang lebih baik mungkin mendefinisikan host playbook menggunakan variabel, lalu mengirimkan alamat host tertentu melalui --extra-vars:

# file: user.yml  (playbook)
---
- hosts: '{{ target }}'
  user: ...

Menjalankan buku pedoman:

ansible-playbook user.yml --extra-vars "target=imac-2.local"

Jika {{ target }}tidak ditentukan, playbook tidak melakukan apa pun. Grup dari file host juga dapat dilewati jika perlu. Secara keseluruhan, ini sepertinya cara yang jauh lebih aman untuk membuat buku pedoman yang berpotensi merusak.

Playbook menargetkan satu host:

$ ansible-playbook user.yml --extra-vars "target=imac-2.local" --list-hosts

playbook: user.yml

  play #1 (imac-2.local): host count=1
    imac-2.local

Playbook dengan sekelompok host:

$ ansible-playbook user.yml --extra-vars "target=office" --list-hosts

playbook: user.yml

  play #1 (office): host count=3
    imac-1.local
    imac-2.local
    imac-3.local

Lupa untuk mendefinisikan host aman!

$ ansible-playbook user.yml --list-hosts

playbook: user.yml

  play #1 ({{target}}): host count=0
joemaller
sumber
52
Ini dapat dipecahkan dalam 1.5.3 dengan--limit office[0]
NG.
4
Variabel perlu dikutip - yaitu: '{{ target }}'- menurut docs.ansible.com/...
Limbo Peng
9
Ini adalah jawaban "gagal aman", tidak seperti yang lain - jika Anda meninggalkan sesuatu, itu tidak akan melakukan apa-apa. Berjalan di 'hanya' satu host menggunakan Ansible 1.7's run_oncemasih bisa merusak sehingga itu bukan ide yang bagus.
RichVel
4
Jika Anda menginginkan perintah yang lebih pendek, -esetara dengan--extra-vars
William Turrell
1
Jika konfigurasi yang memungkinkan Anda mengharuskan host tidak boleh kosong atau tidak ditentukan kemudian menggunakan variabel yang dikombinasikan dengan karya filter jinja, seperti:hosts: "{{ target | default('no_hosts')}}"
Zach Weg
178

Ada juga trik kecil yang lucu yang memungkinkan Anda menentukan satu host pada baris perintah (atau beberapa host, kurasa), tanpa inventaris perantara:

ansible-playbook -i "imac1-local," user.yml

Catat koma ( , ) di bagian akhir; ini menandakan bahwa ini adalah daftar, bukan file.

Sekarang, ini tidak akan melindungi Anda jika Anda secara tidak sengaja memasukkan file inventaris nyata, jadi itu mungkin bukan solusi yang baik untuk masalah khusus ini. Tapi itu trik yang mudah diketahui!

Tybstar
sumber
2
Itu luar biasa. Saya secara teratur menggunakan flag -l, yang bekerja dengan etc / ansible / hosts (yang diisi menggunakan API penemuan EC2), tetapi kadang-kadang saya benar-benar hanya membutuhkan satu mesin. Terima kasih!
Vic
3
Haruskah trik ini menggunakan file hosts? Saya menggunakan host sebagai persediaan dinamis untuk sistem AWS EC2 kami dan itu kembali: skipping: no hosts matched. Mungkin trik ini tidak lagi berfungsi sejak --limitberhasil?
hamx0r
1
Trik ini tidak berhasil untuk saya. Tapi ini bekerja: $ ansible-playbook -kK --limit=myhost1 myplaybook.yml. Lihat jawaban Marwan.
Donn Lee
2
Harus disebutkan bahwa agar ini berfungsi, tuan rumah harus diatur alldalam permainan - ini membutuhkan waktu beberapa saat untuk saya
ketahui
83

Pendekatan ini akan keluar jika lebih dari satu host disediakan dengan memeriksa variabel play_hosts . The gagal modul digunakan untuk keluar jika kondisi host tidak terpenuhi. Contoh di bawah ini menggunakan file host dengan dua host alice dan bob.

user.yml (playbook)

---
- hosts: all
  tasks:
    - name: Check for single host
      fail: msg="Single host check failed."
      when: "{{ play_hosts|length }} != 1"
    - debug: msg='I got executed!'

Jalankan playbook tanpa filter host

$ ansible-playbook user.yml
PLAY [all] ****************************************************************
TASK: [Check for single host] *********************************************
failed: [alice] => {"failed": true}
msg: Single host check failed.
failed: [bob] => {"failed": true}
msg: Single host check failed.
FATAL: all hosts have already failed -- aborting

Jalankan playbook pada satu host

$ ansible-playbook user.yml --limit=alice

PLAY [all] ****************************************************************

TASK: [Check for single host] *********************************************
skipping: [alice]

TASK: [debug msg='I got executed!'] ***************************************
ok: [alice] => {
    "msg": "I got executed!"
}
Marwan Alsabbagh
sumber
1
Pasti yang terbaik, --limitadalah cara untuk pergi
berto
7
play_hoststidak digunakan lagi dalam Ansible 2.2 dan diganti dengan ansible_play_hosts. Untuk berjalan di satu host tanpa perlu --limit, Anda bisa menggunakan when: inventory_hostname == ansible_play_hosts[0].
Trevor Robinson
[WARNING]: conditional statements should not include jinja2 templating delimiters such as {{ }} or {% %}. Found: {{ play_hosts|length }} == ''pada An 2.8.8.
Thomas
32

Ada IMHO cara yang lebih nyaman. Anda memang dapat secara interaktif meminta pengguna untuk mesin yang ingin dia terapkan berkat playbook ke vars_prompt:

---

- hosts: "{{ setupHosts }}"
  vars_prompt:
    - name: "setupHosts"
      prompt: "Which hosts would you like to setup?"
      private: no
  tasks:
    […]
Buzut
sumber
2
Sangat keren. Ini juga memiliki keuntungan bahwa playbook tidak spesifik untuk file inventaris.
Erfan
2
Terima kasih untuk edit! Saya benar-benar bertanya-tanya mengapa inputnya diperlakukan secara "gaya kata sandi". Saya telah melewatkannya di dokumen :)
Buzut
Bisakah host var diatur dari baris perintah untuk menghilangkan prompt dengan buku pedoman ini?
andig
1
@andig dengan --extra-varsdan var normal di buku pedoman Anda ...
Buzut
Sebenarnya, saya tidak bisa mendapatkan ini berfungsi - sepertinya {{ hosts }}dievaluasi sebelum nilainya dimasukkan - atau ada trik khusus?
Remigius Stalder
18

Untuk memperluas jawaban joemailer, jika Anda ingin memiliki kemampuan pencocokan pola untuk mencocokkan setiap subset mesin jarak jauh (seperti yang dilakukan ansibleperintah), tetapi masih ingin membuatnya sangat sulit untuk secara tidak sengaja menjalankan playbook pada semua mesin, ini adalah apa yang saya pikirkan:

Buku pedoman yang sama dengan jawaban lain:

# file: user.yml  (playbook)
---
- hosts: '{{ target }}'
  user: ...

Mari kita memiliki host berikut:

imac-10.local
imac-11.local
imac-22.local

Sekarang, untuk menjalankan perintah di semua perangkat, Anda harus secara eksplisit mengatur variabel target menjadi "semua"

ansible-playbook user.yml --extra-vars "target=all"

Dan untuk membatasinya ke pola tertentu, Anda dapat mengatur target=pattern_here

atau, sebagai alternatif, Anda dapat meninggalkan target=alldan menambahkan --limitargumen, misalnya:

--limit imac-1*

yaitu. ansible-playbook user.yml --extra-vars "target=all" --limit imac-1* --list-hosts

yang mengakibatkan:

playbook: user.yml

  play #1 (office): host count=2
    imac-10.local
    imac-11.local
deadbeef404
sumber
Ini adalah pola yang saya ikuti di ansible-django-postgres-nginx
Ajoy
13

Saya benar-benar tidak mengerti bagaimana semua jawaban itu sangat rumit, cara untuk melakukannya adalah:

ansible-playbook user.yml -i hosts/hosts --limit imac-2.local --check

The checkmode memungkinkan Anda untuk menjalankan dalam mode kering-lari, tanpa membuat perubahan.

knocte
sumber
7
Mungkin karena bertanya-tanya tentang jawaban, Anda melewatkan pertanyaan, yang meminta cara untuk mencegah agar tidak berjalan ketika parameter dihilangkan karena kesalahan. Anda menyarankan menambahkan lebih banyak parameter yang bertentangan dengan persyaratan.
techraf
2
ah, tentu saja, tetapi jika orang-orang mendukung saya, itu mungkin karena mereka adalah pemula yang mungkin (seperti saya ketika saya menulis jawaban saya) yang bahkan tidak tahu tentang bendera --check, jadi saya kira ini masih berguna sebagai dokumentasi, karena pertanyaan ini mungkin sangat googlable
knocte
6

Pengguna AWS yang menggunakan Script Inventaris Eksternal EC2 dapat dengan mudah memfilter berdasarkan id instan:

ansible-playbook sample-playbook.yml --limit i-c98d5a71 --list-hosts

Ini berfungsi karena skrip inventaris membuat grup default .

jujur
sumber
4
Opsi --limit tidak terbatas pada EC2 dan dapat digunakan untuk meng-host / mengelompokkan nama inventaris Anda. Terima kasih.
martinezdelariva
5

Kami memiliki beberapa buku pedoman umum yang dapat digunakan oleh sejumlah besar tim. Kami juga memiliki file inventaris khusus lingkungan, yang berisi beberapa deklarasi grup.

Untuk memaksa seseorang yang memanggil playbook untuk menentukan grup yang akan dilawan, kami menambakan entri boneka di bagian atas playbook:

[ansible-dummy-group]
dummy-server

Kami kemudian menyertakan cek berikut sebagai langkah pertama dalam buku pedoman bersama:

- hosts: all
  gather_facts: False
  run_once: true
  tasks:
  - fail:
      msg: "Please specify a group to run this playbook against"
    when: '"dummy-server" in ansible_play_batch'

Jika server-dummy muncul di daftar host, playbook ini dijadwalkan untuk dijalankan melawan (ansible_play_batch), maka penelepon tidak menentukan grup dan eksekusi playbook akan gagal.

mcdowellstl
sumber
ansible_play_batchdaftar batch saat ini saja, jadi ketika menggunakan batching ini masih tidak aman. Lebih baik digunakan ansible_play_hostssebagai gantinya.
Thomas
Terlepas dari itu, trik ini tampaknya menjadi yang paling sederhana dan paling dekat dengan apa yang diminta; Saya mengadopsinya!
Thomas
4

Karena versi 1.7 dimungkinkan memiliki opsi run_once . Bagian ini juga berisi beberapa diskusi tentang berbagai teknik lainnya.

Berend de Boer
sumber
4

Ini menunjukkan cara menjalankan buku pedoman di server target itu sendiri.

Ini agak sulit jika Anda ingin menggunakan koneksi lokal. Tetapi ini harus OK jika Anda menggunakan variabel untuk pengaturan host dan dalam file host buat entri khusus untuk localhost.

Di (semua) buku pedoman ada host: baris disetel ke:

- hosts: "{{ target | default('no_hosts')}}"

Dalam file host inventaris, tambahkan entri untuk localhost yang menetapkan koneksi menjadi lokal:

[localhost]
127.0.0.1  ansible_connection=local

Kemudian pada baris perintah jalankan perintah secara eksplisit menetapkan target - misalnya:

$ ansible-playbook --extra-vars "target=localhost" test.yml

Ini juga akan berfungsi saat menggunakan tarikan yang mungkin:

$ ansible-pull -U <git-repo-here> -d ~/ansible --extra-vars "target=localhost" test.yml

Jika Anda lupa mengatur variabel pada baris perintah, perintah tersebut akan error dengan aman (selama Anda belum membuat grup host yang disebut 'no_hosts'!) Dengan peringatan:

skipping: no hosts matched

Dan seperti yang disebutkan di atas Anda dapat menargetkan satu mesin (selama itu ada di file host Anda) dengan:

$ ansible-playbook --extra-vars "target=server.domain" test.yml

atau grup dengan sesuatu seperti:

$ ansible-playbook --extra-vars "target=web-servers" test.yml
bailey86
sumber
0

Saya memiliki skrip pembungkus yang disebut ketentuan yang memaksa Anda untuk memilih target, jadi saya tidak harus menanganinya di tempat lain.

Bagi mereka yang penasaran, saya menggunakan ENV vars untuk opsi yang digunakan vagrantfile saya (menambahkan argumen yang sesuai yang mungkin untuk sistem cloud) dan membiarkan sisa argumen yang memungkinkan melewati. Di mana saya membuat dan menyediakan lebih dari 10 server sekaligus, saya menyertakan coba ulang otomatis pada server gagal (selama kemajuan sedang dibuat - saya menemukan saat membuat 100 atau lebih server pada suatu waktu sering beberapa akan gagal saat pertama kali sekitar ).

echo 'Usage: [VAR=value] bin/provision [options] dev|all|TARGET|vagrant'
echo '  bootstrap - Bootstrap servers ssh port and initial security provisioning'
echo '  dev - Provision localhost for development and control'
echo '  TARGET - specify specific host or group of hosts'
echo '  all - provision all servers'
echo '  vagrant - Provision local vagrant machine (environment vars only)'
echo
echo 'Environment VARS'
echo '  BOOTSTRAP - use cloud providers default user settings if set'
echo '  TAGS - if TAGS env variable is set, then only tasks with these tags are run'
echo '  SKIP_TAGS - only run plays and tasks whose tags do not match these values'
echo '  START_AT_TASK - start the playbook at the task matching this name'
echo
ansible-playbook --help | sed -e '1d
    s#=/etc/ansible/hosts# set by bin/provision argument#
    /-k/s/$/ (use for fresh systems)/
    /--tags/s/$/ (use TAGS var instead)/
    /--skip-tags/s/$/ (use SKIP_TAGS var instead)/
    /--start-at-task/s/$/ (use START_AT_TASK var instead)/
'
iheggie
sumber
0

Solusi yang sedikit berbeda adalah dengan menggunakan variabel khusus ansible_limityang merupakan isi dari --limitopsi CLI untuk pelaksanaan Ansible saat ini.

- hosts: "{{ ansible_limit | default(omit) }}"

Tidak perlu mendefinisikan variabel tambahan di sini, cukup jalankan playbook dengan --limitbendera.

ansible-playbook --limit imac-2.local user.yml
Manolo
sumber