webentwicklung-frage-antwort-db.com.de

Verhindern Sie gleichzeitige Implementierungen mit Ansible

Jeder in meinem Team kann SSH in unseren speziellen Bereitstellungsserver einbinden und von dort ein Ansible-Playbook ausführen, um neuen Code auf die Computer zu pushen.

Wir machen uns Sorgen, was passiert, wenn zwei Personen gleichzeitig Bereitstellungen durchführen. Wir möchten es so gestalten, dass das Playbook fehlschlägt, wenn jemand anderes es gerade ausführt.

Irgendwelche Vorschläge, wie das geht? Die Standardlösung ist die Verwendung einer PID-Datei, aber Ansible bietet keine integrierte Unterstützung für diese.

24
James Koppel

Sie könnten einen Wrapper für Ansible-Befehle schreiben:

ansible-playbook() {
  lock="/tmp/ansible-playbook.lock"

  # Check if lock exists, return if yes
  if [ -e $lock ]; then
    echo "Sorry, someone is running already ansible from `cat $lock`"
    return
  fi

  # Install signal handlers
  trap "rm -f $lockfile; trap - INT TERM EXIT; return" INT TERM EXIT

  # Create lock file, saving originating IP
  echo $SSH_CLIENT | cut -f1 -d' ' > $lock

  # Run ansible with arguments passed at the command line
  `which ansible-playbook` "[email protected]"

  # Remove lock file
  rm $lock

  # Remove signal handlers
  trap - INT TERM EXIT
}

Definieren Sie diese Funktion im ~/.bashrc Ihrer Benutzer in der Implementierungsbox, und Sie sind festgelegt. Sie können dasselbe für den Befehl ansible tun, wenn Sie möchten, aber angesichts der Frage bin ich nicht sicher, ob dies erforderlich ist.

BEARBEITEN: Mit Signalhandler umgeschrieben, um zu verhindern, dass Sperrdateien herumlaufen, wenn Benutzer Strg-C drücken.

EDIT2: Tippfehler behoben

18
leucos

Ich persönlich benutze RunDeck ( http://rundeck.org/ ) aus mehreren Gründen als Wrapper um meine Ansible-Playbooks:

  • Sie können einen RunDeck-Job so einstellen, dass er nur einmal ausgeführt werden kann (oder so einstellen, dass er so oft ausgeführt wird, wie Sie möchten.)
  • Sie können Benutzer innerhalb des Systems so einrichten, dass die Überprüfung der Personen, die ausgeführt wurden, eindeutig ist
  • Sie können zusätzliche Variablen mit Einschränkungen für die Verwendungsmöglichkeiten festlegen (geben Sie eine Liste mit Optionen an).
  • Es ist viel billiger als Ansible Tower (RunDeck ist kostenlos)
  • Es verfügt über eine vollständige API zum pragmatischen Ausführen von Jobs von Buildsystemen aus
  • Sie müssen keine komplizierten Bash-Wrapper für den Befehl ansible-playbook schreiben
  • SSH kann zu einem Lackmustest für "etwas muss ein lesbares Skript geschrieben werden" werden - Ich erlaube keinen SSH-Zugriff, es sei denn, es gibt vollständige Break/Fix-Situationen, und wir haben glücklichere SAs als Ergebnis
  • Schließlich und definitiv ganz weit oben in der Kategorie "Nice to have" ist es, dass Sie RunDeck-Jobs so planen können, dass ansible Playbooks auf einfache Weise ausgeführt werden können, damit jeder, der sich an der Konsole anmeldet, sieht, was wann läuft

Es gibt natürlich noch viele gute Gründe, aber meine Finger werden müde vom Tippen;)

27
PhillipHolmes

Ich habe dies in mein Hauptbuch aufgenommen 

    hosts: all. 

lock_file_path: Dies ist eine Datei, deren Vorhandensein darauf hinweist, dass derzeit eine ausführbare Bereitstellung ausgeführt wird oder zuvor eine Bereitstellung stattfand, die aus irgendeinem Grund abgebrochen wurde.

force_ignore_lock: Dies ist standardmäßig auf false gesetzt und wird durch ein Optionsflag zurückgesetzt, das Sie in einem Wrapper für Befehlszeilen auf ansible setzen können. Es ermöglicht es dem Benutzer, mit der Bereitstellung fortzufahren.

Was macht das?

pre_tasks

Der erste pre_task prüft, ob der lock_file_path existiert, und zeichnet das Ergebnis in einem Register namens lock_file auf.

Die nächste Aufgabe prüft dann, ob die Datei vorhanden ist und ob die bereitgestellte Person sich entschieden hat, sie zu ignorieren (hoffentlich nach der Kommunikation mit anderen Teammitgliedern). Wenn nicht, schlägt der Job mit einer hilfreichen Fehlermeldung fehl.

Wenn der Benutzer die Bereitstellung fortsetzen möchte, selbst wenn ein lock_file vorhanden ist, löscht die nächste Aufgabe den zuvor erstellten lock_file und erstellt einen neuen. Die anderen Aufgaben in der ausgewählten Rolle laufen dann problemlos weiter.

