image

О системе управления конфигурациями Ansible мы уже писали два года назад. Мы активно её используем в собственной практике и внимательно следим за всеми изменениями и обновлениями.

Конечно же, мы не могли оставить без внимания следующую новость: вышла в свет вторая бета-версия Ansible v2.0. Черновой вариант Ansible v2.0 был размещён на GitHub уже давно, а теперь наконец-то появился более или менее стабильный бета-релиз.

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

Что нового


Никаких радикальных изменений во второй версии нет — об этом уже давно говорил один из разработчиков Ansible Джеймс Каммарата в докладе, прочитанном в этом году на конференции AnsibleFest в Нью-Йорке.

Появилось много новых модулей, а в некоторые из старых модулей были внесены изменения. Разработчики гарантируют стопроцентную совместимость со сценариями (Playbooks) для предыдущих версий.

Однако изменения затронули внутренние API, и тем, кто пользуется «самописными» плагинами, придётся эти плагины скорректировать. Разработчики уверяют (см. доклад по ссылке выше), что переход на новую версию должен произойти без особых проблем.

Подробный список всех изменений опубликован здесь.

Сообщения об ошибках


Система оповещения об ошибках в первой версии была довольно неудобной. Вот пример ошибки в имени модуля:

- hosts: all
  gather_facts: no
  tasks:
    - debug: msg="hi"
    - not_a_syntax_error_just_invalid_module: msg="error"
    - debug: msg="bye"

При обнаружении этой ошибки первая версия выдавала весьма лаконичное сообщение:

ERROR: not_a_syntax_error_just_invalid_module is not a legal parameter in an Ansible 
task or handler 

Вторая же версия выдаёт гораздо более информативное сообщение и указывает место где была обнаружена ошибка:

ERROR! no action detected in task

The error appears to have been in '... .yml': line 5, column 44, but may  
be elsewhere in the file depending on the exact syntax problem.

The offending line appears to be:

  - debug: msg="hi"
  - not_a_syntax_error_just_invalid_module: msg="error"
                                           ^ here

Это делает процесс работы с Ansible более удобным, а во многих случаях ещё и экономит время.

Блоки


Ещё одной интересной новацией являются блоки. Во многих языках программирования для обработки исключений используется конструкция try/except/finally. Похожая конструкция появилась и в Ansible:

- hosts: localhost
  connection: local
  gather_facts: no
  tasks:
  - block:
      - command: /bin/false
      - debug: msg="you shouldn't see me"
    rescue:
      - debug: msg="this is the rescue"
      - command: /bin/false
      - debug: msg="you shouldn't see this either"
    always:
      - debug: msg="this is the always block, it will always be seen"

Вывод при выполнении приведённого сценария будет выглядеть примерно так:

PLAY [<no name specified>] ******************************************************

TASK [command] ******************************************************************  
fatal: [localhost]: FAILED! => ...

NO MORE HOSTS LEFT **************************************************************

CLEANUP TASK [debug msg=this is the rescue] *************************************  
ok: [localhost] => {  
    "msg": "this is the rescue", 
    "changed": false
}

CLEANUP TASK [command] **********************************************************  
fatal: [localhost]: FAILED! => ...

CLEANUP TASK [debug msg=this is the always block, it will always be seen] *******  
ok: [localhost] => {  
    "msg": "this is the always block, it will always be seen", 
    "changed": false
}

Если при выполнении блока случится ошибка, то будут выполнены действия, указанные в секциях rescue и always. После этого выполнение сценария будет остановлено.

Стратегии


В новой версии Ansible можно выбирать, в какой последовательности будут выполняться содержащиеся в сценарии задания. Возможные варианты выполнения называются стратегиями (strategy). Существует два вида стратегий:

  • линейная (linear)— работает так же, как и в предыдущей версии: выполнение нового задания начнётся после того, как текущее задание будет выполнено на всех хостах;
  • произвольная (free) — на каждом хосте задание выполняется как можно быстрее и без учёта того, что происходит на других хостах.

