Tous les membres de mon équipe peuvent utiliser SSH sur notre serveur de déploiement spécial, puis exécuter un livre de lecture Ansible pour transmettre le nouveau code aux machines.
Nous nous inquiétons de ce qui se passera si deux personnes tentent de faire des déploiements simultanément. Nous aimerions faire en sorte que le playbook échoue si quelqu'un d'autre l'exécute actuellement.
Des suggestions pour savoir comment faire cela? La solution standard consiste à utiliser un fichier pid, mais Ansible n’a pas de prise en charge intégrée pour ces derniers.
Vous pouvez écrire un wrapper pour les commandes ansible comme ceci:
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` "$@"
# Remove lock file
rm $lock
# Remove signal handlers
trap - INT TERM EXIT
}
Définissez cette fonction dans le ~/.bashrc
de vos utilisateurs dans la zone de déploiement et vous êtes défini . Vous pouvez faire de même pour la commande ansible
si vous le souhaitez, mais compte tenu de la question, je ne suis pas sûr que ce soit nécessaire.
EDIT: Réécrit avec le gestionnaire de signal pour éviter que le fichier de verrouillage ne pendille si les utilisateurs appuyaient sur Ctrl-C.
EDIT2: faute de frappe fixe
Personnellement, j'utilise RunDeck ( http://rundeck.org/ ) pour envelopper mes playbooks Ansible pour plusieurs raisons:
Il y a beaucoup d'autres bonnes raisons bien sûr, mais mes doigts en ont marre de taper;)
Je mets cela dans mon livre principal, après
hosts: all.
lock_file_path
: Il s'agit d'un fichier dont l'existence indique qu'un déploiement ansible est en cours, ou qu'un déploiement a déjà eu lieu, qui a été interrompu pour une raison quelconque.
force_ignore_lock
: false par défaut, réinitialisé par un indicateur d'option que vous pouvez définir dans un wrapper de ligne de commande sur ansible. Cela permet à ansible de poursuivre le déploiement.
pre_tasks
Le premier pre_task
vérifie si le lock_file_path
existe et enregistre le résultat dans un registre appelé lock_file
.
La tâche suivante vérifie ensuite si le fichier existe et si la personne qui effectue le déploiement a choisi de l'ignorer (éventuellement après avoir communiqué avec d'autres membres de l'équipe). Sinon, le travail échoue avec un message d'erreur utile.
Si l'utilisateur choisit de poursuivre le déploiement même s'il y avait un lock_file
, la tâche suivante supprime le lock_file
précédemment créé et en crée un nouveau. Les autres tâches du rôle sélectionné se poursuivent ensuite avec bonheur.
post_tasks
Ceci est un raccordement appelé immédiatement après que toutes les tâches du déploiement ont été terminées. La tâche ici supprime le lock_file
, permettant à la personne suivante de se déployer avec bonheur, sans aucun problème.
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
Avez-vous envisagé de définir maxsyslogins
dans limits.conf? Vous pouvez limiter cela par groupe.
# for a group called 'deployers'
@deployers - maxsyslogins 1
C'est un peu plus grave que ce que vous avez demandé. Vous voudrez peut-être d'abord l'essayer sur VM. Notez que personne parmi les déployeurs n'aura accès s'il y a d'autres utilisateurs sur le système, la limite 1 ne compte pas seulement les déployeurs. De plus, si en tant qu’utilisateur vous multiplexez vos connexions SSH (ControlMaster auto), vous pourrez toujours vous connecter plusieurs fois; ce sont les autres utilisateurs qui seraient en lock-out.
Vous pouvez utiliser la commande flock, qui encapsulera votre commande avec un flock basé sur un système de fichiers (2):
$ flock /tmp/ansible-playbook.lock ansible-playbook foo bar baz
Enveloppez-le, mais cela convient le mieux à vos utilisateurs. Cela laissera un fichier de verrouillage persistant dans/tmp, mais notez qu'il est not safe pour le supprimer [1]. C'est atomique, et très simple.
[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
Les scripts wrapper ne sont pas utiles lorsque les travaux de déploiement peuvent être exécutés à partir de plusieurs hôtes de génération. Pour ce type de cas, le verrouillage doit être traité par le playbook.
Ansible a maintenant un module wait_for qui peut être utilisé pour le verrouillage. Voici un court exemple (sans tenir compte des serrures périmées):
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 recherchera le fichier de verrouillage pendant une période configurable, puis abandonnera s’il n’est pas supprimé au cours de cette période.
Vous pouvez également utiliser une variante simple de wrapper:
# 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
J'examinerais un mécanisme de verrouillage distribué tel que zookeeper que je voudrais inclure comme rôle, car il existe un module znode
. Distribué pour la haute disponibilité et le verrouillage en écriture des noeuds cibles.
Le rôle écrirait un znode du nom de la cible sous /deployment/
au début et le supprimerait ensuite. Si le verrou existe déjà, vous pouvez échouer et envoyer un message dans une variable block
.