Limitare in modo sicuro i Playbook Ansible a una singola macchina?


227

Sto usando Ansible per alcune semplici attività di gestione degli utenti con un piccolo gruppo di computer. Attualmente, ho i miei playbook impostati hosts: alle il mio file hosts è solo un singolo gruppo con tutte le macchine elencate:

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

Mi sono trovato spesso a dover mirare a una singola macchina. Il ansible-playbookcomando può limitare i giochi in questo modo:

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

Ma sembra un po 'fragile, specialmente per un libro di gioco potenzialmente distruttivo. Lasciare la limitbandiera significa che il playbook verrebbe eseguito ovunque. Dal momento che questi strumenti vengono utilizzati solo occasionalmente, sembra che valga la pena prendere provvedimenti per una riproduzione a prova di errore, in modo da non annotare accidentalmente qualcosa tra qualche mese.

Esiste una procedura ottimale per limitare l'esecuzione dei playbook su una singola macchina? Idealmente i playbook dovrebbero essere innocui se venissero esclusi alcuni dettagli importanti.

Risposte:


209

Si scopre che è possibile inserire un nome host direttamente nel playbook, quindi l'esecuzione del playbook funzionerà correttamente hosts: imac-2.local. Ma è un po 'goffo.

Una soluzione migliore potrebbe essere quella di definire gli host del playbook usando una variabile, quindi passare un indirizzo host specifico tramite --extra-vars:

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

Esecuzione del playbook:

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

Se {{ target }} non definito, il playbook non fa nulla. Se necessario, è anche possibile passare un gruppo dal file hosts. Nel complesso, questo sembra un modo molto più sicuro per costruire un playbook potenzialmente distruttivo.

Playbook destinato a un singolo 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 con un gruppo di 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

Dimenticare di definire host è sicuro!

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

playbook: user.yml

  play #1 ({{target}}): host count=0

52
Questo è risolvibile in 1.5.3 con--limit office[0]
NG.

4
La variabile deve essere quotata - cioè: '{{ target }}'- secondo docs.ansible.com/…
Limbo Peng

9
Questa è una risposta "fail safe", a differenza di altre - se lasci qualcosa fuori, non farà nulla. L'esecuzione su "solo" un host che utilizza Ansible 1.7 run_oncepotrebbe essere comunque distruttiva, quindi non è una buona idea.
RichVel

4
Se desideri un comando più breve, -eè l'equivalente di--extra-vars
William Turrell,

1
Se la tua configurazione ansible richiede che gli host non possano essere vuoti o indefiniti, utilizzare una variabile combinata con un filtro jinja funziona, come ad esempio:hosts: "{{ target | default('no_hosts')}}"
Zach Weg,

178

C'è anche un piccolo trucco che ti consente di specificare un singolo host sulla riga di comando (o più host, immagino), senza un inventario intermedio:

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

Nota la virgola ( , ) alla fine; questo indica che si tratta di un elenco, non di un file.

Ora, questo non ti proteggerà se passi accidentalmente un vero file di inventario, quindi potrebbe non essere una buona soluzione a questo specifico problema. Ma è un trucco utile da sapere!


2
È stupefacente. Uso regolarmente il flag -l, che funziona con etc / ansible / hosts (che viene popolato usando l'API di rilevamento EC2), ma a volte ho davvero bisogno di una sola macchina. Grazie!
Vic

3
Questo trucco dovrebbe usare il file hosts? Sto utilizzando gli host come un inventario dinamico per il nostro sistema AWS EC2 e restituisce: skipping: no hosts matched. Forse questo trucco non funziona più da quando --limitfunziona?
hamx0r

1
Questo trucco non ha funzionato per me. Ma questo ha funzionato: $ ansible-playbook -kK --limit=myhost1 myplaybook.yml. Vedi la risposta di Marwan.
Donn Lee,

2
Va detto che affinché questo funzioni, gli host devono essere impostati allnelle rappresentazioni teatrali - mi ci è voluto un po 'di tempo per capire ...
Remigius Stalder

83

Questo approccio verrà chiuso se viene fornito più di un singolo host controllando il chiuso variabile play_hosts . Il modulo fail viene utilizzato per uscire se non viene soddisfatta la condizione dell'host singolo. Gli esempi seguenti usano un file hosts con due host Alice e 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!'

Esegui playbook senza filtri 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

Esegui il playbook su un singolo 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!"
}

1
Sicuramente il migliore, --limitè la strada da percorrere
berto

7
play_hostsè deprecato in Ansible 2.2 e sostituito con ansible_play_hosts. Per eseguire su un host senza richiedere --limit, è possibile utilizzare 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 }} == ''su Ansible 2.8.4.
Thomas,

32

C'è l'IMHO in un modo più conveniente. Puoi infatti richiedere in modo interattivo all'utente le macchine a cui desidera applicare il playbook grazie a vars_prompt:

---

- hosts: "{{ setupHosts }}"
  vars_prompt:
    - name: "setupHosts"
      prompt: "Which hosts would you like to setup?"
      private: no
  tasks:
    […]

2
Molto bello. Ciò ha anche il vantaggio che il playbook non è specifico del file di inventario.
Erfan,

2
Grazie per la modifica! In realtà mi stavo chiedendo perché l'input fosse trattato di default "stile password". Mi ero perso nei documenti :)
Buzut,

Gli host var potrebbero essere impostati dalla riga di comando per eliminare il prompt con questo playbook?
Andig

