Ansible: soporte para "serial" en una tarea individual

Creado en 31 ago. 2015  ·  65Comentarios  ·  Fuente: ansible/ansible

(Idea de función)

La forma natural de manejar la configuración u otras actualizaciones que requieren un reinicio continuo sería realizar actualizaciones en paralelo, luego notificar a un controlador, que realiza un reinicio con serial . Pero esto no es posible, ya que requiere un reinicio manual o trucos feos. Ver https://groups.google.com/forum/#!topic/ansible -project / rBcWzXjt-Xc

affects_2.1 feature

Comentario más útil

@bcoca , creo que esto es necesario, podrías dejar que esto se abra como algo necesario cuando sea más fácil de implementar.

Las funciones del software no se pueden detener por dificultades técnicas.

Todos 65 comentarios

: +1:

+1
¡Sería genial tener esto para nuestros scripts de implementación ansible-ceph!

: +1:

+1

+1

+1; sin embargo, la serie no necesita fallar en toda la reproducción para los hosts restantes si todos los hosts de la serie actual fallan. A menudo tengo que hacer reinicios continuos, un servidor a la vez en más de 50 servidores. Apesta cuando el juego falla en el servidor 3 porque el servidor 3 tuvo alguna condición extraña e inesperada que hizo que el reinicio fallara. Establecer max_failpercent en algo superior al 100% debería obligar a ansible a continuar el juego para los hosts restantes.

+1

+1!

+1

+1

: +1:

+1

+1

+1

Es una buena idea, pero no solo debería pasar de los libros de jugadas a las tareas individuales, sino también a los bloques, incluidas todas las opciones dependientes como max_fail_percentage y run_once.

El ejemplo de actualización-reinicio podría explicarlo fácilmente:

  • Tarea: Actualizar servidor (todo en paralelo)
  • Tarea opcional: compruebe si el servidor necesita reiniciarse
  • Bloque: (serial)

    • Reiniciar servidor

    • Espere a que vuelva el servidor

  • Pasos adicionales (todos en paralelo)

+1

+1 para esto.

+1

+1, los reinicios sucesivos son útiles para sistemas distribuidos que están todos encadenados. Para Ceph, no queremos reiniciar todos los demonios de almacenamiento al mismo tiempo porque el archivo de configuración cambió.

+1

+1

+1

solución alterna:

- name: service restart
  # serial: 1 would be the proper solution here, but that can only be set on play level
  # upstream issue: https://github.com/ansible/ansible/issues/12170
  run_once: true
  with_items: "[{% for h in play_hosts  %}'{{ h }}'{% if not loop.last %} ,{% endif %}{% endfor %}]"
  delegate_to: "{{ item }}"
  command: "/bin/service restart"

@alvaroaleman gracias por su sugerencia, sin embargo, parece conducir a este error: https://github.com/ansible/ansible/issues/15103
Al menos para mí, apliqué su solución alternativa así: with_items: "[{% for h in groups[mon_group_name] %}'{{ h }}'{% if not loop.last %} ,{% endif %}{% endfor %}]" .

¿Me estoy perdiendo de algo?

Buena idea. Aunque con Ansible 1.9, esto se puede simplificar haciendo:

with_items: '{{play_hosts}}'

@alvaroaleman gracias por la solución.

¿Existe alguna solución para la serie: 30%?

Buena idea.

Supone que una tarea sabe qué hosts se están ejecutando en ella O se dirige a todos los hosts de la obra.

Si se trata de una tarea en un rol (que podría haberse dirigido a cualquier cosa), esto no tiene sentido.

Puede crear un grupo temporal, pero luego no puede ejecutar esa tarea dos veces sin apuntar a algunos hosts dos veces.

Teniendo en cuenta la forma en que funcionan las 'series' y las obras de teatro, esto no es realmente algo que veamos agregar a Ansible. Tiene varias formas de implementar algo similar con la funcionalidad existente, muchas otras se han discutido anteriormente y en otros lugares, por lo que solo detallaré las 2 que cubren la mayoría de los casos.

  • Utilice una reproducción intermedia (permite muchas series de diferencias, pero requiere que las tareas anteriores finalicen en todos los hosts):
- hosts: all 
  tasks:
   - anything:...
  ....

- hosts: all
  serial: 1
  tasks: 
   - singletask:

- hosts: all 
  tasks:
   - morestuff:...
  ....
  • ejecutar una vez + bucle + delegado (limitado al comportamiento serial = 1):
- mytask: ..
  delegate_to: "{{item}}"
  run_once: true
  # many diff ways to make the loop
  with_inventory_hostnames: all

