Ansible идемпотентный. Алексей Соколов


    Привет! Меня зовут Алексей Соколов. Я представляю компанию mail.ru. И сегодня мы с вами поговорим об Ansible.




    Сначала маленький опрос. Кто хоть раз работал с Ansible? Чудно, почти все. И это очень показательная вещь. Ansible – это обычно тот инструмент, который начинают использовать, в первый раз придя в историю про DevOps.


    А кто с Ansible ушел в пользу других инструментов? Например, в Salt, Chef? Почему?


    На самом деле история в следующем. Обычно люди приходят в профессию, начинают заниматься с Ansible. Все им нравится, все хорошо. Да, действительно, работает. Да, действительно, просто и комфортно.



    Потом хочется чего-то посложнее, помощнее. И они начинают лезть в другие истории: в Chef, в Puppet. Почему? Потому что считается, что это более мощные решения, централизованные и удобные решения. И создается мнение, что Ansible – это инструмент для новичков. Пришли, попробовали – вроде работает.


    На самом деле очень многие не используют функционал Ansible так, как он задумывался изначально. И получается замкнутый круг. Люди приходят, люди думают, что это простой инструмент. Они начинают использовать его как простой инструмент. Забивают на большинство функций, а потом разочаровываются и уходят.


    Люди приходят, фигачат что-нибудь, что потом ни фига не работает, что потом никто не может использовать. Это прямая цитата достаточно известного человека.



    И он прав. Я сам столкнулся с такой же историей.



    Я пришел в Ansible. Мне дали простую задачку. Сказали, что есть сценарии Ansible, нужно запустить, все сработает.


    Не сработало.


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


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



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



    Как выйти из ситуации? Простой пользователь просто забьет на это дело. Он сделает все руками. Ему не интересно копаться в инструменте, разбираться, как он работает и как его починить.


    У разработчика терпения обычно побольше. Он полезет, посмотрит. Где-то накостыляет, где-то что-то подправит. Все запустится, и он забудет про это как про страшный сон.



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


    Хорошо – это как?


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


    Поговорим мы о том, как это сделать хорошо, но часть оставим за кадром.


    • Во-первых, не будем говорить о стилистике написания, т. е. о том, как сам код должен выглядеть. Понятно, что эта тема важная, потому что, если человек не может прочитать, что там написано, то непонятно, как это поддерживать.
    • Во-вторых, не будем упоминать хранение и версионирование. Понятно, что это должно быть. Понятно, что в случае чего: git blame + один телефонный звонок и мы как-то решаем проблему.


    О чем поговорим?


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

    Вот это, наверное, главный слайд во всей презентации:



    Все люди, которые начинают работать с Ansible, должны это вбить себе в голову: Ansible – это не Shell.


    В Ansible люди приходят обычно из администрирования, где все привыкли писать shell-скрипты. Ansible – это не про это. Ansible – это про декларативную модель, про то, что мы приводим систему в какое-то состояние.



    И под капотом у Ansible есть огромный инструментарий для того, чтобы это делать.


    • Казалось бы, обычный SSH. Имеем доступ на машинку по SSH, можем что-то Ansible’м сделать, можем руками.
    • Но Ansible делает для нас гораздо больше. Это огромное количество кода на Python, который серьезно прорабатывает все, что вы хотите сделать.
    • Код собирается в модули. И модули на каждый случай жизни. Большая часть того, что люди решают shell’ом, в модуле уже реализована. Не надо изобретать велосипед.


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


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


    Мы хотим, чтобы там что-то лежало. Мы хотим, чтобы там что-то работало, что-то запускалось и т. д.


    Мы приводим ее к состоянию, а не пытаемся сделать что-то руками.



    И отсюда как следствие: необходимо избегать shell. Т. е. если вы пишете в Ansible просто обертку, чтобы запустить shell-сценарий или makefile, или что-то в этом духе, то вы делаете неправильно. Так не надо делать. Для всего есть модули.



    Лучше избегать shell любой ценой. Почему? Мы об этом поговорим чуть позже.



    А пока поговорим про результат.


    • Мы убираем shell. Мы начинаем использовать модули и внезапно вывод Ansible начинает приобретать какой-то конкретный смысл.
    • И вместо того, чтобы думать, что не упало и хорошо, мы понимаем, что вот тут что-то поменялось.
    • А вот тут ничего не поменялось и это значит, что система в нужном состоянии уже была.

    И мы можем понять, что сделал Ansible после того, как мы его запустили.



    Как убедиться, что все настроено корректно? Как понять, что все так, как мы хотим? Через идемпотентные сценарии.



    Что это такое? Википедия дает нам конкретное определение идемпотентности, но нам это не особо интересно. Мы интерпретируем его для себя.



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



    Если совсем проще: не работает – почини, работает – не трогай.



    С чего начать? Как начать разбираться, что Ansible делает?


    • Во-первых, узнать, что на самом деле происходит под капотом.
    • Во-вторых, сравнить с тем, что должно происходить. Т. е. вы хотите какой-то результат и смотрите, насколько Ansible все правильно делает.
    • В-третьих, понять, что происходить не должно. Бывает, что вы пишете код, который потом что-то ломает, т. е. вы что-то не учли, где-то не подумали и т. д. Это тоже надо проверять.


    Как это делать?


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


    Есть гораздо более простой способ. Это модули register, т. е. директивы register, которые позволяют нам зарегистрировать то, что модуль возвращает. Модуль там что-то сделал и сказал: «Я сделал вот это». И мы можем вот это посмотреть, выведя переменную, которую мы зарегистрировали.



    И в этой переменной мы увидим целую кучу информации:


    • Каков результат, что Ansible сделал на самом деле.
    • Что изменилось.
    • Что осталось неизменным.


    Это примерный вывод того, как даже самая простая команда копирования выводит нам кучу информации из-под капота. Это и diff, и контрольные суммы, и код вывода, если это команда, и права на файлы и т. д.


    Все вот это вот мы можем сравнить с тем, что мы хотим. Хотим ли мы действительно такие права? Правильно ли мы скопировали файл? Та ли там чек-сумма?



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


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


    Придумали какой-то сценарий. Что-то там пошло не так. Вроде бы не страшно. Где-то что-то не проверилось и т. д. Ignore_errors – поехали дальше.


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



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


    И, кстати, лирическое отступление про предыдущую тему, про то, что не нужно использовать shell: даже Ansible нам иногда об этом подсказывает. Он прямо явно говорит: «Вы что делаете, ребята? Какой rm, какой shell-скрипт в Ansible? У нас есть модуль для этого. Пользуйтесь!»



    Какая мораль? Знайте, что вы игнорируете. Старайтесь понимать для чего вы ignore_errors пишете.



    И бывает три типовых ситуации:


    • Во-первых, если мы запускаем какой-то сценарий. Действительно, бывают ситуации, когда без этого не обойтись. И есть какая-то специальная утилита, которую нам нужно запустить. Нам не хочется писать свой модуль. И мы решили сделать это командой. И если она не выходит с кодом возврата 0 по какой-то причине, мы решаем проигнорировать.
    • Бывают ситуации, когда есть состоянии гонки (англ. race condition). Мы что-то хотим проверить. Оно должно было к этому моменту запуститься и не запустилось. Вроде не страшно, если не проверим. Ignore_errors – поехали.
    • Избыточные действия. Допустим, у нас есть необходимость скачать что-то из одного места. Не получилось скачать из одного места, скачаем из второго. На первом мы делаем игнорирование ошибок, запускаем второй сценарий.


    Как эти проблемы решать так, чтобы это было качественно и надежно?


    Для первого есть register. Мы берем вывод команды, который выходит с exit code ненулевым. Регистрируем его вывод. И говорим, что она должна падать при определенных условиях. Для этого в Ansible есть директива failed_when. Мы можем самому модулю сказать, какое условие он должен считать таковым, чтобы думать, что он упал.


    Мы обрабатываем вывод, либо просто код вывода. Смотрим, что на самом деле там произошло. И какую-то свою логику ему вписываем. Мы говорим: «Если вот это произошло, тогда ты свалился». А какие-то свои истории, когда он вроде бы с точки зрения системы свалился, а на самом деле нет, мы описываем, учитываем и просто пролетаем мимо.



    Вот один из примеров. Мы берем простую команду, мы выходим со 124-ым кодом. И говорим в явном виде: «Если код возврата не 124, тогда он сломался». Если 124, то все хорошо для нас, если не 124, то что-то упало. Выходим с кодом 124 и видим, что на самом деле Ansible считает, что все хорошо.


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


    Но есть маленькая проблемка. Проблемка кроется наверху. Ansible считает, что что-то поменялось. А команда «exit 124» что-то меняет на системе? Очевидно, что нет. И нам это тоже надо учесть.




    • Для этого есть другая директива changed_when, т. е. мы можем сказать модулю, когда считать, что модуль что-то поменял. Если мы знаем, что команда ничего не меняет, мы можем сказать, что она никогда ничего не меняет: changed_when: false и Ansible всегда будет писать, что там ничего не поменялось, что все Ок.


    • Если мы работаем с файлами, то все может быть интересней. Есть директивы: creates и removes. Это директивы, которые позволяют нам сказать, что команды, которые мы выполняем, создают что-то, либо удаляют что-то.


      И, соответственно, модуль будет обрабатывать это следующим образом: если мы создаем какой-то файл и там стоит creates для этого файла и этот файл на системе есть, то Ansible посчитает, что все Ок. Он даже не будет эту команду запускать, он ее пропустит и все.


      Тоже самое с removes. Если файла на системе нет, команду он не будет запускать, пройдет мимо.


    • И, наконец, мы можем обработать вывод и код выхода. Мы можем поставить условную логику, которая будет проверять даже stdout, stderr команды. И на основе этого будет делать выводы – сломался на самом деле или не сломался.




    Как это выглядит на практике? Почти та же самая история. Почти та же самая команда. Мы говорим, что changed_when: false. Эта команда у нас ничего не меняет. Ansible ее благополучно проскакивает. Он пишет: «Ок, там все хорошо».



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


    Классический сценарий, когда мы используем для этого pause. Просто ставим на паузу на несколько секунд и надеемся, что за эти несколько секунд сценарий сделает то, что он хочет.


    Гораздо правильней будет ждать конкретной ситуации. Для этого есть модуль wait_for. Мы можем сказать: «Подожди, пожалуйста, пока произойдет конкретное событие». И это событие благодаря Ansible может быть почти чем угодно. Это может быть появление файлов в файловой системе. Это может быть открытие какого-то сокета. Это может быть конкретное подключение по tcp, если оно с конкретным статусом, который нам нужен. Т. е. есть достаточно богатый функционал для того, чтобы проверять какие-то условия.


    Если уж совсем не в терпеж и нам нужно пользователя дождаться, то мы тоже это можем сделать. Мы можем сделать паузу с выводом конкретной строки. И тогда вместо того, чтобы ждать какое-то определенное время, сценарий будет ждать ввода от пользователя. Сценарий будет ждать, пока пользователь кнопочку нажмет и все заработает.



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



    И для пользователя примерно так же. Мы говорим: «Pause», выводим какое-то сообщение пользователю. И пока пользователь кнопочку не нажмет, ничего дальше не поедет. Это очень удобно для сценариев, когда нам нужно подтверждения со стороны пользователя, например, на установку чего-то.



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



    Пример тоже живенький. Мы говорим, что у нас запускается exit 0. Он никогда не фэйлится. И если вдруг предыдущая первая команда exit 0 не отработала, мы выполняем вторую. В первом случае мы, где второй пункт, просто пролетаем мимо, потому что условие не выполнилось. Во второй раз у нас первое условие не выполняется, соответственно, выполняется второе.


    Это очень хорошо помогает не делать одну и ту же работу дважды.



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


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



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


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


    Обратите внимание на самую нижнюю строчку: failed_when: true. Если мы этого не сделаем, то сценарий на этом шаге поедет дальше. Мы должны в явном виде сказать: «Выведи пользователю сообщение и свались». И тогда пользователь поймет, что здесь какое-то действие нужно. И вместо того, чтобы видеть вот это огромное полотно с ошибками, он прочитает и поймет, что надо «поплакать и залезть под стол».



    О мелочах. Казалось бы, мелочи, но на самом деле бывают очень неприятные ситуации с этими вещами.



    Во-первых, handler.


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


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


    Учитывайте, что кто-то может вам вот здесь подгадить. И сами не гадьте другим, не флашьте handlers без крайней необходимости. Потому что тоже самое, что можно сделать flush_hundlers вручную, можно сделать другим способом – when: changed. Вы просто берете какой-то таск и говорите, что если тот шаг поменялся, то запусти.


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



    Переменные. С переменными очень весело. Помните, что переменные – это глобальная вещь. Т. е. на весь запуск, на все роли, которые вы запускаете в рамках одного playbook, переменные всегда одни и те же. И если вы задаете простую переменную типа port, она и в этой роли port, и в другой роли port, если вы ее, конечно, там явно не переопределите.


    • Здесь можно очень неприятно напороться. Старайтесь использовать имена переменных, которые содержат в себе название роли. Таким образом, вы защите себя от пересечения.
    • И мое любимое – это слияние словарей.


    Слияние словарей – это «прелестно». Вы берете один словарь в дефолтах, вы пишете какие-то переменные. И оп, и первые взяли и потеряли просто целиком. Почему? Потому что механика слияния словарей по умолчанию в Ansible подразумевает полную замену. Вы берете один ключ верхнего уровня, и все, что под ним он просто перетирает.



    Есть способ это обойти. Есть hash_behavior. Это параметр конфигурационного файла Ansible, который позволяет сливать словари динамически и подтягивать все эти вещи.


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



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


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



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



    Вы будете видеть, что у вас система Ок по всем статьям, у вас ничего не поменялось, если вам не нужно было этого делать.


    Подразумевается, что это вывод второго запуска. Первый – у вас что-то поменялось, что-то переделалось, все сделано так, как надо. Второй запуск – вы видите очень четко, что у вас система в нужном вам состоянии.


    И если вдруг на этом моменте вы видите changes не 0, то вы сами понимаете, что что-то на системе поменялось, что мы не учли. Например, кто-то туда залез, кто-то ручками подковырял или там что-то упало.


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


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



    • Привыкайте к декларативной модели. Привыкайте к тому, чтобы не использовать в Ansible shell, а использовать Ansible на всю его мощь.
    • Используйте модули везде, где только можно. Зачастую не нужно изобретать велосипед, все уже придумано до вас.
    • Обрабатывайте свои собственные ошибки, которые можете как-то предугадать. И думайте над тем, где еще что-то может упасть. И о том, что где-то нужно подкостылять, чтобы оно держалось стабильней и как-то пользователю эту информацию возвращало.
    • И помните про окружение. Сценарии могут запускаться где угодно и кем угодно.


    Самое главное: вы пишете код не только для себя, вы пишете код для того, кто будет его использовать. Делайте его удобным не только для себя, не только для эксперта, но и для других, кто к этому совершенно не подготовлен.



    Всем спасибо! Я буду рад ответить на вопросы.


    Вопросы


    Меня зовут Владимир. Мне интересна сфера применения Ansible. Что именно вы разворачиваете? Только ПО или, может быть, операционки еще при помощи него?


    Чаще всего это инфраструктурные задачи, т. е. какие-то вещи, например, задача развернуть на нескольких серверах, например, типа Kubernetes. Это больше инфраструктурные задачи.


    Привет! Спасибо за доклад! Какое количество ролей примерно вы сейчас поддерживаете? Я так понимаю, что все сейчас упирается в поддержку идемпотентных ролей. И как вы боретесь с версионированием этих ролей и с поддержкой разных версий?


    Количество ролей измеряется десятками. Не так много.


    Что касается версионирования, то обычно делается одна роль на конкретную версию ПО, которое разворачивается. И зачастую держится в branch. Отдельный branch – отдельная galaxy-роль = отдельный инфраструктурный элемент какой-то конкретной версии.


    Привет! Меня зовут Лев. Есть вопрос: «Вы тестируете роли свои и используете линтеры: какой-нибудь YAML, Ansible-lint или фреймворки типа Молекулы?».


    Тестировать YAML-lint – это обязательная вещь. Потому что, если там ошибку совершить, то все полетит.


    Что касается тестирования функционала конкретной роли, то зачастую – это какие-то проверки, что что-то запустилось в самом конце этой роли. Мы ожидаем, что у нас запустится какое-то приложение на каком-то порту, мы этот порт проверяем. Опционально смотрим вывод на этом порту. Если все соответствует, то мы считаем, что – да, эта роль, скорее всего, запустилась.


    Более детального тестирования мы не делаем. Там не такие катастрофически сложные роли, чтобы это реализовывать.


    Спасибо за доклад! Вы еще по каким-то причинам против hash_behavior merge, кроме того, что его разработчики не любят? Как вы делаете, если у вас несколько окружений и вы хотите кучу одинаковых переменных, и хотите различие переменных помержить?


    Проще эту задачу решать через defaults, через нормальные дефолты и переменные одного уровня, т. е. не плодить словари, а делать переменные в один список.


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


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


    Да, все время наталкиваются. А вы дефолты имеете в виду в ролях?


    Да, дефолты для ролей.


    Большое спасибо за доклад! Как вы соблюдаете безопасность от какого пользователя выполняются непосредственно Ansible-роли? Как распределяются ключи? И вы все роли от root запускаете или у вас отдельный пользователь под запуск Ansible-ролей? Как происходит дело с безопасностью?


    Везде, где это не критично, если мы не говорим о запуске SystemD, хотя даже там можно обойтись sudo, обычно все сценарии не из-под root.


    Привет! Вы, наверное, используете в своих ролях include_task, import_task? Если используете, то как к этому относитесь, потому что получается, что одна роль зависит от другой или от каких-то общих вещей? И есть ли у вас роль common?*


    Роли common нет.


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


    Импортится через симлинки или как?


    Через импорт таск отдельным файлом. Лежит рядышком файл.


    Из роли в роль копируется этот файл?


    Обычно он привязан к роли. Обычно это просто какой-то элемент этой роли.


    Ты говоришь, что он переиспользуется …


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


    Задам более точный вопрос. Где именно в структуре каталогов лежит этот общий файл?


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


    А между ролями нет никаких?


    Сейчас нет.


    Спасибо за доклад! Используете ли вы одну версию Ansible или их несколько? И как вы решаете проблему с их обновлением? И как вы храните секреты в ролях?


    Секреты чаще всего храним в Ansible-vault, хотя есть более удачное решение.


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


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


    Т. е. вы не управляете никак Ansible, который запускается? Грубо говоря, пользователь сам выбирает?


    Да, чаще всего пользователь сам выбирает.


    Здравствуйте! Спасибо за доклад! Что вы делаете со скоростью работы? Одна из главных проблем, почему мы отказались от Ansible, это как раз скорость его работы при большой инфраструктуре, где 300-400-500 серверов.


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


    P.S. Помимо этого доклада у автора есть воркшоп, выложенный на Гитхабе, который может пригодиться.

    Only registered users can participate in poll. Log in, please.

    Используете ли вы метод «purge» (purge — удаление объектов, которые не описаны в SCM, например, юзеров) в ваших SCM (Chef, Puppet, Ansible, Salt)?

    • 28.6%Да (встроено или написал сам)4
    • 42.9%Нет, оно не нужно6
    • 7.1%Нет, но хотелось бы иметь такую возможность1
    • 14.3%У меня нет SCM (Chef, Puppet, Ansible, Salt)2
    • 7.1%Я не знаю что это такое1

    Similar posts

    Ads
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More

    Comments 106

      +1
      И если реально нужно какое-то высокопроизводительное решение, рассчитанное на большое количество серверов, то – да, наверное, стоит от Ansible отходить в пользу других централизованных решений, которые умеют все это разруливать гораздо более надежно. Ansible – не про то, чтобы разрулить несколько сотен, тысяч серверов.
      Тем не менее есть различные способы ускориться, от разных strategy плагинов до добавок типа Mitogen.
      В крайнем случае можно и ansible pull использовать.
        +1

        Пулл фигня, он ломает всю идею ансибла
        Митоген — мой коллега, tnt4brain в очередной статье рассказывал про его подводные камни

        +1

        Concentrated field experience — очень круто, спасибо!


        Перспектива малого бизнеса

        У нас полсотни серверов и десяток персоналок, и мы пока боимся добавлять vendor lock (даже open source) на критическом пути. Пробовали Ansible — для отчетов по оборудованию и софту — быстро научились (там всё более-менее процедурно). Для интеграции с нашей разнородной инфраструктурой пошло не очень, надо было много гуглить как делать идиоматически, либо делать всё процедурно и утонуть в этой лапше в конце. Ну и если что не так, то долго разбираться. Держать дополнительные знания в голове, когда еще до фига чего надо — тяжело.


        Мы хорошо умеем настраивать Debian и сеть и писать на Bash & Python (пару десятков лет опыта у каждого), ну и программировать на "нормальных" языках (С++, Scala, Java, etc.). Ещё мы очень жадные не хотим делиться деньгами если можем сделать сами.


        Поэтому пока что потихоньку подписываем своё небольшое JSON-driven самопальное на Python/Requests/Paramiko/CherryPy. Что не работает — выкидываем и переделываем с нуля. Пока неплохо получается, и декларативность с идемпотентностью в норме. Мы также приняли строгий закон делать change management как с кодом и ничего не хачить вручную — это было ключевым моментом.


        Наверное мы тоже сдадимся когда вырастем еще, но у нас будет хорошая спецификация всего на JSON/Python и будет проще решить на что пересесть.


        Как-то так.

          0
          А почему не тот же Fabric? Неужели писать свой велосипед выгоднее
            0

            Fabric это который библиотека на питоне? Просто не попался пока, посмотрю обязательно, спасибо за наводку!


            Свой велосипед выгоднее/удобнее был пока что. Мы стараемся делать только самое самое необходимое пока растем и часто меняемся. Обычно сначала вообще делаем руками по чеклисту, чтобы отловить основные косяки, потом автоматизируем самым базовым способом, потом обобщаем если имеет смысл.

              –1
              «Это не те SCM, которые вы ищете» (пасс руками по кругу).
              Если я скажу, что Ansible — это как Fabric на стероидах, так понятнее будет? Ну и никто не мешает ровно тем же способом использовать Ansible: в качестве нормальной такой платформы.
          0
          Вооот! Ну наконец-то хоть кто-то озвучил самый логичный юзкейс! Для ансибла самое годное применение это быстрые развёртывания и быстрые же похороны без малейших попыток сопровождения.
            0

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

              0
              Был кейс прямо вот по слайду: разворачивали инфраструктуру Энсиблом, но довольно быстро упёрлись в предел применимости (началось всё с танцев вокруг zabbix-proxy) и очень больно мигрировали на Шефа. Но смигрировали-таки и не пожалели. Сопровождение — когда инстансов становится больше тысячи (bare metal) клиент/агент не просто нужен, а очень-преочень нужен.
                0

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

                  +2
                  Про «быстро похоронить»: когда я понял, что systemd понятнее наших ролей, а инвентори перевалил за третий килобайт и обзавёлся собственными README и TODO, мы с CTO сели и взгрустнули. Потом я показал тестовый стенд с Шефом.
                    0
                    Ни в коем случае не назову Chef серебряной пулей. Как минимум, он «странненький» в плане видимости функций. А ещё есть два способа подружить его с SELinux:
                    * отключить к чёртовой бабушке SELinux.
                    * совершенно непонятные танцы с capabilities и не только.
            0

            Кстати, мое мнение про решение для большого количества серверов: chef/puppet + ansible.


            chef/puppet хорошо подходит для большой инфраструктуры. Можно с помощью chef/puppet устанавливать системные пакеты, пакеты, которые должны быть установлены на всех серверах, например zabbix-agent. Но chef/puppet не подходит для blue-green deployment, rolling update не используя kubernetes.


            Аnsible не очень хорошо подходит для для большой инфраструктуры. Аnsible подходит для blue-green deployment, rolling update не используя kubernetes.

              +1
              Вы предлагаете их сочетать и получить артефакты от всего и сразу?
                0
                получить артефакты от всего и сразу

                Ошибки?

                  0
                  Скорее именно артефакты, потому что с точки зрения каждой из систем всё будет «just as planned», но в реальности будет вообще всё что угодно кроме ожидаемого результата.
                  И да, про идемпотентность в таком случае лучше даже и не вспоминать.
                    0

                    Можете привести пример?

                      +1
                      Из собственного опыта, к счастью, не могу. Но умозрительная ситуация напрашивается сама:
                      Одновременное исполнение Энсиблом и Шефом любых операций с пакетным менеджером приведёт к блокировке у кого-то из них. А в случае с pip'ом, я даже не хочу себе представлять, что получится. Как такого добиться при рационалных настройках? Да запросто! Надо просто выключить хост на сутки и «очень вовремя» включить, чтобы Энсибл «дозвонился» именно тогда, когда шеф втянул новые конфиги и радостно приступил к исполнению.
                      Тут ИМХО первичен другой вопрос: вот мы по сути дублируем инструментарий, а чтобы что?
                        0

                        Я писал выше можно с помощью chef/puppet устанавливать системные пакеты. То есть установка пакетов только через yum/apt. Не думаю что будут проблемы запуске yum от puppet и от ansible. Просто будет подольше.


                        Используя chef/puppet + ansible можно устанавливать zabbix-agent на тысячи серверов и делать обновление продукта/бизнес-программы через blue-green deployment, rolling update не используя kubernetes.

                          0
                          Используя chef/puppet + ansible можно устанавливать zabbix-agent на тысячи серверов и делать обновление продукта/бизнес-программы через blue-green deployment, rolling update не используя kubernetes.

                          А без дублирования инструментария, получается, всё это нельзя?
                            0

                            Используя только chef/puppet сложно сделать обновление продукта/бизнес-программы через blue-green deployment, rolling update.


                            Используя только ansible сложно управлять тысячами серверов.

                              +1

                              Это не дублирование инструментария, инструменты решают разную задачу, ansible хоть и пытается решить задачу менеджмента конфигураций, но решает её хуже на мой взгляд.
                              В задачах деплоймента приложения на хосты, не надо ставить пакеты. С этим прекрасно справятся puppet/chef. Зато выполнять в строгом порядке какие-то действия для rolling деплоймента приложения, при этом иметь доступ ко всем переменным и фактам всех хостов куда идет деплоймент — с этим уже лучше справятся ansible/bolt/choria.
                              То есть инструменты вполне совместимы, когда ты ими не решаешь одни и те же задачи.

                                0

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

                                  0

                                  Нет. bolt хорошо дополняет экосистему puppet. Более того, модули для puppet могут содержать tasks, которые можно использовать в plans(аналог плейбука) bolt'а. bolt умеет работать с puppetdb.
                                  Он призван закрыть задачи, которые очень трудно решить puppet'ом либо их решение с помощью puppet не рационально.

                        +2

                        Не согласен, поскольку ansible и puppet всё таки разные инструменты, и с определенными задачами puppet лучше справляется (менеджмент конфигураций), а с другими ansible (оркестрация/ad-hoc задачи). То вполне их можно безболезненно объединять. Более того, puppet как раз и предлагает это делать, правда вместо ansible предлагает использовать bolt. Но это по сути тот же ansible, только с DSL puppet'а и лучше встраивается в его экосистему.

                    0
                    chef и puppet

                    Пора закопать оба. Как минимум — из-за руби.


                    Аnsible не очень хорошо подходит для для большой инфраструктуры. Аnsible подходит для blue-green deployment, rolling update не используя kubernetes.

                    Смотри. Ты на chef и puppet можешь сделать то же самое. Придётся немного попрограммировать модули ) но это эквивалентно написанию сценариев (=плейбуков) ансибл. Но зато экосистема единая.

                      0
                      Золотые ваши слова. Я ведь теперь в кошмарах буду видеть инфраструктуру, в которой и Энсибл, и Шеф. Это не просто странно, а скорее даже debugging-driven debugging. К тому же бессмысленный и бесконечный.
                        +2

                        Дело в том что в экосистеме puppet четко разделены задачи. 1) декларативное описание инфры — puppet, и оркестрация/ad-hoc задачи/другая автоматизация — bolt/choria. Вы просто не знакомы с таким подходом вот и всё. Замени puppet на chef, а bolt на ansible ничего не изменится, и получили решение тех же задач только с помощью chef + ansible вместо puppet + bolt. Совершенно нормальный подход

                        0
                        Смотри. Ты на chef и puppet можешь сделать то же самое. Придётся немного попрограммировать модули )

                        Мне кажется не получится. Но если у тебя получится, поделись опытом.

                      +2
                      Про декларативную модель чуть подробней. Общий смысл в том, что мы не делаем какой-то процесс, мы не выполняем какие-то команды, а мы хотим привести систему в желаемое состояние.

                      декларативность там какая-то недостаточно декларативная ИМХО.


                      я не могу, например, сказать «на системе должны быть user1, user2 и всё», я могу сказать «добавь пользователя user1» и «удали пользователя user4». если в системе был кем-то заведён user5, то ansible никак об этом не узнает.
                      и так во всём — от списка репозиториев в sources.list до конфигов нжинкса в conf.d.


                      я не воспринимаю конфиги ансибла как декларативные, скорее они императивные с идемпотентностью.

                        +1

                        Формально они, большинство из них, всё же деклариративные, просто не поддерживается "и всё". Только явное описание, что должно быть в системе и и явное описание чего не должно быть в ней. Что не описали — состояние неизвестно. Если вам это важно — опишите явно. И это разумный подход для подавляющего большинства случаев. Особенно с учётом последовательного выполнения.

                        +2

                        edo1h


                        я не воспринимаю конфиги ансибла как декларативные, скорее они императивные с идемпотентностью

                        + только не «конфиги», а «плейбуки»


                        я не могу, например, сказать «на системе должны быть user1, user2 и всё», я могу сказать «добавь пользователя user1» и «удали пользователя user4». если в системе был кем-то заведён user5, то ansible никак об этом не узнает.

                        По второму — да, не узнаёт, если явно это не прописать («получить список пользователей», «отфильтровать нужные», «создать недостающие», «шлепнуть лишние»). Но это беда любого SCM. И того, что система может вносить в себя изменения вне SCM (например, при установке пакетов). По идее решением мог быть интегрированная в саму операционную систему система управления конфигурацией. Пример как в NixOS. Но это не мейнстрим.
                        По первой части — ну, зависит от того, как модуль написан. Именно к модулю user претензий нет. Все стандартно state=present — проверяет есть ли юзер и если надо — создаёт. Аналогично со state=absent.

                          +3
                          Но это беда любого SCM

                          Я бы так не сказал. Тот же пресловутый puppet знает о всех пользователях и других ресурсах в системе, даже если они не описаны. Это заложено в архитектуре провайдеров модулей, которые управляют этими ресурсами. Там обязательно реализуется метод — "получить все ресурсы данного типа".


                          Например ресурс user:


                          Скрытый текст
                          $ puppet resource user
                          user { '_apt':
                            ensure             => 'present',
                            gid                => 65534,
                            home               => '/nonexistent',
                            password           => '*',
                            password_max_age   => 99999,
                            password_min_age   => 0,
                            password_warn_days => 7,
                            shell              => '/usr/sbin/nologin',
                            uid                => 104,
                          }
                          user { 'bin':
                            ensure             => 'present',
                            comment            => 'bin',
                            gid                => 2,
                            home               => '/bin',
                            password           => '*',
                            password_max_age   => 99999,
                            password_min_age   => 0,
                            password_warn_days => 7,
                            shell              => '/usr/sbin/nologin',
                            uid                => 2,
                          }
                          user { 'daemon':
                            ensure             => 'present',
                            comment            => 'daemon',
                            gid                => 1,
                            home               => '/usr/sbin',
                            password           => '*',
                            password_max_age   => 99999,
                            password_min_age   => 0,
                            password_warn_days => 7,
                            shell              => '/usr/sbin/nologin',
                            uid                => 1,
                          }
                          user { 'dnsmasq':
                            ensure             => 'present',
                            comment            => 'dnsmasq,,,',
                            gid                => 65534,
                            home               => '/var/lib/misc',
                            password           => '*',
                            password_max_age   => 99999,
                            password_min_age   => 0,
                            password_warn_days => 7,
                            shell              => '/usr/sbin/nologin',
                            uid                => 107,
                          }
                          user { 'games':
                            ensure             => 'present',
                            comment            => 'games',
                            gid                => 60,
                            home               => '/usr/games',
                            password           => '*',
                            password_max_age   => 99999,
                            password_min_age   => 0,
                            password_warn_days => 7,
                            shell              => '/usr/sbin/nologin',
                            uid                => 5,
                          }
                          user { 'gnats':
                            ensure             => 'present',
                            comment            => 'Gnats Bug-Reporting System (admin)',
                            gid                => 41,
                            home               => '/var/lib/gnats',
                            password           => '*',
                            password_max_age   => 99999,
                            password_min_age   => 0,
                            password_warn_days => 7,
                            shell              => '/usr/sbin/nologin',
                            uid                => 41,
                          }
                          user { 'irc':
                            ensure             => 'present',
                            comment            => 'ircd',
                            gid                => 39,
                            home               => '/var/run/ircd',
                            password           => '*',
                            password_max_age   => 99999,
                            password_min_age   => 0,
                            password_warn_days => 7,
                            shell              => '/usr/sbin/nologin',
                            uid                => 39,
                          }
                          user { 'landscape':
                            ensure             => 'present',
                            gid                => 112,
                            home               => '/var/lib/landscape',
                            password           => '*',
                            password_max_age   => 99999,
                            password_min_age   => 0,
                            password_warn_days => 7,
                            shell              => '/usr/sbin/nologin',
                            uid                => 108,
                          }
                          user { 'list':
                            ensure             => 'present',
                            comment            => 'Mailing List Manager',
                            gid                => 38,
                            home               => '/var/list',
                            password           => '*',
                            password_max_age   => 99999,
                            password_min_age   => 0,
                            password_warn_days => 7,
                            shell              => '/usr/sbin/nologin',
                            uid                => 38,
                          }
                          user { 'lp':
                            ensure             => 'present',
                            comment            => 'lp',
                            gid                => 7,
                            home               => '/var/spool/lpd',
                            password           => '*',
                            password_max_age   => 99999,
                            password_min_age   => 0,
                            password_warn_days => 7,
                            shell              => '/usr/sbin/nologin',
                            uid                => 7,
                          }
                          user { 'lxd':
                            ensure             => 'present',
                            gid                => 65534,
                            home               => '/var/lib/lxd/',
                            password           => '*',
                            password_max_age   => 99999,
                            password_min_age   => 0,
                            password_warn_days => 7,
                            shell              => '/bin/false',
                            uid                => 105,
                          }
                          user { 'mail':
                            ensure             => 'present',
                            comment            => 'mail',
                            gid                => 8,
                            home               => '/var/mail',
                            password           => '*',
                            password_max_age   => 99999,
                            password_min_age   => 0,
                            password_warn_days => 7,
                            shell              => '/usr/sbin/nologin',
                            uid                => 8,
                          }
                          user { 'man':
                            ensure             => 'present',
                            comment            => 'man',
                            gid                => 12,
                            home               => '/var/cache/man',
                            password           => '*',
                            password_max_age   => 99999,
                            password_min_age   => 0,
                            password_warn_days => 7,
                            shell              => '/usr/sbin/nologin',
                            uid                => 6,
                          }
                          user { 'messagebus':
                            ensure             => 'present',
                            gid                => 107,
                            home               => '/nonexistent',
                            password           => '*',
                            password_max_age   => 99999,
                            password_min_age   => 0,
                            password_warn_days => 7,
                            shell              => '/usr/sbin/nologin',
                            uid                => 103,
                          }
                          user { 'news':
                            ensure             => 'present',
                            comment            => 'news',
                            gid                => 9,
                            home               => '/var/spool/news',
                            password           => '*',
                            password_max_age   => 99999,
                            password_min_age   => 0,
                            password_warn_days => 7,
                            shell              => '/usr/sbin/nologin',
                            uid                => 9,
                          }
                          user { 'nginx':
                            ensure             => 'present',
                            comment            => 'nginx user,,,',
                            gid                => 119,
                            home               => '/nonexistent',
                            password           => '!',
                            password_max_age   => 99999,
                            password_min_age   => 0,
                            password_warn_days => 7,
                            shell              => '/bin/false',
                            uid                => 115,
                          }
                          user { 'nobody':
                            ensure             => 'present',
                            comment            => 'nobody',
                            gid                => 65534,
                            home               => '/nonexistent',
                            password           => '*',
                            password_max_age   => 99999,
                            password_min_age   => 0,
                            password_warn_days => 7,
                            shell              => '/usr/sbin/nologin',
                            uid                => 65534,
                          }
                          user { 'ntp':
                            ensure             => 'present',
                            gid                => 114,
                            home               => '/nonexistent',
                            password           => '*',
                            password_max_age   => 99999,
                            password_min_age   => 0,
                            password_warn_days => 7,
                            shell              => '/usr/sbin/nologin',
                            uid                => 111,
                          }
                          user { 'pollinate':
                            ensure             => 'present',
                            gid                => 1,
                            home               => '/var/cache/pollinate',
                            password           => '*',
                            password_max_age   => 99999,
                            password_min_age   => 0,
                            password_warn_days => 7,
                            shell              => '/bin/false',
                            uid                => 110,
                          }
                          user { 'postfix':
                            ensure             => 'present',
                            gid                => 117,
                            home               => '/var/spool/postfix',
                            password           => '*',
                            password_max_age   => 99999,
                            password_min_age   => 0,
                            password_warn_days => 7,
                            shell              => '/usr/sbin/nologin',
                            uid                => 113,
                          }
                          user { 'proxy':
                            ensure             => 'present',
                            comment            => 'proxy',
                            gid                => 13,
                            home               => '/bin',
                            password           => '*',
                            password_max_age   => 99999,
                            password_min_age   => 0,
                            password_warn_days => 7,
                            shell              => '/usr/sbin/nologin',
                            uid                => 13,
                          }
                          user { 'root':
                            ensure             => 'present',
                            comment            => 'root',
                            gid                => 0,
                            groups             => ['ssh-allow-login'],
                            home               => '/root',
                            password           => '!',
                            password_max_age   => 99999,
                            password_min_age   => 0,
                            password_warn_days => 7,
                            shell              => '/bin/bash',
                            uid                => 0,
                          }
                          user { 'sshd':
                            ensure             => 'present',
                            gid                => 65534,
                            home               => '/run/sshd',
                            password           => '*',
                            password_max_age   => 99999,
                            password_min_age   => 0,
                            password_warn_days => 7,
                            shell              => '/usr/sbin/nologin',
                            uid                => 109,
                          }
                          user { 'statd':
                            ensure             => 'present',
                            gid                => 65534,
                            home               => '/var/lib/nfs',
                            password           => '*',
                            password_max_age   => 99999,
                            password_min_age   => 0,
                            password_warn_days => 7,
                            shell              => '/usr/sbin/nologin',
                            uid                => 114,
                          }
                          user { 'sync':
                            ensure             => 'present',
                            comment            => 'sync',
                            gid                => 65534,
                            home               => '/bin',
                            password           => '*',
                            password_max_age   => 99999,
                            password_min_age   => 0,
                            password_warn_days => 7,
                            shell              => '/bin/sync',
                            uid                => 4,
                          }
                          user { 'sys':
                            ensure             => 'present',
                            comment            => 'sys',
                            gid                => 3,
                            home               => '/dev',
                            password           => '*',
                            password_max_age   => 99999,
                            password_min_age   => 0,
                            password_warn_days => 7,
                            shell              => '/usr/sbin/nologin',
                            uid                => 3,
                          }
                          user { 'syslog':
                            ensure             => 'present',
                            gid                => 106,
                            groups             => ['adm', 'projects'],
                            home               => '/home/syslog',
                            password           => '*',
                            password_max_age   => 99999,
                            password_min_age   => 0,
                            password_warn_days => 7,
                            shell              => '/bin/false',
                            uid                => 102,
                          }
                          user { 'systemd-network':
                            ensure             => 'present',
                            comment            => 'systemd Network Management,,,',
                            gid                => 102,
                            home               => '/run/systemd/netif',
                            password           => '*',
                            password_max_age   => 99999,
                            password_min_age   => 0,
                            password_warn_days => 7,
                            shell              => '/usr/sbin/nologin',
                            uid                => 100,
                          }
                          user { 'systemd-resolve':
                            ensure             => 'present',
                            comment            => 'systemd Resolver,,,',
                            gid                => 103,
                            home               => '/run/systemd/resolve',
                            password           => '*',
                            password_max_age   => 99999,
                            password_min_age   => 0,
                            password_warn_days => 7,
                            shell              => '/usr/sbin/nologin',
                            uid                => 101,
                          }
                          user { 'uucp':
                            ensure             => 'present',
                            comment            => 'uucp',
                            gid                => 10,
                            home               => '/var/spool/uucp',
                            password           => '*',
                            password_max_age   => 99999,
                            password_min_age   => 0,
                            password_warn_days => 7,
                            shell              => '/usr/sbin/nologin',
                            uid                => 10,
                          }
                          user { 'uuidd':
                            ensure             => 'present',
                            gid                => 110,
                            home               => '/run/uuidd',
                            password           => '*',
                            password_max_age   => 99999,
                            password_min_age   => 0,
                            password_warn_days => 7,
                            shell              => '/usr/sbin/nologin',
                            uid                => 106,
                          }
                          user { 'www-data':
                            ensure             => 'present',
                            comment            => 'www-data',
                            gid                => 33,
                            home               => '/var/www',
                            password           => '*',
                            password_max_age   => 99999,
                            password_min_age   => 0,
                            password_warn_days => 7,
                            shell              => '/usr/sbin/nologin',
                            uid                => 33,
                          }

                          И получается задача:


                          я не могу, например, сказать «на системе должны быть user1, user2 и всё», я могу сказать «добавь пользователя user1» и «удали пользователя user4». если в системе был кем-то заведён user5, то ansible никак об этом не узнает.

                          Решается так:


                            user {'user1':
                                ensure => present,
                            }
                            user {'user2':
                                ensure => present,
                            }
                            resources {'user':
                                purge => true,
                                unless_system_user => true, # Чтобы не удалить системных пользователей
                            }

                          То есть в puppet очень просто можно сказать — удали все ресурсы какого-то типа, которые не описаны, поскольку он знает о всех ресурсах которыми управляет в системе. Конечно с пакетами в системе я бы так не советовал делать.


                          в ansible декларативность там какая-то недостаточно декларативная ИМХО.

                          Да, я тоже так считаю. ansible императивный, поскольку просто выполняет таски как скрипт, каждую по очереди. Например:


                          tasks:
                            - file:
                                path: /tmp/test
                                state: absent
                            - file:
                                path: /tmp/test
                                state: directory

                          Приведет к тому, что директория /tmp/test будет создана. А если поменять местами, то она будет удалена.


                          В puppet такое невозможно. Такая запись приведет к ошибке дубликата ресурсов. Поскольку не может быть одного ресурса с двумя состояниями:


                          file {'/tmp/test':
                              ensure => absent,
                          }
                          file {'/tmp/test':
                              ensure => directory,
                          }

                          Это будет ошибка: Error: Evaluation Error: Error while evaluating a Resource Statement, Duplicate declaration: File[/tmp/test] is already declared at (file: /root/1.pp, line: 1); cannot redeclare (file: /root/1.pp, line: 4) (file: /root/1.pp, line: 4, column: 1) on node


                          И без разницы, в какой последовательности их написать. И даже если попробовать схитрить и сделать так:


                          file {'id2':
                              path   => '/tmp/test',
                              ensure => directory,
                          }
                          file {'id1':
                              path   => '/tmp/test',
                              ensure => absent,
                          }

                          То это все равно приведет к ошибке дубликата: Error: Evaluation Error: Error while evaluating a Resource Statement, Cannot alias File[id2] to ["/tmp/test"] at (file: /root/1.pp, line: 5); resource ["File", "/tmp/test"] already declared (file: /root/1.pp, line: 1) (file: /root/1.pp, line: 5, column: 1) on node test


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


                          Но в эпоху иммутбл инфры, облаков. Когда есть контейнеры, packer, pulumi, terraform, AWS Cloud Development Kit, k8s, nomad и т.д. Наверное вообще не имеет смысла сидеть и сравнивать все эти инструменты (ansible/puppet/chef/solt), так как они потихоньку уходят на задний план.

                            0
                            Тот же пресловутый puppet знает о всех пользователях и других ресурсах в системе, даже если они не описаны

                            это не решает проблему гоночек, в случае, если этим ресурсом пытается управлять какая-то еще другая система. Пример — iptables правила в случае использования docker/любого vpn. Удачи это описать бесконфликтно и чтоб надежно работало.


                            То есть puppet более честен в плане декларативности чем ansible.

                            еще проблема, что нужно не только состояние ("куда мы идем?"), а порядок действий ("как мы туда придем?"). И в том же паппете это делается, но через боль (например, нам обязательно нужно установить пакет А до пакета Б — вот потому что это так работает, есть альтернативы — например, пересобрать пакеты самому и всю логику реализовата в post-inst, но я бы не сказал, что они радикально лучше)


                            Но в эпоху иммутбл инфры, облаков. Когда есть контейнеры, packer, pulumi, terraform, AWS Cloud Development Kit, k8s, nomad и т.д. Наверное вообще не имеет смысла сидеть и сравнивать все эти инструменты (ansible/puppet/chef/solt), так как они потихоньку уходят на задний план.

                            солидарен. Для имутбл инфры как раз ансибл отлично подходит для подготовки базовых образов. Да и кубер тоже отжирает большой кусок рынка от традиционных SCM...

                              0
                              это не решает проблему гоночек, в случае, если этим ресурсом пытается управлять какая-то еще другая система. Пример — iptables правила в случае использования docker/любого vpn. Удачи это описать бесконфликтно и чтоб надежно работало.

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


                              еще проблема, что нужно не только состояние ("куда мы идем?"), а порядок действий ("как мы туда придем?"). И в том же паппете это делается, но через боль (например, нам обязательно нужно установить пакет А до пакета Б — вот потому что это так работает, есть альтернативы — например, пересобрать пакеты самому и всю логику реализовата в post-inst, но я бы не сказал, что они радикально лучше)

                              Да, это так. Поскольку puppet декларативен, то нужно стараться описать конечное состояние. А вот если ты пытаешься им делать "как мы туда придем?" то будет беда скорее всего. Поэтому это делается только если ну очень надо. Но это про более сложные истории чем установить пакет А до пакета Б, поскольку зависимости как раз он умеет без проблем:


                              package {'p1':}
                              package {'p2':
                                require => Package['p1']
                              }

                              "как мы туда придем?" — а для такого обычно и используют ansible, а в экосистеме puppet обычно для такого используют bolt, но можно и ansible.

                                0
                                Да, это так. Поскольку puppet декларативен, то нужно стараться описать конечное состояние. А вот если ты пытаешься им делать "как мы туда придем?" то будет беда скорее всего. Поэтому это делается только если ну очень надо. Но это про более сложные истории чем установить пакет А до пакета Б, поскольку зависимости как раз он умеет без проблем:

                                нам stage puppet приходилось для этого использоваться. require как в сниппете было недостаточно (

                                  0

                                  stage и anchor использовались для этого да. Обычно это говорит о том, что надо рефакторить код из-за большого количества связей ресурсов между разными классами, или наоборот, некоторые ресурсы в текущем классе выделять в отдельный.
                                  Рекомендуется выставлять связи между классами, а не ресурсами которые зависят от ресурсов другого класса.


                                  Типо такого:


                                    Class['proxysql::prerequisites']
                                    -> Class['proxysql::repo']
                                    -> Class['proxysql::install']
                                    -> Class['proxysql::config']
                                    -> Class['proxysql::service']
                                    -> Class['proxysql::admin_credentials']
                                    -> Class['proxysql::reload_config']
                                    -> Class['proxysql::configure']
                                  
                                    Class['proxysql::install']
                                    ~> Class['proxysql::service']

                                  С puppet проблема — он очень сложный. Думаю из-за этого не популярен.

                                    0
                                    С puppet проблема — он очень сложный. Думаю из-за этого не популярен.

                                    а что бы такое обзорное почитать чтобы понять нужно ли оно мне?


                                    очень смущает, что ansible стал стандартом de-facto, многие переходят с puppet/chef/etc на ansible, и мало кто в другую сторону.

                                      +1

                                      Мне кажется нет смысла. Сейчас потихоньку от этих инструментов отказываются или сильно минимизируют их использование в пользу иммутбл инфры. А всякие сложные вещи реализуют на языках программирования. Например операторы для k8s пишут на golang, также установщики самого k8s пишут часто на golang (rke,pke).
                                      Всё меньше и меньше мест, где применяют ansible/solt/puppet/chef. Поэтому начинать переходить на какой-то из них из другого в этом списке, наверное нет смысла.

                                        0
                                        Поэтому начинать переходить на какой-то из них из другого в этом списке, наверное нет смысла.

                                        имеет смысл, если инфра, например, на бареметал… и кубера того же не превидится в обозримом будущем, но я бы все равно смотрел в сторону MAAS/Tinkerbell и прочих методов автоматизации и предоставления metal-as-a-service и дальнейшей immutable infra даже поверх железа

                                      0
                                      Думаю из-за этого не популярен.
                                      Ну когда-то он был довольно популярен и являлся стандартом де-факто, обойдя тот же шеф.
                                      Вряд ли это из-за «сложности», терраформ тоже простотой не страдает, а популярен в своей нише.
                                        +1

                                        Ну вот terraform похож не много на puppet и страдает как мне кажется. Появились неплохие конкуренты: pulumi и AWS Cloud Development Kit.


                                        А порой хочется какую-то задачу просто решить через ansible чем решать ее сложно terraform'ом. Вот например ребята из skyeng так и сделали для задачи управления github'ом: https://habr.com/ru/company/skyeng/blog/516192/

                                          0
                                          pulumi и AWS Cloud Development Kit.
                                          Pulumi только тискал, вроде ничего. А AWS Cloud Development Kit только для AWS. С опенстеком что делать то?
                                          По ссылке там слегка трешово, видимо у терраформа не очень хорошая library на этот случай.
                                0
                                user {'user1':
                                ensure => present,
                                }
                                user {'user2':
                                ensure => present,
                                }
                                resources {'user':
                                purge => true,
                                unless_system_user => true, # Чтобы не удалить системных пользователей
                                }


                                Приниципиально это абсолютно ничем не отличается от Ansible, ну вообще никак.
                                В Ansible модуль user можно добавить такой же функционал как purge => true и все будет работать ровно так же. Это всего лишь добавка к модулю, а не какая-то принципиальная декларативность.
                                Вот пример с директорией это другое дело, там действительна видна разница. Но это выглядит явно не как достоинство puppet.
                                  +1

                                  Отличие принципиальное. puppet знает о всех ресурсах, независимо от того описаны они в коде или нет, ansible знает только что описано в коде, а точнее знает только о тасках которые требуется выполнить.


                                  purge => true — это не функционал модуля user. Таким же образом можно сделать например с пользователями mysql:


                                      resources {'Mysql_user':
                                           purge => true,
                                      }

                                  Или базами clickhouse


                                      resources { 'Clickhouse_database':
                                          purge => true,
                                      }

                                  Тип ресурса и в каком модуле он реализован — не играет роли.
                                  Это архитектура инструмента. Когда пишешь модуль, ты обязан реализовать некоторые методы, в том числе и метод — "достать все ресурсы для этого модуля". puppet получает с мастера все ресурсы, потом смотрит какие есть ресурсы на хосте, смотрит разницу и старается привести ресурсы на хосте к описанным на мастере — если огрублять.


                                  Но это выглядит явно не как достоинство puppet.

                                  Это честная декларативность. Один и тот же ресурс не может иметь два состояния одновременно. Если же может, то это не декларативность. Плохо это или хорошо — зависит от ситуации. Как по мне, именно для управления конфигурациями — это хорошо, а для всяких ad-hoc задач — неудобно, тут лучше взять более императивные инструменты, например ansible.

                                    0
                                    Как по мне, именно для управления конфигурациями — это хорошо, а для всяких ad-hoc задач — неудобно, тут лучше взять более императивные инструменты, например ansible.

                                    именно так, не могу не согласиться. И поэтому я буду утверждать, что ansible — это не SCM. Ни разу. Но может использоваться для настройки окружения.

                                      0
                                      Когда пишешь модуль, ты обязан реализовать некоторые методы, в том числе и метод — «достать все ресурсы для этого модуля».
                                      Так вот именно! Я же об этом и говорю.
                                      Нет никакого принципиального отличия, потому что я в том же модуле ансибла узнаю обо всех ресурсах и так же само могу имплементировать purge. Вся разница в том, что в puppet это must, в Ansible по желанию.
                                        +1

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


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


                                          resources { 'Clickhouse_database':
                                                purge => true,
                                            }

                                        То puppet просто применит метод "удалить ресурс" из твоего провайдера модуля, ко всем базам clickhouse что он видит на хосте, кроме тех что описаны в puppet коде. Тебе со стороны провайдера модуля не надо реализовывать само это сравнение ресурсов, puppet это делает сам. В провайдере модуля только методы изменить аттрибут/аттрибуты ресурса/удалить ресурс/создать ресурс/получить все ресурсы и всё.


                                        В случае с ansible такой механики насколько я знаю нет. Для выполнения task'а он копирует код модуля на хост, и запускает его. Вроде область его видимости текущий task и его свойства, этот код ничего не знает о других task'ах описанных в текущем play или других task'ов которые описаны в play других playbook'ов. Также всю эту механику: сравнить список текущих ресурсов и их свойств на хосте, со всеми tasks и их свойствами во всех playbook'ах, что будут применены к хосту, надо будет делать самому, причем в пределах выполнения каждого task такого типа, что будет очень затратно в плане скорости выполнения как минимум. Я не знаю возможно ли в ansible в пределах выполнения task модулем достать список всех тасков, которые также будут применены к хосту для сравнения со списком ресурсов которые уже есть на хосте, чтобы не удалить лишние ресурсы, но это придется делать самому + это затратная операция, чем больше таких task'ов, тем больше код будет делать одно и тоже для каждого такого типа task.


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


                                        Ну и разница в декларативности также остается. Puppet более декларативен, в нем ты описываешь состояние, а task'и на как в ansible.

                                          0
                                          Насчет ансибл модуля все немного не так. Факты хоста собираются facter-ом при начала плейбука и доступны всем таскам в нем. (А также другим и между запусками тоже с cacheable facts) Мне не нужно знать про другие таски, если я определяю юзеров, я делаю это в одном таске.
                                          В модуле я просто сравниваю имеющихся юзеров и юзеров определенных в таске, а там уже по логике.
                                          Если дальше где-то в плейбуке зачем-то начать опять определять юзеров, то все пойдет заново, защиты от дурака тут нет.
                                            +1

                                            факты это другой механизм, он с данным вопросом не связан. Речь о механизме работы именно с ресурсами в терминах puppet и task'ами в терминах ansible.


                                            Стандартный модуль user в ansible делает что-то с одним пользователем в одном task'e насколько я с ним знаком:


                                            - name: 'user johnd'
                                              user:
                                                name: johnd
                                                comment: John Doe
                                                uid: 1040
                                                group: admin

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


                                            В puppet же, без разницы где и сколько раз ты объявил ресурс user и в каких классах или где-то еще. Всё это в итоге вместе со всеми другими ресурсами попадет на агент в виде json, и агент всё сделает. Для этого не требуется писать какой-то свой специальный модуль user, со своей специальной логикой.

                                              –1
                                              Так как сразу получим проблему при использовании нескольких task в разных плеях и плейбуках, которые используют такой модуль
                                              Нет, не получим никакой проблемы. С чего вдруг?
                                              Наш модуль будет просто напросто удалять всех других пользователей, которые определены стандартным модулем user или же они определены в других task'ах нашим же модулем
                                              Я же написал что защиты от дурака нет, если вы постоянно перезапускаете модуль юзеров без всякой причины, значит вы не понимаете что творите.
                                              Ансибл позволяет вам стрелять хоть очередями по ногам, но в реальном мире с нормальными плейбуками на это нет никаких причин.

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

                                                0
                                                Нет, не получим никакой проблемы. С чего вдруг?

                                                Я возможно не понял о чём идет речь. Но вы же сами писали что нужно написать свой модуль + использовать только один task с ним:


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

                                                Какой модуль ansible имеется в виду? Можете привести пример решения задачи: "нужно чтобы на хосте были пользователи user1, user2, при этом любые другие не системные пользователи (то есть с uid > 1000), которые не объявлены в task'ах модуля user удалялись."


                                                Да, потому что его уже написали.

                                                Я еще раз повторю — это не часть логики конкретного модуля puppet. Никто не пишет эту логику в модулях puppet, она заложена в агенте. У него такой принцип работы, реализовывать это разработчику модуля не требуется. Вы почему-то каждый раз упускаете это из виду. У puppet архитектура другая совсем. Никто его "уже не написал". Эту логику написал разработчики puppet, а не разработчики его модулей.


                                                Итого вы говорите принципиальных отличий нет. Но для реализации
                                                аналогичного в ansible предлагаете:


                                                • Написать свой кастомный модуль
                                                • Использовать task такого модуля только один раз, так как если его вызывать несколько раз с разными параметрами — это выстрел по ногам. Да и в целом такой модуль не юзабелен, и вызовет больше проблем чем пользы.

                                                В puppet это заложено архитектурно. Для этого не надо реализовывать специальную логику в его модулях и писать кастомные модули для такой логики. Нет ограничений на один ресурс(task в ansible). Можно объявлять ресурсы модуля вообще в разных местах, в puppet агент они прилетят все вместе в одном json. Вменяемо, насколько я понимаю, в ansible это не реализовать, в виду другой архитектуры.


                                                Это и есть принципиальные отличия.

                                                  0
                                                  Написать свой кастомный модуль
                                                  Да, там где это надо — люди так и пишут. Например в клаудных модулях.
                                                  То что нет для этого кастомного модуля юзеров значит что это не настолько надо кому-то.
                                                  Для этого не надо реализовывать специальную логику в его модулях и писать кастомные модули для такой логики.
                                                  ОК, я понял, в ансибле надо сделать для этого дополнительные телодвижения, но добиться этого вполне возможно при желании. Нет принципиальных архитектурных блокеров.
                                                  Использовать task такого модуля только один раз, так как если его вызывать несколько раз с разными параметрами
                                                  Не понял вашего юзкейса. Зачем вам вызывать модуль, создающих юзеров, несколько раз с разными параметерами? Вы не решаетесь каких юзеров хотите создать и постоянно меняете конфиг? Это будет точной копией юзер модуля паппета и таким же юзабельным как он.
                                                    +1
                                                    но добиться этого вполне возможно при желании

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


                                                    Нет принципиальных архитектурных блокеров.

                                                    Есть. Я уже выше достаточно подробно рассказал какие.


                                                    Это будет точной копией юзер модуля паппета и таким же юзабельным как он.

                                                    Нет не будет. В puppet я могу использовать модуль user в куче классов одновременно в любых вариациях. В ansible я могу такое делать со стандартным модулем user тоже, но с ограничением о котором мы говорим, а в предложенной вами реализации модуля я смогу использовать только один раз task в одном месте. Это далеко не тоже самое, что в puppet.


                                                    Зачем вам вызывать модуль, создающих юзеров, несколько раз с разными параметерами?

                                                    Чтобы создать разных пользователей. Я могу в puppet иметь разные классы, один для настройки ssh сервер который создает пользователя, другой для mysql, который тоже создает пользователя, третий для nginx с созданием пользователя. И их переиспользовать как угодно. Тоже самое я могу делать и со стандартным модулем user в ansible, правда с ограничением о котором мы как раз говорим. А предложенный вами способ заставит вынести создание пользователей из всех этих ролей в отдельный task, и каждый раз когда я хочу переиспользовать одну из таких ролей мне нужно будет добавлять такой task для модуля user отдельно и согласовывать его с другими ролями.
                                                    А если учесть что мы перепишем все модули, как вы предлагаете, то таких единичных task'ов и параметров к ним будет настолько много, что любое изменение в ролях или удаление/добавление ролей будет адово сложным.

                                                      0
                                                      А предложенный вами способ заставит вынести создание пользователей из всех этих ролей в отдельный task, и каждый раз когда я хочу переиспользовать одну из таких ролей мне нужно будет добавлять такой task для модуля user отдельно и согласовывать его с другими ролями.
                                                      Или не использовать purge как вы делаете это в паппете? Впрочем это уже все усложнение и придумывание новых условий.
                                                      Переписать все модули ansible и использовать для таких модулей всего один task — это нерабочее решение
                                                      Зачем вам переписывать все модули? Не нужно использовать только один таск, зачем вы придумываете?
                                                      Есть. Я уже выше достаточно подробно рассказал какие.
                                                      Нет, никакие из них не архитектурные, всего лишь некоторый легко восполняемый функционал уже встроен в паппет, и который легко можно реализовать в ансибл. Ничего блокирующего тут нет.
                                                        +2
                                                        Или не использовать purge как вы делаете это в паппете? Впрочем это уже все усложнение и придумывание новых условий.

                                                        Так изначальный тред про purge и декларативность. О каких новых условиях идет речь? Напомню о постановке задачи edo1h:


                                                        я не могу, например, сказать «на системе должны быть user1, user2 и всё», я могу сказать «добавь пользователя user1» и «удали пользователя user4». если в системе был кем-то заведён user5, то ansible никак об этом не узнает.
                                                        и так во всём — от списка репозиториев в sources.list до конфигов нжинкса в conf.d.
                                                        я не воспринимаю конфиги ансибла как декларативные, скорее они императивные с идемпотентностью.

                                                        Поэтому ваше высказывание про придумывание новых условий — неправда.


                                                        Зачем вам переписывать все модули? Не нужно использовать только один таск, зачем вы придумываете?

                                                        Тогда лучше объясните что вы предлагаете для реализации задачи, а лучше с примером — я вас к сожалению не понял.


                                                        Нет, никакие из них не архитектурные

                                                        Архитектурные различия между puppet и ansible значительные. ansible выполняет список tasks по порядку, puppet совсем действует по другому, я уже не однократно описывал как. Именно поэтому в ansible хуже с декларативностью и есть большие проблемы с нормальной реализацией purge.


                                                        всего лишь некоторый легко восполняемый функционал уже встроен в паппет, и который легко можно реализовать в ансибл

                                                        Нет, это неправда к сожалению.

                                                          0
                                                          Тогда лучше объясните что вы предлагаете для реализации задачи, а лучше с примером — я вас к сожалению не понял.
                                                          Ответил ниже, я не предполагал что юзеры будут создаваться в разных местах по разным ролям и таскам.
                                                          Обычно делается как-то концентрированно.
                                                          Именно поэтому в ansible хуже с декларативностью и есть большие проблемы с нормальной реализацией purge.
                                                          Я уж точно нигде не говорил что у ансибла лучше с декларативностью, это объективно не так. И архитектурные различия между анисблом и паппетом тоже есть, конечно.
                                                          Я утверждал другое, если вы внимательно читали:
                                                          в ансибле надо сделать для этого дополнительные телодвижения, но добиться этого вполне возможно при желании. Нет принципиальных архитектурных блокеров.
                                                            +1
                                                            Обычно делается как-то концентрированно.

                                                            Обычно по ролям разбросано. Системные и прикладные аккаунты в соотвествующих ролях. Сконцентрировано разве что
                                                            персональные аккаунты для админов операторов и то проблемы с группами могут быть.

                                                    +1
                                                    Можете привести пример решения задачи: «нужно чтобы на хосте были пользователи user1, user2, при этом любые другие не системные пользователи (то есть с uid > 1000), которые не объявлены в task'ах модуля user удалялись.»
                                                    а зачем такое нужно? Какой реальный use case?
                                                      +2

                                                      Дело не в юзерах. Дело в том, что это типовая задача:


                                                      1. получить список контейнеров, оставить только те, которые описаны в [манифесте|плейбуке] и привести их к определенному состоянию. Остальное — шлепнуть
                                                      2. то же самое с правилами iptables
                                                      3. то же самое с файлами
                                                      4. то же самое с пользователями

                                                        N. your option
                                                        +1
                                                        Ну спорные задачи. Если ансибл ничего не знает о каких то контейнерах/пользователях/файлах/объектах — то возможно не стоит их удалять. Вижу очень много подводных камней в такой парадигме. Возможно для каких то специфических задач оно и нужно, но у меня, например, не возникало подобных за все время использования ansible (2+ года)
                                                          0
                                                          Если ансибл ничего не знает о каких то контейнерах/пользователях/файлах/объектах — то возможно не стоит их удалять.

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


                                                          то возможно не стоит их удалять.

                                                          Почему? Предположим, у Вас каталог с конфигурацией vhost nginx. И админ туда зашел своими грязными руками и что-то поменял. Или обновление накатило дополнительный конфигурационный файл. А мы хотим, чтобы структура каталогов /etc/nginx была ровно такая, как мы ее задумали.


                                                          А если, например, мы управляем файлами конфигураций в ролях? Бр… какой-то ад начинается (ладно, можно просто так не делать и "не пытаться хотеть странного")

                                                            –2
                                                            , то легко можно получить дублирование правил, а это не хорошо
                                                            Нет, нельзя, идемпотентность же.
                                                            Или обновление накатило дополнительный конфигурационный файл.
                                                            Удаление этого файла как-раз чревато.
                                                            0
                                                            Ну спорные задачи

                                                            А мне кажется логично. Если я описал в системе управления конфигурациями как должна выглядеть директория /etc/nginx/conf.d и конфигурации в ней, я ожидаю что система будет всегда делать её такой, какой я описал. А если кто-то что-то там поменял, например добавил файл, то естественно это уже не то что я описал в коде, поэтому файл должен быть удалён. Более того, такой лишний файл конфигурации может сломать всю конфигурацию nginx, поэтому от системы управления конфигурациями я ожидаю больших гарантий того, как она за этим следит.


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

                                                              +1
                                                              Если я описал в системе управления конфигурациями как должна выглядеть директория /etc/nginx/conf.d и конфигурации в ней, я ожидаю что система будет всегда делать её такой, какой я описал
                                                              если прям нужно такое, хотя не понимаю зачем — никто не мешает сначала удалить все файлы, а потом проверить нужные. Да лишний шаг, но зато ты точно знаешь что ты делаешь и что хочешь получить.

                                                              Решение в puppet выглядит ружъем, которое рано или поздно выстрелит. И будет больно, имхо
                                                                +1
                                                                никто не мешает сначала удалить все файлы, а потом проверить нужные

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


                                                                Решение в puppet выглядит ружъем, которое рано или поздно выстрелит. И будет больно, имхо

                                                                я так не считаю. Хочешь (и нужно) — используешь, не хочешь — не используешь

                                                                  +1
                                                                  если прям нужно такое, хотя не понимаю зачем — никто не мешает сначала удалить все файлы

                                                                  Это плохое решение и оно не общее для любых типов ресурсов. Аналогичное с правилами iptables или правами к базе приведет к ужасным последствиям. Да и каждый раз удалять все файлы при применении конфигурации это дичь. Если ничего не поменялось, то не надо это менять, а в таком решении мы получим статус о changed task'ах — при любых прогонах. Опять скрипты вместо управления конфигурациями.

                                                                    0
                                                                    Это плохое решение и оно не общее для любых типов ресурсов.
                                                                    и не должно быть общим для разных ресурсов, имхо

                                                                    а в таком решении мы получим статус о changed task'ах — при любых прогонах
                                                                    это да, но не критично

                                                                    Опять скрипты вместо управления конфигурациями.
                                                                    никаких скриптов, только модули
                                                                      0
                                                                      и не должно быть общим для разных ресурсов, имхо

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


                                                                      и не должно быть общим для разных ресурсов

                                                                      Если писать свои костыли для очистки, конечно не должно.


                                                                      никаких скриптов

                                                                      Угу, оно и видно. Вместо ресурсов такси, вместо графа зависимостей, копирование кода на хост и выполнение его по порядку. Упала какая-то таска, handler не выполнился. Обычный императивный скрипт на мой взгляд, хоть и оформлен в yaml формате. Но в этом одновременно и приемущество ansible на задачах не про управление конфигурациями.

                                                0
                                                потому что я в том же модуле ансибла узнаю обо всех ресурсах

                                                нет


                                                Вся разница в том, что в puppet это must, в Ansible по желанию.

                                                да

                                                  0
                                                  потому что я в том же модуле ансибла узнаю обо всех ресурсах

                                                  нет
                                                  Почему нет? Посчитайте сколько модулей в ансибл заканчиваются на "_info" и "_facts". Все они как раз для собирания информации о ресурсах.
                                                    +1

                                                    Ровно потому что модуль не может опираться на «факты» для понимания того — нужно ли применять некое действие или нет. Т.е. факты — это хорошо, для понимания стартовых условий, в которых был применён плейбук (ну, не знаю — там хостнейм, айпи адрес и прочее). Но Вы же сами пишете, что никакой защиты от дурака нет и получается, что в процессе выполнения play факты могут разъехаться с реальностью. Ну, либо необходимо тогда факты обновлять после каждого вызова любого модуля ансибла, но мы бы чокнулись это делать (быстродействие и так не фонтан)

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

                                                        но к фактам, простите, это имеет отношения чуть менее, чем никакого

                                                      +1

                                                      Так нам же нужно не просто собрать информацию о ресурсах на хосте. Нам еще нужно знать какие task этого же модуля на этом хост прилетят. Иначе мы не сможем построить список того, что мы хотим получить, и сравнить со списком того что есть на хосте, а затем привести хост к тому что хотим получить.


                                                      А иначе мы просто будем сравнивать только текущий task с тем что есть на хосте, и так каждый такой task. Что и происходит в ansible.

                                                        0
                                                        Нам еще нужно знать какие task этого же модуля на этом хост прилетят.
                                                        Я не совсем понимаю, они прямо перед вами в плейбуке.
                                                          0

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

                                                            +1

                                                            Насколько я знаком с ansible. Он для выполнения task'а, копирует код модуля на хост и выполняет его, и так с каждым task такого модуля. Область видимости модуля один task и его свойства. Поправьте если я не прав.


                                                            Но опять же это детали. В puppet, при разработке модуля, тебе вообще не надо думать об области видимости модуля, и решать связанные с этим задачи для реализации удаления ресурсов. puppet это делает сам, сравнивая список всех ресурсов(task в терминах ansible) описанных на мастере для этого хоста, с тем что есть в этом хосте. Поэтому у тебя не болит голова о том, как реализовать purge для ресурсов, которые не описаны — оно само.

                                                              0
                                                              Поправьте если я не прав.
                                                              Не правы. Есть контекст плейбука и данных с прошлых запусков если они кешируются.
                                                              Поэтому у тебя не болит голова о том, как реализовать purge для ресурсов, которые не описаны — оно само.
                                                              Да, в ансибл нужно это сделать самому в модуле, и я, честно говоря, не вижу от чего тут может болеть голова. В придачу с другими фишками ансибла у тебя свобода — хочешь пиши модуль чтобы был декларативным, хочешь — оставь только идемпотентным, хочешь — вообще забей и просто запусти баш.
                                                                +1
                                                                Не правы. Есть контекст плейбука и данных с прошлых запусков если они кешируются.

                                                                Контекст текущего плейбука, а других плейбуков, которые тоже будут применены к этому хосту? Прошлые запуски не помогут узнать, что у нас появились новые task'и для модуля в другом playbook'e. Хочется посмотреть примеры на ansible, которые это используют для того чтобы удалять с хоста ресурсы, которые не под управлением ansible.


                                                                я, честно говоря, не вижу от чего тут может болеть голова

                                                                Ну это довольно не тривиальная вещь на мой взгляд. И требует большой работы. Например выше вы предложили ограничить использование модуля одним task'ом — что накладывает очень много ограничений на удобство использования такого модуля.


                                                                хочешь пиши модуль чтобы был декларативным

                                                                Не совсем понял что значит декларативность модуля. Так как модули/провайдеры выполняют как раз императивные действия, как в ansible так и в puppet. От модулей ansible/провайдеров puppet мы требуем идемпотентности как правило.


                                                                Ну вот конструкция:


                                                                tasks:
                                                                  - file:
                                                                      path: /tmp/test
                                                                      state: absent
                                                                  - file:
                                                                      path: /tmp/test
                                                                      state: directory

                                                                Не декларативна, поскольку тут один и тот же объект имеет два разных состояния. Как я понял вы предлагаете переписать модуль file на свой custom_file и делать что-то вроде:


                                                                tasks:
                                                                  - custom_file:
                                                                      - path: /tmp/test
                                                                        state: absent
                                                                      - path: /tmp/test
                                                                        state: directory
                                                                      - path: /etc/nginx
                                                                        state: directory

                                                                И уже в модуле custom_file проверять path и таким образом гарантировать декларативность и выдавать ошибку при одинаковых path. Но на мой вкус это сделает ansible просто не юзабельным, придется вытаскивать все task с использованием модуля file из всех ролей, и описать это все отдельно одним task'ом — что ужасно и не юзабельно плюс сильно ухудшит возможность переиспользования.


                                                                В придачу с другими фишками ансибла у тебя свобода

                                                                Аналогичная свобода есть в любой другой реализации SCM.

                                                                  –1
                                                                  Контекст текущего плейбука, а других плейбуков, которые тоже будут применены к этому хосту? Прошлые запуски не помогут узнать, что у нас появились новые task'и для модуля в другом playbook'e.
                                                                  Нет, будущее ансибл не предсказывает и это не нужно.
                                                                  Например выше вы предложили ограничить использование модуля одним task'ом
                                                                  Использование purge одним таском, а не модуля.
                                                                  Как я понял вы предлагаете переписать модуль file на свой custom_file
                                                                  Нет, я такого не предлагал. Для чего вы это делаете? Какая ваша конечная цель?
                                                                  tasks:
                                                                  - file:
                                                                  path: /tmp/test
                                                                  state: absent
                                                                  - file:
                                                                  path: /tmp/test
                                                                  state: directory
                                                                    0
                                                                    Нет, будущее ансибл не предсказывает и это не нужно.

                                                                    Текущее выполнение task'ов, которых не было в предыдущем запуске, это не будущее, а настоящее.


                                                                    Использование purge одним таском, а не модуля.

                                                                    Как при выполнении task purge, узнать о всех task'ах которые применятся к хосту, чтобы удалить только то, что не описано в плейбуках?


                                                                    Нет, я такого не предлагал. Для чего вы это делаете? Какая ваша конечная цель?

                                                                    Тогда я не понял вас, каким образом вы предлагаете обеспечить декларативность? Конечная цель сделать описание декларативным, и сделать так, чтобы все что не подконтрольно ansible удалялось для указанного типа модуля

                                                                      0
                                                                      Тогда я не понял вас, каким образом вы предлагаете обеспечить декларативность? Конечная цель сделать описание декларативным, и сделать так, чтобы все что не подконтрольно ansible удалялось для указанного типа модуля

                                                                      задачу написания идемпотентных (не декларативных!) плейбуков, очевидно, ансибл перекладывает на плечи разработчика (администратора) плейбуков. Соответственно, в случае плейбуков, которые занимаются какими-то операционными вещами — эта идемпотентность не нужна. В случае плейбуков, которые реализуют использование ansible как SCM средства — нужна. Поди разберись! Но т.к. сам инструмент по сути является инструментом "simple IT automation", то такие вопросы и возникают.

                                                                        0
                                                                        Как при выполнении task purge, узнать о всех task'ах которые применятся к хосту, чтобы удалить только то, что не описано в плейбуках?
                                                                        Я понял, вы хотите разбросать создание всех юзеров по всем ролям и таскам в плейбуке. Нет, с ансиблом это не пройдет. Вам нужно понимать каких именно юзеров вы хотите создать и например в конце плейбука использовать уже purge. Для этого нужно спланировать нормально плейбук, само по себе не будет работать как в паппете/терраформе.
                                                                        Тогда я не понял вас, каким образом вы предлагаете обеспечить декларативность?
                                                                        Я пытаюсь понять какое состояние вы хотите достичь этой конструкцией:

                                                                        tasks:
                                                                        — file:
                                                                        path: /tmp/test
                                                                        state: absent
                                                                        — file:
                                                                        path: /tmp/test
                                                                        state: directory

                                                                        Зачем вы удаляете и создаете ту же директорию?
                                                                          0
                                                                          Зачем вы удаляете и создаете ту же директорию?

                                                                          это просто демонстрация того, что ансибл — это не декларативный инструмент. Это инструмент, который последовательно выполняет таски. Баш на стероидах, ч.т.д. Для использования в качестве SCM не годится, но если он хочется, то можно.

                                                                            0
                                                                            Для использования в качестве SCM не годится
                                                                            Вообще вполне даже годится. Ставишь Tower/AWX и назначаешь регулярные запуски плейбуков конфигурации. Все ручные изменения будут перезатерты в следующий запуск.
                                                                              0

                                                                              зачем, если salt/puppet/chef это все умеют из коробки?
                                                                              Сколько нужно обмазывать ансибл, чтобы им можно было начать пользоваться?
                                                                              Ну, я уж не говорю о том, что по возможности нужно съезжать на IaC + immutable infra

                                                                                0
                                                                                Что значит «из коробки»? Паппет не нужно устанавливать, он с неба спускается? Не надо ничего ничем обмазывать, Tower это как тот же паппет мастер.
                                                                                Я только за IaC + immutable infra, но не вижу тут никаких противоречий.
                                                                                  +1

                                                                                  Перефразирую — ансибл не нужно устанавливать, он с неба спускается? Не нужно, чтобы целевая система имела тот же python-интерпретатор правильной версии?


                                                                                  Что значит «из коробки»?

                                                                                  то и значит. Посмотрите архитектуру указанных систем, задачи, которые они решают, и как они эти задачи решают.


                                                                                  Я только за IaC + immutable infra, но не вижу тут никаких противоречий.

                                                                                  Tower/AWX к этому никакого отношения не имеет. Он просто запускалка плейбуков. Не более.

                                                                                    –1
                                                                                    Перефразирую
                                                                                    Не надо перефразировать, это было ваше утверждение про «из коробки». Нет ничего из коробки.

                                                                                    Он просто запускалка плейбуков.
                                                                                    Паппет это всего лишь запускалка руби скриптов, ансибл всего лишь… Любой продукт можно свести к «всего лишь», но зачем?
                                                                            +2
                                                                            Нет, с ансиблом это не пройдет

                                                                            Ну это печально что тут сказать. Из всех ролей вытаскивать task'и определенного модуля, чтобы реализовать хоть какой-то purge — сильно неудобное в поддержке решение и оно совсем не будет работать как в puppet. Так как puppet просто применяет absent ко всем ресурсам которые не описаны, ansible так не может сделать принципиально.


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


                                                                            Либо давайте пример такой task'и purge. Я вангую там будет очередной костыль в виде shell скрипта или в лучшем случае отдельный модуль, который удаляет ресурсы, отсутствующие в контексте play, но эти решения оба плохи, так как такие ресурсы должны управляться родным модулем и удалятся его средствами через absent.


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


                                                                            Поэтому "можно реализовать" — это лукавство. Можно, но с кучей ограничений, таких что пользоваться таким решением просто невозможно. Причина этих ограничений: другая архитектура, слабая декларативность, императивная природа инструмента. Это принципиальные отличия.


                                                                            Зачем вы удаляете и создаете ту же директорию?

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


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


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

                                                        0
                                                        Один и тот же ресурс не может иметь два состояния одновременно. Если же может, то это не декларативность.

                                                        Это просто разная декларативность. Декларация полного конечного состояния системы после полного применения единого по сути конфига и декларация состояния системы после применения одного плэя.

                                                          0
                                                          декларация состояния системы после применения одного плэя.

                                                          Нет. Это не декларация. Как минимум потому что ансибл не оперирует термином «ресурс», а именно, что модули. И вы полностью (как в обычном программировании) полагаетесь на… side-effects. Немного спасает, что бОльшая часть модулей написана правильно и умеет в идемпотентность, но в первую очередь — это желаемое свойство самого плейбука. Которое достигается тем, что он будет особым способом написан.
                                                          Был уже пример про


                                                          - file:
                                                          path: /tmp/test
                                                          state: absent
                                                          - file:
                                                          path: /tmp/test
                                                          state: directory
                                                            0
                                                            декларация состояния системы после применения одного плэя

                                                            Не одного play, а одного task по идее, в одном play нет декларативности, поскольку там несколько task'ов могут описать один и тот же объект с разными состояниями. Если бы была декларативность на уровне одного play, то это уже было бы неплохо.


                                                            На мой взгляд декларативность не может применяться к одному объекту с одним действием/описанием над ним. Например:


                                                            a = 10

                                                            Конечно такая конструкция всегда декларативна, в любом языке, пока она не превратится в такую:


                                                            a = 10
                                                            ...
                                                            ...
                                                            a = 12

                                                            Декларативность это как раз про декларацию полного состояния, как это делает terraform например.

                                                    0
                                                    и так во всём — от списка репозиториев в sources.lis
                                                    Последний раз когда я конфигурил RPM репозитории на центосе паппетом, никакие другие репозитории он не удалял. Чем довольно таки раздражал, ведь заявлена была «декларативность».
                                                      0
                                                      никакие другие репозитории он не удалял

                                                      а должен?

                                                        +1

                                                        Потому что вы не сказали ему это сделать.


                                                        Файлы довольно уникальный тип ресурса для puppet. Так как файлов в системе очень много, и не очень эффективно их загонять все в ресурсы и знать обо всех, поэтому puppet с ними работает не много иначе. Но тем не менее, задача с source.list решается так:


                                                        Если используется /etc/apt/sources.list.d:


                                                        file {'/etc/apt/sources.list.d':
                                                          ensure  => directory,
                                                          recurse => true,
                                                          purge   => true,
                                                        }

                                                        Если используется модуль apt. То нужно классу apt об этом сказать:


                                                        class {'apt':
                                                            purge => {
                                                                'sources.list'   => true,
                                                                'sources.list.d' => true,
                                                                'preferences'    => true,
                                                                'preferences.d'  => true,
                                                                'apt.conf.d'     => true,
                                                            }
                                                        }

                                                        На самом деле он внутри это делает также передавая свойство purge в ресурсе файл. С конфигами nginx решается аналогично.

                                                        0

                                                        Коллеги, добавил опрос. Прошу проголосовать.

                                                          0

                                                          мне кажется, «нет» нужно поделить на «нет, оно не нужно» и «нет, но хотелось бы иметь такую возможность»

                                                            0

                                                            Добавил. Но сбросить результаты опроса похоже не получится.

                                                              0

                                                              и переголосовать нельзя )

                                                          0

                                                          identw
                                                          dominigato
                                                          chemtech
                                                          еще один вариант того, как могла бы выглядеть "идеальная" система SCM
                                                          https://github.com/asteris-llc/converge

                                                            0
                                                            И чем он отличается от терраформа?
                                                              0

                                                              тем, что это не терраформ? У терраформа вполне конкретная задача — управление инфраструктурными объектами, а не настройка конкретных серверов...

                                                                0
                                                                Я не знаю какая у него конкретная задача, но управлять серверами он тоже умеет. Тот же докер контейнер модуль из доков есть и в терраформе, например.
                                                                HCL language (not parseable), golang, похожая структура… Все это очень напоминает подражание терраформу.
                                                                  0

                                                                  terraform всё таки позиционируется на провижионе инфраструктуры, а не управлении софтом на серверах. О чём честно пишут в доках:


                                                                  Terraform is not a configuration management tool

                                                                  контекст:


                                                                  Configuration management tools install and manage software on a machine that already exists. Terraform is not a configuration management tool, and it allows existing tooling to focus on their strengths: bootstrapping and initializing resources.


                                                                  Terraform focuses on the higher-level abstraction of the datacenter and associated services, while allowing you to use configuration management tools on individual systems. It also aims to bring the same benefits of codification of your system configuration to infrastructure management.


                                                                  If you are using traditional configuration management within your compute instances, you can use Terraform to configure bootstrapping software like cloud-init to activate your configuration management software on first system boot.


                                                                  И советуют совмещать эти инструменты, если нужно.

                                                          Only users with full accounts can post comments. Log in, please.