1
@andig with --extra-varsand a normal var nel tuo playbook ...
Buzut

In realtà, non sono riuscito a farlo funzionare - sembra che {{ hosts }}sia valutato prima di inserire il valore - o c'è un trucco speciale?
Remigius Stalder,

18

Per espandere la risposta di joemailer, se si desidera avere la capacità di corrispondenza dei modelli per abbinare qualsiasi sottoinsieme di macchine remote (proprio come ansible comando), ma si desidera comunque rendere molto difficile eseguire il playbook accidentalmente su tutte le macchine, questo è cosa ho pensato:

Stesso playbook dell'altra risposta:

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

Diamo i seguenti host:

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

Ora, per eseguire il comando su tutti i dispositivi, è necessario impostare esplicitamente la variabile target su "all"

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

E per limitarlo a un modello specifico, è possibile impostare target=pattern_here

oppure, in alternativa, puoi lasciare target=alle aggiungere l' --limitargomento, ad esempio:

--limit imac-1*

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

che si traduce in:

playbook: user.yml

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

Questo è il modello che ho seguito in ansible-django-postgres-nginx
Ajoy

13

Davvero non capisco come tutte le risposte siano così complicate, il modo per farlo è semplicemente:

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

La checkmodalità consente di eseguire la modalità di funzionamento a secco, senza apportare alcuna modifica.


7
Probabilmente perché ti chiedi delle risposte, hai perso la domanda, che ha chiesto un modo per impedire l'esecuzione quando i parametri vengono omessi per errore. Hai suggerito di aggiungere altri parametri che vanno contro il requisito.
techraf

2
ah, certo, ma se le persone mi votano, potrebbe essere perché sono dei principianti Ansible (come ero quando ho scritto la mia risposta) che non conoscono nemmeno la bandiera --check, quindi immagino che sia ancora utile dal punto di vista della documentazione, come questa domanda può essere molto googlable
knocte

6

Gli utenti AWS che utilizzano lo script di inventario esterno EC2 possono semplicemente filtrare per ID istanza:

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

Questo funziona perché lo script di inventario crea gruppi predefiniti .


4
Opzione --limit non è limitato a EC2 e può essere utilizzato per ospitare / raggruppare i nomi del tuo inventario. Grazie.
martinezdelariva,

5

Abbiamo alcuni playbook generici utilizzabili da un gran numero di squadre. Disponiamo inoltre di file di inventario specifici dell'ambiente, che contengono più dichiarazioni di gruppo.

Per forzare qualcuno che chiama un playbook a specificare un gruppo contro cui eseguire, seminiamo una voce fittizia nella parte superiore del playbook:

[ansible-dummy-group]
dummy-server

Includiamo quindi il seguente controllo come primo passo nel playbook condiviso:

- 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'

Se il server fittizio viene visualizzato nell'elenco degli host per cui è pianificato l'esecuzione di questo playbook (ansible_play_batch), il chiamante non ha specificato un gruppo e l'esecuzione del playbook non verrà eseguita.


ansible_play_batchelenca solo il batch corrente, quindi quando si utilizza il batch questo non è ancora sicuro. È meglio usare ansible_play_hostsinvece.
Thomas,

A parte questo, questo trucco sembra essere il più semplice e il più vicino a ciò che è stato chiesto; Lo sto adottando!
Thomas,

4

Dalla versione 1.7 ansible ha l' opzione run_once . La sezione contiene anche alcune discussioni su varie altre tecniche.


4

Questo mostra come eseguire i playbook sul server di destinazione stesso.

Questo è un po 'più complicato se si desidera utilizzare una connessione locale. Ma questo dovrebbe essere OK se usi una variabile per l'impostazione degli host e nel file hosts crei una voce speciale per localhost.

In (tutti) i playbook hanno gli host: line impostata su:

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

Nel file degli host di inventario aggiungere una voce per localhost che imposta la connessione su locale:

[localhost]
127.0.0.1  ansible_connection=local

Quindi sulla riga di comando esegui i comandi impostando esplicitamente la destinazione, ad esempio:

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

Questo funzionerà anche quando si utilizza ansible-pull:

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

Se si dimentica di impostare la variabile sulla riga di comando, il comando verrà errato in modo sicuro (purché non sia stato creato un gruppo host chiamato "no_hosts"!) Con un avviso di:

skipping: no hosts matched

E come menzionato sopra puoi scegliere come target un singolo computer (purché sia ​​nel tuo file hosts) con:

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

o un gruppo con qualcosa come:

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

0

Ho uno script wrapper chiamato provision che ti obbliga a scegliere l'obiettivo, quindi non devo gestirlo altrove.

Per quelli che sono curiosi, io uso ENV var per le opzioni che il mio vagrantfile usa (aggiungendo l'arg corrispondente ansible per i sistemi cloud) e lascia passare il resto degli argomenti ansible. Laddove sto creando e eseguendo il provisioning di più di 10 server alla volta, includo un nuovo tentativo automatico su server non riusciti (purché siano stati compiuti progressi - ho scoperto che quando si creano 100 o più server alla volta spesso alcuni falliscono la prima volta ).

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)/
'

0

Una soluzione leggermente diversa consiste nell'utilizzare la variabile speciale ansible_limitche è il contenuto di--limit dell'opzione CLI per l'esecuzione corrente di Ansible.

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

Non è necessario definire una variabile aggiuntiva qui, basta eseguire il playbook con la --limitbandiera.

ansible-playbook --limit imac-2.local user.yml
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.