He estado tratando de jugar con un complemento de estrategia que hace esto ... pero tendría que recrear 1/2 de la utilidad principal de Ansible para lograrlo y aún tendría muchos problemas al tratar con otras partes del sistema (como devoluciones de llamada ).

En este punto, no veo ningún caso de uso que no esté cubierto por ninguna de las soluciones anteriores y dudo mucho que alguien en el núcleo aborde esto, así que voy a cerrar este problema.

Por supuesto, si alguien envía un código que pueda agregar esta función de una manera sensata, se considerará su inclusión, pero dudo que esto sea posible actualmente (o no soy lo suficientemente inteligente como para ver una manera).

@bcoca , creo que esto es necesario, podrías dejar que esto se abra como algo necesario cuando sea más fácil de implementar.

Las funciones del software no se pueden detener por dificultades técnicas.

@detiber gracias por la info.

De hecho, resolví mi problema con la solución publicada aquí.

Tengo un caché de proxy y cuando instalo paquetes para mi clúster, necesito instalarlos primero en una máquina y, con todos los paquetes almacenados en caché, los instalo en las otras máquinas.

- name: Update all packages
  # serial: 1 would be the proper solution here, but that can only be set on play level
  # upstream issue: https://github.com/ansible/ansible/issues/12170
  run_once: true
  delegate_to: "{{ play_hosts[0] }}"
  yum: name=* state=latest

- yum: name=* state=latest

Pero creo que esta característica podría interesarle a más marsopas y seria: es la forma más simple y lógica de lograrlo.

@bcoca Usamos su segunda sugerencia en nuestro proyecto para lograr el comportamiento serial = 1. El único aspecto negativo es que el resumen al final de la jugada se estropea porque cada ok o cambio cuenta para el primer anfitrión y no para el delegado. ¿Puedes pensar en alguna solución para eso?

Realmente no queremos usar la primera sugerencia porque tenemos algunas dependencias y necesitaríamos incluir roles tanto al principio como al final del juego para admitir el uso de etiquetas.

Gracias

@ kami8607 puedes probar esto, aunque no probado con el último ansible:

- hosts: all
  tasks:

    - name: set fact
      set_fact:
        marker: marker

    - name: group by marker
      group_by: key=marker
      changed_when: no

    - name: target task
      debug: msg="Performing task on {{ inventory_hostname }}, item is {{ item }}"
      with_items: "{{ groups['marker'] }}"
      when: "hostvars[item].inventory_hostname == inventory_hostname"

@hryamzik ​​¡ muchas gracias! Funciona de maravilla.
(Para su información, usamos ansible 2.0.2.0)

@ kami8607 también puedes intentar reemplazar el truco del marcador con play_hosts .

@hryamzik. Muy bonita, solución perfecta para nosotros ahora. Gracias de nuevo :)

Hola. Descubrimos un problema con la solución proporcionada por @hryamzik.
Si la tarea falla en uno de estos hosts "pseudo-seriales", la tarea se ejecuta en los otros hosts en lugar de fallar inmediatamente. No importa lo que intentemos, no pudimos omitir el libro de jugadas directamente después del host fallido.

Quizás alguien tenga una solución para nosotros. Gracias

Hola, creo que esta es realmente una característica crucial para utilizar de forma segura las dependencias de roles.

En nuestro proyecto, modelamos la mayoría de nuestras obras con la ayuda de las dependencias de roles, ya que crea libros de jugadas breves y fáciles de leer y evita la duplicación.

Si tengo un rol A que depende del rol B y es peligroso actualizar B en varios hosts simultáneamente, por lo que entiendo, la única forma compatible de lograrlo es establecer serial: 1 para la obra que usa el rol A Esto puede ser inaceptable cuando hay muchos hosts y muchos roles que dependen de B.

También estamos usando la solución de @hryamzik ​​en este momento, pero como dijo @ kami8607 , ansible no se detiene cuando se encuentra con una falla.

También @bcoca , no creo que este tipo de soluciones deban ser el objetivo al diseñar una herramienta como ansible. Parece haber muchas personas con casos de uso similares que requieren alguna solución. Como dijo @ pando85 , las dificultades técnicas no deberían ser motivo para cerrar este tema.
Sería muy bueno si se pudiera reabrir este ticket o se pudiera considerar alguna otra solución.

Un gran +1 de mi parte ... la forma en que están configuradas las cosas, parece que debería poder habilitar los reinicios continuos agregando serial: 1 a cualquiera de mis controladores de reinicio. Entonces, cualquier cambio de configuración, actualización de versión, etc. resultaría en un reinicio continuo.

