Ansible-Playbooks sicher auf einen einzigen Computer beschränken?

227

Ich verwende Ansible für einige einfache Benutzerverwaltungsaufgaben mit einer kleinen Gruppe von Computern. Derzeit habe ich meine Playbooks eingestellt hosts: allund meine Hosts-Datei ist nur eine einzelne Gruppe mit allen aufgelisteten Computern:

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

Ich musste häufig auf eine einzelne Maschine zielen. Der ansible-playbookBefehl kann Spiele wie folgt einschränken:

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

Aber das scheint etwas fragil zu sein, besonders für ein potenziell zerstörerisches Spielbuch. Wenn Sie die limitFlagge weglassen, wird das Spielbuch überall ausgeführt. Da diese Tools nur gelegentlich verwendet werden, lohnt es sich, Schritte zur narrensicheren Wiedergabe zu unternehmen, damit wir in Monaten nicht versehentlich etwas zerstören.

Gibt es eine bewährte Methode zum Beschränken von Playbook-Läufen auf einen einzelnen Computer? Idealerweise sollten die Spielbücher harmlos sein, wenn einige wichtige Details weggelassen wurden.

Joemaller
quelle

Antworten:

208

Es stellt sich heraus, dass es möglich ist, einen Hostnamen direkt in das Playbook einzugeben, sodass das Ausführen des Playbooks mit hosts: imac-2.localeinwandfrei funktioniert. Aber es ist irgendwie klobig.

Eine bessere Lösung könnte darin bestehen, die Hosts des Playbooks mithilfe einer Variablen zu definieren und dann eine bestimmte Hostadresse zu übergeben über --extra-vars:

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

Ausführen des Playbooks:

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

Wenn {{ target }}nicht definiert, macht das Playbook nichts. Bei Bedarf kann auch eine Gruppe aus der Hosts-Datei übergeben werden. Insgesamt scheint dies ein viel sichererer Weg zu sein, um ein potenziell zerstörerisches Spielbuch zu erstellen.

Playbook für einen einzelnen 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

Spielbuch mit einer Gruppe von Gastgebern:

$ 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

Das Vergessen, Hosts zu definieren, ist sicher!

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

playbook: user.yml

  play #1 ({{target}}): host count=0
Joemaller
quelle
52
Dies ist in 1.5.3 mit--limit office[0]
NG
4
Die Variable muss in Anführungszeichen gesetzt werden - dh: '{{ target }}'- gemäß docs.ansible.com/…
Limbo Peng
9
Dies ist im Gegensatz zu einigen anderen eine "ausfallsichere" Antwort. Wenn Sie etwas weglassen, wird dies nichts bewirken. Das Ausführen auf "nur" einem Host mit Ansible 1.7 run_oncekann immer noch destruktiv sein, daher ist dies keine so gute Idee.
RichVel
4
Wenn Sie einen kürzeren Befehl wünschen, -eist das Äquivalent von--extra-vars
William Turrell
1
Wenn Ihre ansible Konfiguration erfordert, dass Hosts nicht leer oder undefiniert sein dürfen, funktioniert die Verwendung einer Variablen in Kombination mit einem Jinja-Filter wie:hosts: "{{ target | default('no_hosts')}}"
Zach Weg
178

Es gibt auch einen niedlichen kleinen Trick, mit dem Sie einen einzelnen Host in der Befehlszeile (oder mehrere Hosts, denke ich) ohne Zwischeninventar angeben können:

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

Beachten Sie das Komma ( , ) am Ende; Dies signalisiert, dass es sich um eine Liste handelt, nicht um eine Datei.

Dies schützt Sie nicht, wenn Sie versehentlich eine echte Inventardatei übergeben. Daher ist dies möglicherweise keine gute Lösung für dieses spezielle Problem. Aber es ist ein praktischer Trick zu wissen!

Tybstar
quelle
2
Das ist erstaunlich. Ich verwende regelmäßig das Flag -l, das mit etc / ansible / hosts funktioniert (das mit der EC2-Erkennungs-API gefüllt wird), aber manchmal brauche ich wirklich nur einen einzigen Computer. Danke dir!
Vic
3
Sollte dieser Trick die Hosts-Datei verwenden? Ich verwende Hosts als dynamisches Inventar für unser AWS EC2-System und es gibt Folgendes zurück : skipping: no hosts matched. Vielleicht funktioniert dieser Trick nicht mehr, seit er --limitfunktioniert?
Hamx0r
1
Dieser Trick hat bei mir nicht funktioniert. Aber das hat funktioniert : $ ansible-playbook -kK --limit=myhost1 myplaybook.yml. Siehe Marwans Antwort.
Donn Lee
2
Es sollte erwähnt werden, dass, damit dies funktioniert, die Gastgeber allin den Spielen eingestellt sein müssen - es hat eine Weile gedauert, bis ich herausgefunden habe ...
Remigius Stalder
83