post_tasks

Dies ist ein Hook, der unmittelbar nach Abschluss aller Tasks in der Bereitstellung aufgerufen wird. Die Aufgabe löscht hier den lock_file, sodass die nächste Person problemlos und ohne Probleme implementiert werden kann.

vars:
  lock_file_path=/tmp/ansible-playbook-{{ansible_ssh_user}}.lock

pre_tasks: 
   - stat: path={{lock_file_path}}
     register: lock_file

   - fail: msg="Sorry, I found a lockfile, so I'm assuming that someone was already running ansible when you started this deploy job. Add -f to your deploy command to forcefully continue deploying, if the previous deploy was aborted."
   when: lock_file.stat.exists|bool and not force_ignore_lock|bool

   - file: path={{lock_file_path}} state=absent
     Sudo: yes
     when: "{{force_ignore_lock}}"

   - file: path={{lock_file_path}} state=touch
     Sudo: yes

post_tasks:
   - file: path={{lock_file_path}} state=absent
     Sudo: yes
10
poppingtonic

Haben Sie darüber nachgedacht, maxsyslogins in limits.conf einzustellen? Sie können dies nach Gruppen einschränken.

# for a group called 'deployers'
@deployers        -       maxsyslogins      1

Dies ist ein bisschen schwerwiegender als das, wonach Sie gefragt haben. Möglicherweise möchten Sie es zuerst an einem VM ausprobieren. Beachten Sie, dass niemand von den Bereitstellern Zugriff hat, wenn überhaupt andere Benutzer im System vorhanden sind. Das Limit 1 zählt nicht nur die Bereitsteller. Wenn Sie als Benutzer Ihre ssh-Verbindungen multiplexen (ControlMaster auto), können Sie sich trotzdem mehrmals anmelden. Es sind andere Benutzer, die gesperrt werden.

8
bazzargh

Sie können den Befehl flock verwenden, der Ihren Befehl mit einem Dateisystem-basierten Flock (2) umschließt:

$ flock /tmp/ansible-playbook.lock ansible-playbook foo bar baz

Wickeln Sie das so ein, dass es Ihren Benutzern am besten entspricht. Dadurch bleibt eine permanente Sperrdatei in/tmp. Beachten Sie jedoch, dass das Löschen von nicht safe [1] möglich ist. Es ist atomar und sehr einfach.

[1]
A: flock /tmp/foo.lock -c "echo running; sleep 5; rm /tmp/foo.lock"
B: flock /tmp/foo.lock -c "echo running; sleep 5; rm /tmp/foo.lock"
   B blocks waiting for lock on /tmp/foo.lock
A: Finish, deleting /tmp/foo.lock
B: Runs, using lock on now deleted /tmp/foo.lock
C: flock /tmp/foo.lock -c "echo running; sleep 5; rm /tmp/foo.lock"
   Creates new /tmp/foo.lock, locks it and runs immediately, parallel with B
5
Matthew Booth

Wrapperskripts sind nicht nützlich, wenn Bereitstellungsaufträge von mehreren Build-Hosts ausgeführt werden können. In solchen Fällen muss die Sperrung vom Spielbuch vorgenommen werden.

Ansible verfügt jetzt über ein wait_for - Modul, das zum Sperren verwendet werden kann. Hier ein kurzes Beispiel (ohne Rücksicht auf veraltete Sperren):

vars:
  lock_file: "{{ deploy_dir }}/.lock"
pre_tasks:
  - name: check for lock file
    wait_for:
      path: "{{ lock_file }}"
      state: absent
  - name: create lock file
    file:
      path: "{{ lock_file }}"
      state: touch
post_tasks:
  - name: remove lock file
    file:
      path: "{{ lock_file }}"
      state: absent

Ansible sucht für eine konfigurierbare Zeitüberschreitung nach der Sperrdatei und gibt dann auf, wenn sie in dieser Zeit nicht entfernt wird.

4
Ioan Rogers

Sie können auch eine einfache Variante des Wrappers verwenden:

# Check lock file - if exists then exit. Prevent running multiple ansible instances in parallel
while kill -0 $(cat /tmp/ansible_run.lock 2> /dev/null) &> /dev/null; do
  echo "Ansible is already running. Please wait or kill running instance."
  sleep 3
done
# Create lock file
echo $$ > /tmp/ansible_run.lock

ansible-playbook main.yml

# Remove lock
rm -f /tmp/ansible_run.lock
0
gmy

Ich würde einen verteilten Sperrmechanismus wie zookeeper betrachten, den ich als Rolle einschließen würde, da es ein znode -Modul gibt. Verteilt für Hochverfügbarkeit und Schreibschutz für die Zielknoten. 

Die Rolle würde am Anfang einen Znode des Zielnamens unter /deployment/ schreiben und danach löschen. Wenn die Sperre bereits vorhanden ist, können Sie fehlschlagen und eine Nachricht in eine block werfen.