+1

+1

+1

Los roles son efectivamente INÚTILES a gran escala sin esto.

+1 realmente buena idea

También en apoyo de esto. Usamos roles y para usar cualquiera de las soluciones, tendríamos que extraer solo las tareas serializadas en la obra o un archivo de tareas incluido en ella, rompiendo así la encapsulación de roles.

+1

+1

Si la tarea falla en uno de estos hosts "pseudo-seriales", la tarea se ejecuta en los otros hosts en lugar de fallar inmediatamente. No importa lo que intentemos, no pudimos omitir el libro de jugadas directamente después del host fallido.

@ kami8607 Me he enfrentado al mismo problema con fallas, ya que las actualizaciones continuas y los reinicios requieren que todo el libro de jugadas falle en cualquier error. Resuelto con any_errors_fatal: true .

También confirmo que esta solución funciona con include_tasks , sin embargo, el modo check se ejecuta en paralelo.

- name: install and configure alive servers
  include_tasks: "install_configure.yml"
  with_items: "{{ healthy_servers }}"
  when: "hostvars[host_item].inventory_hostname == inventory_hostname"
  loop_control:
      loop_var: host_item

Si observa que los comentarios reciben 👎, es porque no tienen sentido, ya que todo su contenido dice "+1".

También hay una solución de trabajo en este hilo.

hay una solución alternativa que funciona para la mayoría de las piezas, pero no hay solución

No hay una solución funcional en este hilo. Si bien puede funcionar para algunos, no es una solución.
register no funciona correctamente (tendrá la esencia para respaldar esto más adelante); supongo que no es la única función que no funcionará correctamente.

@jonhatalla No tengo ningún problema con register , ¿puedes compartir una esencia o un repositorio que no funciona?

Me gustaría limitar solo una tarea de descarga de artefactos (debido a algunas restricciones) pero ejecutar el resto de tareas en paralelo.

He venido con una propuesta después de leer los comentarios que todavía no funciona como se esperaba para el caso de descarga. Tenga en cuenta que 2 es el número máximo de ejecuciones de tareas simultáneas deseadas.

    - name: Download at ratio three at most
      win_get_url:
        url: http://ipv4.download.thinkbroadband.com/100MB.zip
        dest: c:/ansible/100MB.zip
        force: yes
      with_sequence: start=0 end={{ (( play_hosts | length ) / 2 ) | round (0, 'floor') | int }}
      when: "(( ansible_play_batch.index(inventory_hostname) % (( play_hosts | length ) / 2 )) | round) == (item | int)"

Si bien esto coincidirá con el when en cada iteración solo si para ciertos hosts todavía puedo ver todo el servidor realizando la descarga al mismo tiempo.

Otra forma de probarlo es depurar un mensaje y agregar un retraso entre iteraciones. De esta manera queda claro que solo se ejecutan dos en cada iteración.

    - debug:
        msg: "Item {{ item }} with modulus {{ (( ansible_play_batch.index(inventory_hostname) % (( play_hosts | length ) / 2 )) | round) }}"
      with_sequence: start=0 end={{ (( play_hosts | length ) / 2 ) | round (0, 'floor') | int }}
      loop_control:
        pause: 2
      when: "(( ansible_play_batch.index(inventory_hostname) % (( play_hosts | length ) / 2 )) | round) == (item | int)"

Descubrí este tema gracias a esta pregunta SO

¿Alguna idea de por qué la descarga no parece funcionar como lo hace el mensaje de depuración?

En este punto, no veo ningún caso de uso que no esté cubierto por ninguna de las soluciones anteriores.

Como dijeron los comentaristas anteriores, tampoco veo ninguna solución para los controladores que reinician los servicios en un clúster en el que no desea reiniciar todos los nodos al mismo tiempo. Por lo tanto, hay al menos un caso de uso en el que no parece haber una solución ... Esto hace que los controladores en este caso sean totalmente inútiles, ya que los controladores se utilizan para reiniciar los servicios CUANDO esto es necesario.

Y todas las demás soluciones (manejar la escritura simultánea en un archivo de hosts local, por ejemplo) funcionan, pero son tan feas ...

Finalmente, estoy de acuerdo, cerrar un problema porque es un problema demasiado grande para resolverlo es un poco deprimente ...