Поясним сказанное конкретным примером и рассмотрим следующий фрагмент сценария:

- hosts: all
  gather_facts: no
  strategy: free
  tasks:
    - pause: seconds={{ 10 |random}}
    - debug: msg="msg_1"
    - pause: seconds={{ 10 |random}}
    - debug: msg="msg_2"
    - pause: seconds={{ 10 |random}}
    - debug: msg="msg_3"

Если бы мы выбрали традиционную (линейную) стратегию, то при выполнении этого сценария увидели бы такой вывод (приводим в несколько сокращённом варианте):

TASK [debug msg=msg_1] **********************************************************  
ok: [host3] => { "msg": "msg_1", "changed": false}  
ok: [host4] => { "msg": "msg_1", "changed": false}  
ok: [host2] => { "msg": "msg_1", "changed": false}  
ok: [host1] => { "msg": "msg_1", "changed": false}

TASK [debug msg=msg_2] **********************************************************  
ok: [host4] => {"msg": "msg_2", "changed": false}  
ok: [host1] => {"msg": "msg_2", "changed": false}  
ok: [host2] => {"msg": "msg_2", "changed": false}  
ok: [host3] => {"msg": "msg_2", "changed": false}

TASK [debug msg=msg_3] **********************************************************  
ok: [host1] => {"msg": "msg_3", "changed": false}  
ok: [host2] => {"msg": "msg_3", "changed": false}  
ok: [host3] => {"msg": "msg_3", "changed": false}  
ok: [host4] => {"msg": "msg_3", "changed": false}

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

Если же выбрана произвольная стратегия, вывод будет совсем другим:

PLAY [<no name specified>] ******************************************************  
ok: [host3] => {"msg": "msg_1", "changed": false}  
ok: [host4] => {"msg": "msg_1", "changed": false}  
ok: [host2] => {"msg": "msg_1", "changed": false}  
ok: [host4] => {"msg": "msg_2", "changed": false}  
ok: [host2] => {"msg": "msg_2", "changed": false}  
ok: [host4] => {"msg": "msg_3", "changed": false}  
ok: [host1] => {"msg": "msg_1", "changed": false}  
ok: [host2] => {"msg": "msg_3", "changed": false}  
ok: [host3] => {"msg": "msg_2", "changed": false}  
ok: [host3] => {"msg": "msg_3", "changed": false}  
ok: [host1] => {"msg": "msg_2", "changed": false}  
ok: [host1] => {"msg": "msg_3", "changed": false}

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

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

Include + with


Очень часто новое — это хорошо забытое старое. В более ранних версиях Ansible уже были конструкции вида:

main.yml:

- hosts: localhost
  connection: local
  gather_facts: no
  tasks:
    - include: foo.yml some_var={{ item }}
      with_items:
        - a
        - b
        - c

foo.yml:

- debug: msg={{some_var}}

В версии 1.5 и всех последующих они уже были исключены. Во второй версии они вернулись.

Наследование блоков и ролей


Значения ‘become’ (пришло на замену ‘sudo’ c версии 1.9) и другие теперь могут быть присвоены блокам и ролям, и их будут наследовать все задания, включённые в эти блоки и роли:

- hosts: all
  gather_facts: false
  remote_user: testing
    - roles: {role: foo ,  become_user: root}
  tasks:
    block:
      - command: whoami
      - command: do/something/privileged
      - stat = path=/root/ .ssh/id_rsa
        become_user: root


Как попробовать


Новая версия уже доступна для скачивания здесь. Кроме того, её можно собрать из исходного кода, размещённого на GitHub:

$ git clone https://github.com/ansible/ansible.git
$ cd ansible
$ git checkout v2.0.0-0.4.beta2
$ git submodule update --init
$ . hacking/env-setup

При желании можно собрать deb-пакет:

$ make deb

Или rpm-пакет:

$ make rpm

