Ansible: поддержка «серийника» по индивидуальной задаче

Созданный на 31 авг. 2015  ·  65Комментарии  ·  Источник: ansible/ansible

(Идея функции)

Естественным способом обработки конфигурации или других обновлений, требующих последовательного перезапуска, было бы параллельное выполнение обновлений с последующим уведомлением обработчика, который выполняет перезапуск с помощью serial . Но это невозможно, требуя либо ручного непрерывного перезапуска, либо уродливых хаков. См. Https://groups.google.com/forum/#!topic/ansible -project / rBcWzXjt-Xc

affects_2.1 feature

Самый полезный комментарий

@bcoca , я думаю, это необходимо, вы можете позволить этому открыться как нечто необходимое, когда это будет легче реализовать.

Возможности программного обеспечения не могут быть остановлены техническими трудностями.

Все 65 Комментарий

: +1:

+1
Это было бы здорово для наших сценариев развертывания ansible-ceph!

: +1:

+1

+1

+1; однако серийный номер не должен приводить к сбою всей игры для оставшихся хостов, если все хосты в текущем последовательном интерфейсе выходят из строя. Мне часто приходится выполнять циклический перезапуск, по одному серверу на более чем 50 серверах. Отстой, когда игра не удается на сервере 3, потому что на сервере 3 возникло какое-то странное неожиданное состояние, которое привело к сбою перезапуска. Установка max_failpercent на значение выше 100% должно заставить ansible продолжить игру для оставшихся хостов.

+1

+1!

+1

+1

: +1:

+1

+1

+1

Это хорошая идея, но она должна переходить не только от playbook к отдельным задачам, но и к блокам, включая все зависимые параметры, такие как max_fail_percentage и run_once.

Пример обновления-перезагрузки может легко объяснить это:

  • Задача: Сервер обновлений (все параллельные)
  • необязательная задача: проверьте, не требуется ли перезапуск сервера
  • Блок: (серийный)

    • Перезагрузите сервер

    • Подождите, пока сервер вернется

  • Дополнительные шаги (все параллельно)

+1

+1 за это.

+1

+1, непрерывный перезапуск полезен для распределенных систем, которые связаны цепочкой. Для Ceph мы не хотим перезапускать все демоны хранения одновременно, потому что файл конфигурации изменился.

+1

+1

+1

обходной путь:

- 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 благодарит за ваше предложение, однако, похоже, это привело к этой ошибке: https://github.com/ansible/ansible/issues/15103
По крайней мере, для меня я применил ваш обходной путь следующим образом: with_items: "[{% for h in groups[mon_group_name] %}'{{ h }}'{% if not loop.last %} ,{% endif %}{% endfor %}]" .

Я что-то упускаю?

Отличная идея. Хотя с Ansible 1.9 это можно упростить, выполнив:

with_items: '{{play_hosts}}'

@alvaroaleman благодарит за обходной путь.

это там какое-то решение для серийника: 30%?

Классная идея.

Предполагает, что задача знает, какие хосты работают в ней, ИЛИ нацелена на все хосты из игры.

Если это задача роли (которая могла быть нацелена на что угодно), это не имеет смысла.

Вы можете создать временную группу, но тогда вы не сможете запустить эту задачу дважды, не настроив дважды несколько хостов.

Учитывая то, как работают «серийный» и игровой процесс, это не совсем то, что мы видим в Ansible. У вас есть несколько способов реализовать что-то похожее с существующими функциями, многие другие обсуждались выше и в других местах, поэтому я собираюсь подробно описать только два, которые охватывают большинство случаев.

  • Используйте промежуточную игру (позволяет использовать много серийных номеров различий, но требует, чтобы предыдущие задачи завершались на всех хостах):
- hosts: all 
  tasks:
   - anything:...
  ....

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

- hosts: all 
  tasks:
   - morestuff:...
  ....
  • запустить один раз + цикл + делегат (ограничено поведением serial = 1):
- mytask: ..
  delegate_to: "{{item}}"
  run_once: true
  # many diff ways to make the loop
  with_inventory_hostnames: all

Я пытался поиграть с плагином стратегии, который делает это ... но для этого пришлось бы воссоздать половину основной утилиты Ansible, и у него все еще было бы много проблем при работе с другими частями системы (например, обратные вызовы ).

На данный момент я не вижу ни одного варианта использования, не охваченного каким-либо из вышеперечисленных обходных путей, и очень сомневаюсь, что кто-то в ядре справится с этим, поэтому я собираюсь закрыть эту проблему.

Конечно, если кто-то отправит код, который может добавить эту функцию разумным образом, он будет рассматриваться для включения, но я сомневаюсь, что в настоящее время это возможно (или я недостаточно умен, чтобы увидеть способ).

