tower - ansible tutorial
Evitar despliegues simultáneos con Ansible. (8)
¿Ha considerado configurar maxsyslogins
en limits.conf? Puedes restringir esto por grupo.
# for a group called ''deployers''
@deployers - maxsyslogins 1
Esto es un poco más grave de lo que pediste. Es posible que desee probarlo en una máquina virtual primero. Tenga en cuenta que nadie de los implementadores tendrá acceso si hay otros usuarios en el sistema, el límite 1 no solo cuenta a los implementadores. Además, si como usuario multiplexas tus conexiones ssh (ControlMaster automático), aún podrás iniciar sesión varias veces; son otros usuarios que estarían bloqueados.
Cualquiera en mi equipo puede SSH en nuestro servidor de despliegue especial, y desde allí ejecutar un libro de jugadas de Ansible para enviar código nuevo a las máquinas.
Nos preocupa lo que sucederá si dos personas intentan realizar implementaciones simultáneamente. Nos gustaría hacer que el libro de jugadas falle si alguien más lo está ejecutando.
¿Alguna sugerencia de cómo hacer esto? La solución estándar es usar un archivo pid, pero Ansible no tiene soporte incorporado para estos.
Los scripts de envoltura no son útiles cuando los trabajos de implementación se pueden ejecutar desde varios hosts de compilación. Para ese tipo de casos, el bloqueo debe ser manejado por el libro de jugadas.
Ansible ahora tiene un módulo wait_for que se puede usar para bloquear. Aquí hay un breve ejemplo (sin tener en cuenta los bloqueos antiguos):
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 buscará el archivo de bloqueo por un período de tiempo de espera configurable, y luego se dará por vencido si no se elimina en ese período.
Personalmente, uso RunDeck ( http://rundeck.org/ ) como una envoltura alrededor de mis libros de juego de Ansible por varias razones:
- Puede configurar un ''trabajo'' RunDeck para que solo pueda ejecutarse a la vez (o configurarlo para que se ejecute tantas veces al mismo tiempo como desee)
- Puede configurar usuarios dentro del sistema para que la auditoría de quién ha ejecutado lo que se indica claramente
- Puede establecer variables adicionales con restricciones sobre lo que se puede usar (especifique una lista de opciones)
- Es mucho más barato que Ansible Tower (RunDeck es gratis)
- Tiene una API completa para ejecutar trabajos de manera pragmática desde sistemas de compilación.
- No necesita escribir envoltorios de bash complicados alrededor del comando ansible-playbook
- SSH puede convertirse en una prueba de fuego de "algo se necesita un guión para escribir". No permito el acceso a SSH excepto en situaciones de descanso / reparación, y como resultado tenemos SA más felices
- Por último, y definitivamente en la categoría de "es bueno tener", puede programar los trabajos de RunDeck para ejecutar libros de jugabilidad de Anible de una manera muy fácil para cualquier persona que inicie sesión en la consola para ver qué se está ejecutando cuando
Hay muchas más buenas razones, por supuesto, pero mis dedos se están cansando de escribir;)
Podrías escribir una envoltura para comandos ansibles como tal:
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
}
Defina esta función en el ~/.bashrc
de sus usuarios en el cuadro de implementación y listo. Puede hacer lo mismo para el comando ansible
si lo desea, pero dada la pregunta no estoy seguro de que sea necesario.
EDITAR: vuelva a escribir con el manejador de señales para evitar que el archivo de bloqueo se cuelgue si los usuarios presionan Ctrl-C.
EDIT2: error tipográfico fijo
Puede usar el comando flock, que envolverá su comando con un rebaño basado en el sistema de archivos (2):
$ flock /tmp/ansible-playbook.lock ansible-playbook foo bar baz
Envuélvelo como mejor se adapte a tus usuarios. Esto dejará un archivo de bloqueo persistente en / tmp, pero tenga en cuenta que no es seguro eliminarlo [1]. Es atómico, y muy 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
Puse esto en mi libro de jugadas principal, después de
hosts: all.
lock_file_path
: este es un archivo cuya existencia indica que hay un despliegue actualmente ejecutable, o que hubo un despliegue antes, que se anuló por alguna razón.
force_ignore_lock
: este valor predeterminado es falso, se restablece mediante un indicador de opción que puede establecer en un envoltorio de línea de comandos como ansible. Permite a ansible continuar con el despliegue.
Lo que esto hace
pre_tasks
La primera pre_task
verifica si existe el lock_file_path
y registra el resultado en un registro llamado lock_file
.
La siguiente tarea luego verifica si el archivo existe, y si la persona que lo implementó ha optado por ignorarlo (es de esperar después de comunicarse con otros compañeros de equipo). Si no, el trabajo falla, con un mensaje de error útil.
Si el usuario elige continuar con la implementación incluso si hubiera un lock_file
, la siguiente tarea eliminará el lock_file
creado anteriormente y creará uno nuevo. Las otras tareas en el rol seleccionado continúan felizmente.
post_tasks
Este es un gancho llamado inmediatamente después de que se hayan completado todas las tareas en el despliegue. La tarea aquí elimina el lock_file
, lo que permite que la siguiente persona se despliegue felizmente, sin ningún problema.
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
También puedes usar una variante simple de envoltorio:
# 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
znode
un mecanismo de bloqueo distribuido como el guardián del zoológico que incluiría como rol, ya que hay un módulo znode
. Distribuido para alta disponibilidad y bloqueo de escritura fuera de los nodos de destino.
El rol escribiría un znodo del nombre de destino bajo /deployment/
al principio y lo eliminaría después. Si el bloqueo ya existe, puede fallar y lanzar un mensaje en un block
.