Заключение


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

А вы уже пробовали новую версию Ansible? Если пробовали, приглашаем поделиться впечатлениями в комментариях. Если мы забыли рассказать о каком-либо значимом нововведении — напишите нам, и мы обязательно добавим его в обзор. Также призываем сообщать обо всех замеченных ошибках разработчикам на Github, используя этот шаблон.

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

Комментарии (13)


  1. 1it
    05.11.2015 20:37
    +1

    А с тегами там уже все хорошо, не знаете?


    1. clickfreak
      05.11.2015 21:09
      +2

      Я пока проблем с тегами не замечал, а что с ними было не так?


      1. 1it
        05.11.2015 21:39

        Ну вот например, такая конструкция в контексте плейбука или роли:
        — roles: {role: foo, tags: [«one», «two»]}
        не работает.
        Было много обсуждений, но авторы вроде как ссылались, что исправление будет в версии 2.0.


        1. clickfreak
          06.11.2015 13:59

          проверил в 2.0 — работает, как со --skip-tags=, так и просто c --tags=
          Честно говоря, ранее проблем с этим не замечал. В какой версии проявлялась проблема?


          1. 1it
            06.11.2015 14:01

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


            1. clickfreak
              06.11.2015 15:08

              В плее в таком виде — работает:

                roles:
                  - role: selectel.grafana
                    grafana_use_official_repository: no
                    tags:
                      - grafana
                      - powerdns_dashboard
              

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

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


              1. 1it
                06.11.2015 15:29
                +1

                ansible 1.9.4

                Не работает:

                cat roles/role_name/meta/main.yml
                ---
                dependencies:
                  - { role: role_one, repo: var_name }
                  - { role: role_two, tags: ['single', 'common'] }
                

                С плейбуками пробовал как в документации указано:
                You may also apply tags to roles:
                roles:
                — { role: webserver, port: 5000, tags: [ 'web', 'foo' ] }
                Так тоже не работало.

                Ваш пример выше на какой версии работает?


                1. clickfreak
                  06.11.2015 16:33
                  +3

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

                  Как отметка факта выполнения роли — работает уже давно, иначе бы у меня всё сразу поломалось :) в 2.0 тоже работает. Однако действительно есть неоднозначности и расхождения:

                  • 1.9.4: если у роли есть зависимости и они внутри не помечены тегом, то таски зависимостей не будут выполнены, только сама роль
                  • 2.0b2: если у роли есть зависимости и они внутри не помечены тегом, то зависимостей выполняются.

                  Что касается указания зависимостей в meta для выполнения конкретных тасков требуемых ролей: не работает ни в одной из версий.

                  Должен признать что раньше с тегами у нас не возникало никаких проблем, но, похоже, нужно копнуть эту тему поглубже и идти бодаться в issues :)


                  1. 1it
                    06.11.2015 16:52

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

                    Можно конечно использовать переменные, но мне кажется удобней настраивать логику выполнения тегами (субъективно).


  1. Kanedias
    05.11.2015 22:41

    В свете недавних новостей вопрос к вам как в некоторой мере вовлечённому человеку: как вы относитесь к покупке Ansible компанией Red Hat? Как по вашему мнению это скажется на перспективах?


    1. clickfreak
      06.11.2015 02:17
      +3

      Скорее положительно. Минимум:


      Так что точно не закапают :D Будут дальше улучшать интеграцию с Fedora/RHEL-based утилитами.


  1. isden
    06.11.2015 10:17

    > Стратегии

    А есть же вот такое уже. Если кроме linear/free ничего больше нет, то зачем оно?


    1. clickfreak
      06.11.2015 14:29
      +3

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

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

      Т.к. стратегии реализованы в виде плагинов, то можно реализовать свою специфичную логику (два важных таска — синхронно, следующие — асинхронно). Более подробно реализацию можно посмотреть в коде: github.com/ansible/ansible/blob/devel/lib/ansible/plugins/strategy