@bcoca , я думаю, это необходимо, вы можете позволить этому открыться как нечто необходимое, когда это будет легче реализовать.

Возможности программного обеспечения не могут быть остановлены техническими трудностями.

@detiber спасибо за информацию.

Действительно, я решил свою проблему с помощью описанного здесь обходного пути.

У меня есть прокси-кеш, и когда я устанавливаю пакеты для своего кластера, мне нужно сначала установить их на одном компьютере, а их со всеми кэшированными пакетами я устанавливаю на других машинах.

- 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

Но я думаю, что эта функция может быть интересна большему количеству морских свиней, и seria: - самая простая и логичная форма для ее выполнения.

@bcoca Мы используем ваше второе предложение в нашем проекте для реализации поведения serial = 1. Единственный отрицательный аспект заключается в том, что сводка в конце игры оказывается неверной, потому что каждое одобрение или изменение засчитывается первому хосту, а не делегату. Вы можете придумать какое-нибудь решение?

Мы действительно не хотим использовать первое предложение, потому что у нас есть некоторые зависимости, и нам нужно будет включать роли как в начале, так и в конце игры, чтобы поддерживать использование тегов.

Благодарность

@ kami8607, вы можете попробовать это, хотя не тестировалось с последними версиями:

- 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 ​​большое спасибо! Работает как шарм.
(К вашему сведению, мы используем ansible 2.0.2.0)

@ kami8607, вы также можете попробовать заменить маркер hack на play_hosts .

@hryamzik .Очень хорошее, отличное решение для нас сейчас. Еще раз спасибо :)

Привет. Мы обнаружили одну проблему с решением, предоставленным @hryamzik.
Если задача не выполняется на одном из этих «псевдосерийных» хостов, задача выполняется на других хостах, а не сразу. Что бы мы ни пытались, мы не могли пропустить сценарий сразу после сбойного хоста.

Может у кого-нибудь есть решение для нас. Спасибо

Привет, я считаю, что это действительно важная функция для безопасного использования зависимостей ролей.

В нашем проекте мы моделируем большинство наших пьес с помощью зависимостей ролей, поскольку это позволяет создавать короткие и легко читаемые сценарии и избегать дублирования.

Если у меня есть роль A, которая зависит от роли B, и опасно обновлять B на нескольких хостах одновременно, то, насколько я понимаю, единственный поддерживаемый способ добиться этого - установить serial: 1 для игры, которая использует роль A . Это может быть неприемлемо, если у вас много хостов и много ролей, зависящих от B.

Мы также используем решение @hryamzik прямо сейчас, но, как сказал @ kami8607 , ansible не останавливается при обнаружении сбоя.

Также @bcoca , я не верю, что подобные обходные пути должны быть целью при разработке такого инструмента, как ansible. Кажется, есть много людей с похожими вариантами использования, которым требуется какое-то решение. Как сказал @ pando85 , технические трудности не должны быть причиной для закрытия этого вопроса.
Было бы очень хорошо, если бы этот тикет можно было повторно открыть или рассмотреть какое-то другое решение.

Большой старый +1 от меня ... судя по тому, как все устроено, мне кажется, что я смогу включить циклический перезапуск, добавив serial: 1 в любой из моих обработчиков перезапуска. Затем любое изменение конфигурации, обновление версии и т. Д. Приведет к непрерывному перезапуску.

+1

+1

+1

Без этого роли фактически БЕСПЛАТНЫ в большом масштабе.

+1 действительно хорошая идея

Также в поддержку этого. Мы используем роли, и для того, чтобы использовать какие-либо обходные пути, нам нужно было бы извлечь только сериализованные задачи в игру или файл задачи, включенный в нее, тем самым нарушив инкапсуляцию ролей.

+1

+1

Если задача не выполняется на одном из этих «псевдосерийных» хостов, задача выполняется на других хостах, а не сразу. Что бы мы ни пытались, мы не могли пропустить сценарий сразу после сбойного хоста.

@ kami8607 Я столкнулся с той же проблемой с ошибками, так как any_errors_fatal: true .

Я также подтверждаю, что это решение работает с include_tasks , однако режим check выполняется параллельно.

- 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

Если вы посмотрите на комментарии, получившие 👎, это потому, что они бессмысленны, поскольку весь их контент говорит «+1».

В этой ветке также есть рабочее решение.

есть обходной путь, который работает для большинства частей, но без решения

В этой ветке нет рабочего решения. Хотя для некоторых это может сработать, это не решение.
register не работает должным образом (будет суть, чтобы подтвердить это позже) - я предполагаю, что это не единственная функция, которая не будет работать правильно.

@jonhatalla У меня нет проблем с register , вы можете поделиться сутью или репо, которое не работает?

Я хотел бы ограничить только задачу загрузки артефактов (из-за некоторых ограничений), а остальные задачи выполнять параллельно.