Dieser Ansatz wird beendet, wenn mehr als ein Host bereitgestellt wird, indem die Variable play_hosts überprüft wird. Das Fail-Modul wird zum Beenden verwendet, wenn die Bedingung für einen einzelnen Host nicht erfüllt ist. In den folgenden Beispielen wird eine Hosts-Datei mit zwei Hosts, Alice und Bob, verwendet.

user.yml (Spielbuch)

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

Führen Sie das Playbook ohne Hostfilter aus

$ 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

Führen Sie das Playbook auf einem einzelnen Host aus

$ 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
quelle
1
Auf jeden Fall der beste, --limitist der Weg zu gehen
Berto
7
play_hostsist in Ansible 2.2 veraltet und ersetzt durch ansible_play_hosts. --limitSie können auf einem Host ausführen, ohne dies zu benötigen 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 }} == ''auf Ansible 2.8.4.
Thomas
32

Es gibt meiner Meinung nach einen bequemeren Weg. Sie können den Benutzer tatsächlich interaktiv zur Eingabe der Maschine (n) auffordern, auf die er das Playbook anwenden möchte, dank vars_prompt:

---

- hosts: "{{ setupHosts }}"
  vars_prompt:
    - name: "setupHosts"
      prompt: "Which hosts would you like to setup?"
      private: no
  tasks:
    […]
Buzut
quelle
2
Sehr cool. Dies hat auch den Vorteil, dass das Playbook nicht spezifisch für die Inventardatei ist.
Erfan
2
Danke für die Bearbeitung! Ich habe mich tatsächlich gefragt, warum die Eingabe standardmäßig als "Passwortstil" behandelt wurde. Ich hatte das in den Dokumenten verpasst :)
Buzut
Könnten die Hosts var über die Befehlszeile festgelegt werden, um die Eingabeaufforderung mit diesem Playbook zu beseitigen?
andig
1
@andig mit --extra-varsund eine normale Var in Ihrem Spielbuch…
Buzut
Eigentlich konnte ich das nicht zum Laufen bringen - es scheint {{ hosts }}ausgewertet zu werden, bevor der Wert eingegeben wird - oder gibt es einen speziellen Trick?
Remigius Stalder
18

Um die Antwort von Joemailer zu erweitern: Wenn Sie die Möglichkeit haben möchten, mit Mustern übereinzustimmen, um mit einer beliebigen Teilmenge von Remotecomputern übereinzustimmen (genau wie der ansibleBefehl), es aber dennoch sehr schwierig machen möchten, das Playbook versehentlich auf allen Computern auszuführen, ist dies der Fall was ich mir ausgedacht habe:

Gleiches Spielbuch wie in der anderen Antwort:

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

Lassen Sie uns die folgenden Hosts haben:

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

Um den Befehl auf allen Geräten auszuführen, müssen Sie die Zielvariable explizit auf "all" setzen.

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

Und um es auf ein bestimmtes Muster zu beschränken, können Sie festlegen target=pattern_here

Alternativ können Sie target=alldas --limitArgument verlassen und anhängen , z.

--limit imac-1*

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

was in ... endet:

playbook: user.yml

  play #1 (office): host count=2
    imac-10.local
    imac-11.local
deadbeef404
quelle
Dies ist das Muster, dem ich in ansible-django-postgres-nginx
Ajoy
13

Ich verstehe wirklich nicht, wie kompliziert alle Antworten sind. Der Weg dazu ist einfach:

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

In diesem checkModus können Sie im Trockenlaufmodus arbeiten, ohne Änderungen vorzunehmen.

knocte
quelle
7
Wahrscheinlich, weil Sie sich über die Antworten gewundert haben, haben Sie die Frage verpasst, in der nach einer Möglichkeit gefragt wurde, die Ausführung zu verhindern, wenn Parameter versehentlich weggelassen werden. Sie haben vorgeschlagen, weitere Parameter hinzuzufügen, die der Anforderung widersprechen.
Techraf
2
Ah, sicher, aber wenn die Leute mich positiv bewerten, könnte dies daran liegen, dass sie Ansible-Neulinge sind (wie ich es war, als ich meine Antwort schrieb), die nicht einmal über die Flagge Bescheid wissen. --checkIch denke, dies ist in Bezug auf die Dokumentation immer noch nützlich Diese Frage kann sehr googlable sein
knocte
6