@zwindler puede usar tareas en lugar de controladores. De hecho, utilizo reinicios continuos con comprobaciones de API. Implementado con include_task, works as expected. You can even try include_task` directamente en los controladores, pero no tengo idea de si eso funciona o no.

No estoy seguro de entender lo que sugieres.

  • ¿Quiere decir que usa include_task para reiniciar los servicios, y solo lo hace con una cláusula when: para verificar si debe ocurrir o no un reinicio en este nodo?
  • ¿Cómo puede asegurarse de que solo un nodo tiene sus servicios reiniciados a la vez (ese es todo el problema aquí)? ¿Quiere decir que puede agregar serial con include_task ?
- name: install and configure alive servers
  include_tasks: "install_configure.yml"
  with_items: "{{ healthy_servers }}"
  when: "hostvars[host_item].inventory_hostname == inventory_hostname"
  loop_control:
      loop_var: host_item

en este caso serial=1 se simula para todas las tareas dentro de install_configure.yml .

¿Cómo se ha definido healthy_servers? ¿Cómo se usa en la solución? No veo que se haga referencia. Quiero que se aplique una tarea en serie a todos los hosts contra los que se está ejecutando el libro de jugadas.

@erpadmin, ¿por qué no usas play_hosts en este caso?

Hola a todos,
Hemos detectado un problema similar y no podemos pasar el argumento y usarlo en el libro de jugadas:
serial: $ {serial_mode}
pero si falla con:
ValueError: literal no válido para int () con base 10: 'serial_mode'

parece apuntar a este error, pero me gustaría aclarar:

  • ¿Hay alguna solución para este problema?
  • ¿Cuál es la versión oficial con esta solución?

gracias por su ayuda y por favor manténganos informados.

saludos cordiales, Pablo.

sí, también probé con este y mismo problema.

gracias, Pablo.

El 01/08/2018 11:02, Johannes Najjar escribió:
>

has probado {{serial_mode}}

-
Estás recibiendo esto porque hiciste un comentario.
Responda a este correo electrónico directamente, véalo en GitHub
https://urldefense.proofpoint.com/v2/url?u=https-3A__github.com_ansible_ansible_issues_12170-23issuecomment-2D409504564&d=DwMCaQ&c=RoP1YumCXCgaWHvlZYR8PZh8Bv7qIrMUB65eapI_JnE&r=S57T0QaaR3U1-rdS92VizJ7MMFzQcmoa9SvsdavdKz0&m=yK7T1nGurRdoVF74pYsp2Ww-gi_wzcik9FOhvfi0AO4&s=xx2w8JlL7xtYCFCYV2SVe6ghMflP4n0oJ1XT8yRJiK4&e= ,
o silenciar el hilo
https://urldefense.proofpoint.com/v2/url?u=https-3A__github.com_notifications_unsubscribe-2Dauth_AoC2bq84TS9DNhwP7QHo2lN6rwu6K2fjks5uMW6dgaJpZM4F1SdA&d=DwMCaQ&c=RoP1YumCXCgaWHvlZYR8PZh8Bv7qIrMUB65eapI_JnE&r=S57T0QaaR3U1-rdS92VizJ7MMFzQcmoa9SvsdavdKz0&m=yK7T1nGurRdoVF74pYsp2Ww-gi_wzcik9FOhvfi0AO4&s=0mncwDiylOIi-1VOf_7Bp6ltumjR5pCnTNSjqh_SWjU&e= .

-
Oracle http://www.oracle.com
Pablo Fuentes | Consultor de Middleware de Oracle
Móvil: +34653961879
Oracle Consultoría de Oracle

Oracle España | ORACLE España las Rozas Madrid
Green Oracle http://www.oracle.com/commitment Oracle se compromete a
desarrollar prácticas y productos que ayuden a proteger el medio ambiente

Me parece que el enfoque run once + loop + delegate (limited to serial=1 behavior): no funciona en una declaración include_tasks cuando el inventario tiene dos "hosts de inventario", y cada host tiene el mismo valor para ansible_host .

Dados dos hosts de inventario con el mismo ansible_host , el enfoque _se ejecuta dos veces_; sin embargo, ambas iteraciones son contra el mismo host.

Existe un problema importante con la mayoría de las soluciones alternativas propuestas: el uso de la CPU y la memoria, así como la desaceleración masiva de la implementación. El método para comprobar que inventory_hostname == item en un bucle with_items es O (n ^ 2), que combinado con una gran cantidad de hosts puede aumentar la memoria y la carga de la CPU en gran medida.

Con 200 hosts, he visto que ansible usa 20GB de ram y 70 load avg solo para serializar un bloque include_tasks . Esa tarea en particular tomó varios minutos solo para decidir qué hosts incluir.

+1

Cualquiera está invitado a probar # 42528 para sus casos de uso y agregar un: +1: al PR si lo aprueba.

¿Fue útil esta página
0 / 5 - 0 calificaciones