Я пришел с предложением после прочтения комментариев, которые все еще не работают должным образом для случая загрузки. Обратите внимание, что 2 - это максимальное желаемое количество одновременных выполнений задач.

    - 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)"

Хотя это будет соответствовать when на каждой итерации, только если для определенных хостов я все еще могу видеть весь сервер, выполняющий загрузку одновременно.

Другой способ тестирования - отладка сообщения и добавление задержки между итерациями. Таким образом ясно, что на каждой итерации выполняются только две.

    - 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)"

Я обнаружил эту тему проблемы благодаря этому вопросу SO

Есть идеи, почему загрузка, похоже, не работает, как сообщение отладки?

На данный момент я не вижу вариантов использования, на которые не распространяется ни одно из вышеперечисленных обходных путей.

Как говорили предыдущие комментаторы, я также не вижу обходного пути для обработчиков, перезапускающих службы в кластере, где вы не хотите перезапускать все узлы одновременно. Итак, есть по крайней мере один вариант использования, в котором, похоже, нет решения ... Эти обработчики рендеринга в этом случае совершенно бесполезны, поскольку они используются для перезапуска служб, КОГДА только это необходимо.

И все другие обходные пути (например, обработка одновременной записи в файл локальных хостов) работают, но они такие уродливые ...

Наконец, я согласен, закрытие проблемы, потому что это слишком большая проблема, чтобы ее решить, немного удручает ...

@zwindler вы можете использовать задачи вместо обработчиков. На самом деле я использую скользящие перезапуски с проверками API. Реализовано с помощью include_task, works as expected. You can even try include_task` непосредственно в обработчиках, но я понятия не имею, работает это или нет.

Не совсем уверен, что понимаю, что вы предлагаете.

  • Вы имеете в виду, что используете include_task для перезапуска служб и делаете это только с предложением when: , чтобы проверить, должен ли перезапуск происходить на этом узле?
  • Как вы можете гарантировать, что только один узел перезапускает свои службы за раз (в этом вся проблема)? Вы имеете в виду, что можете добавить serial с помощью 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

в этом случае serial=1 моделируется для всех задач внутри install_configure.yml .

как были определены Healthy_servers? как это используется в обходном пути? Я не вижу, чтобы на него ссылались. Я хочу, чтобы последовательная задача применялась ко всем хостам, против которых запускается playbook.

@erpadmin, почему бы вам в этом случае не использовать play_hosts ?

Всем привет,
мы обнаружили аналогичную проблему, и мы не можем передать аргумент и использовать его в playbook:
серийный номер: $ {serial_mode}
но если не получается:
ValueError: недопустимый литерал для int () с базой 10: 'serial_mode'

кажется, это указывает на эту ошибку, но хотелось бы уточнить:

  • есть ли обходной путь для этой проблемы ??
  • какая официальная версия с этим исправлением?

спасибо за вашу помощь и, пожалуйста, держите нас в курсе.

С уважением, Пабло.

да, тоже пробовал с этой и той же проблемой.

спасибо, Пабло.

08.01.2018 11:02 Йоханнес Наджар написал:
>

вы пробовали {{serial_mode}}

-
Вы получили это, потому что прокомментировали.
Ответьте на это письмо напрямую, просмотрите его на 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= ,
или отключить поток
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
Пабло Фуэнтес | Консультант по промежуточному программному обеспечению Oracle
Мобильный: +34653961879
Oracle Консультации по Oracle

Oracle Испания | ORACLE Испания лас-Росас Мадрид
Экологичный Oracle http://www.oracle.com/commitment Oracle стремится к
разработка методов и продуктов, которые помогают защитить окружающую среду

Мне кажется, что подход run once + loop + delegate (limited to serial=1 behavior): не работает с оператором include_tasks когда в инвентаре есть два «хоста инвентаря», причем каждый хост имеет одинаковое значение для ansible_host .

Учитывая два хоста инвентаризации с одинаковыми ansible_host , подход _исполняется дважды_; однако обе итерации предназначены для одного и того же хоста.

Основная проблема с большинством предлагаемых обходных путей - это использование ЦП и памяти, а также значительное замедление развертывания. Метод проверки того, что inventory_hostname == item в цикле with_items равен O (n ^ 2), что в сочетании с большим количеством хостов может сильно раздуть память и нагрузку на ЦП.

С 200 хостами я видел, как ansible использует 20 ГБ оперативной памяти и 70 загрузок в среднем только для сериализации блока include_tasks . Эта конкретная задача заняла несколько минут, чтобы решить, какие хосты включить.

+1

Всем предлагается протестировать # 42528 для своих сценариев использования и добавить: +1: в PR, если вы одобряете.

Была ли эта страница полезной?
0 / 5 - 0 рейтинги