AWS-Benutzer, die das externe EC2-Inventarskript verwenden, können einfach nach Instanz-ID filtern:

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

Dies funktioniert, weil das Inventarskript Standardgruppen erstellt .

Frank
quelle
4
Option --limit ist nicht auf EC2 beschränkt und kann zum Hosten / Gruppieren von Namen Ihres Inventars verwendet werden. Vielen Dank.
Martinezdelariva
5

Wir haben einige generische Spielbücher, die von einer großen Anzahl von Teams verwendet werden können. Wir haben auch umgebungsspezifische Inventardateien, die mehrere Gruppendeklarationen enthalten.

Um jemanden, der ein Spielbuch aufruft, zu zwingen, eine Gruppe anzugeben, gegen die ausgeführt werden soll, setzen wir einen Dummy-Eintrag oben im Spielbuch:

[ansible-dummy-group]
dummy-server

Als ersten Schritt nehmen wir dann die folgende Prüfung in das freigegebene Spielbuch auf:

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

Wenn der Dummy-Server in der Liste der Hosts angezeigt wird, für die dieses Playbook ausgeführt werden soll (ansible_play_batch), hat der Aufrufer keine Gruppe angegeben und die Ausführung des Playbooks schlägt fehl.

mcdowellstl
quelle
ansible_play_batchlistet nur den aktuellen Stapel auf, daher ist dies bei Verwendung des Stapels immer noch unsicher. Es ist besser, ansible_play_hostsstattdessen zu verwenden .
Thomas
Abgesehen davon scheint dieser Trick der einfachste zu sein und dem, was gefragt wurde, am nächsten zu kommen. Ich nehme es an!
Thomas
4

Seit Version 1.7 hat ansible die Option run_once . Der Abschnitt enthält auch einige Erläuterungen zu verschiedenen anderen Techniken.

Berend de Boer
quelle
4

Dies zeigt, wie die Playbooks auf dem Zielserver selbst ausgeführt werden.

Dies ist etwas schwieriger, wenn Sie eine lokale Verbindung verwenden möchten. Dies sollte jedoch in Ordnung sein, wenn Sie eine Variable für die Hosts-Einstellung verwenden und in der Hosts-Datei einen speziellen Eintrag für localhost erstellen.

In (allen) Playbooks müssen die Hosts: line auf:

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

Fügen Sie in der Datei mit den Inventarhosts einen Eintrag für den lokalen Host hinzu, der die Verbindung auf lokal festlegt:

[localhost]
127.0.0.1  ansible_connection=local

Führen Sie dann in der Befehlszeile Befehle aus, die das Ziel explizit festlegen - zum Beispiel:

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

Dies funktioniert auch bei Verwendung von Ansible-Pull:

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

Wenn Sie vergessen, die Variable in der Befehlszeile festzulegen, wird der Befehl sicher fehlerhaft (solange Sie keine Hostgruppe mit dem Namen 'no_hosts' erstellt haben!) Mit der Warnung:

skipping: no hosts matched

Und wie oben erwähnt, können Sie einen einzelnen Computer (solange er sich in Ihrer Hosts-Datei befindet) mit folgenden Zielen anvisieren:

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

oder eine Gruppe mit so etwas wie:

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

Ich habe ein Wrapper-Skript namens Provision, das Sie zwingt, das Ziel auszuwählen, damit ich es nicht anderswo behandeln muss.

Für diejenigen, die neugierig sind, verwende ich ENV-Variablen für Optionen, die meine Vagrant-Datei verwendet (Hinzufügen des entsprechenden Ansible-Args für Cloud-Systeme), und lasse den Rest der Ansible-Args durch. Wenn ich mehr als 10 Server gleichzeitig erstelle und bereitstelle, füge ich einen automatischen Wiederholungsversuch auf ausgefallenen Servern hinzu (solange Fortschritte erzielt werden - ich habe festgestellt, dass beim Erstellen von etwa 100 Servern gleichzeitig häufig einige beim ersten Mal ausfallen ).

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
quelle
0

Eine etwas andere Lösung besteht darin, die spezielle Variable zu verwenden, ansible_limitdie den Inhalt der --limitCLI-Option für die aktuelle Ausführung von Ansible darstellt.

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

Hier muss keine zusätzliche Variable definiert werden. Führen Sie einfach das Playbook mit dem --limitFlag aus.

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