Привет, Хабр! Меня зовут Игорь Гербылев, я технический директор в компании Just AI. В этой статье я расскажу о методологии структурирования ansible плейбуков, которую мы называем «Компонентный ансибл». Этот подход позволил нам упростить разработку и поддержку большого объёма ансибл-кода, который мы используем для настройки инфраструктуры и развёртывания наших SaaS продуктов.
Продукты состоят из множества микросервисов, имеют множество инфраструктурных зависимостей. Дополнительную сложность составляет необходимость установки системы на множестве окружений в дата-центрах заказчиков. Разумеется, в каждом окружении есть свои особенности конфигурации, иногда отличается состав устанавливаемых компонентов (микросервисов).
Для того, чтобы поменьше путаться в этом богатстве конфигураций, мы применяем несколько особенных решений. Одно из них — это введение понятия «компонента конфигурации» и методология для работы с этими компонентами. Подробнее об этом подходе, его правилах и преимуществах и пойдёт речь в статье. Также я расскажу о некоторых best-practices, которые можно использовать при любом подходе к IaC разработке.
Зоопарк поддерживаемых программ
Для начала хотелось бы рассказать о зоопарке программ, которые мы поддерживаем:
Приложение на микросервисной архитектуре и несколько десятков микросервисов — большая часть на Java, некоторые на Python и пара экзотических;
Несколько баз данных и брокеров сообщений: postgres, mongodb, clickhouse, redis, kafka;
Несколько облачных боевых окружений. Активно используются (и обновляются) 4, но общее количество превышает 10;
В боевых установках все компоненты резервируются по два инстанса, некоторые компоненты отмасштабированы и запускаются по 5-10 экземпляров;
Бессчетное количество девелоперских, тестовых, интеграционных окружений. Их количество уже подбирается к 100;
Несколько различных дата-центров в разных регионах со своей спецификой.
Проблемы автоматизации и способы решения
Обслуживая достаточно развесистую систему мы применяем множество автоматизаций: ansible, terraform, docker — из основных, а теперь, конечно, ещё и kubernetes. Но тем не менее регулярно возникают одни и те же проблемы.
Основная звучит так: в некоторых случаях подход IaC не уменьшает суммарные трудозатраты на управление инфраструктурой, а делает этот процесс даже более сложным, чем при работе «вручную». Конкретные проявления были таковы:
Установка некоторых компонентов вручную, например, БД всё ещё происходит быстрее, чем с помощью плейбуков (потому что не доделаны);
Плейбуки некоторых компонентов правятся при каждом использовании (потому что не отлажены);
Каждому инженеру не нравится код соседа (потому что он не работает с первого раза);
Инженеры тратят очень много времени на изучение скриптов своих коллег (потому что нельзя просто взять и запустить, приходится подправлять);
Конфигурация регулярно разъезжается, и не всегда та, что в коде соответствует той, что на серверах.
Думаю, каждый инженер может дополнить этот список. Давайте обозначим причины:
Отсутствие или размытость стандартов написания инфраструктурного кода. Их наличие должно улучшить читаемость кода, упростить коммуникации между инженерами, и определить способы решения задач;
Качество инфраструктурного кода;
Cоответствие конфигурации реальной и закодированной.
Собственно, борьбой с этими первопричинами и обосновывается появление методики, в составе которой мы:
Разработали стандарт написания плейбуков;
Ввели чек-лист для «приёмки» новых плейбуков;
Ввели процедуры регулярной проверки.
Ключевым элементом подхода является понятие компонента. Компонент — это отдельный, атомарный элемент системы, который может быть установлен самостоятельно, отдельно от других компонентов.
Его введение является основополагающим, потому что деление по компонентам задаёт способы структурирования кода, его тестирования и описания конфигурации системы. Далее приводится набор правил и рекомендаций, цель которых — придать конфигурационным компонентам более строгие очертания и рассказать о том, как с ними работать эффективно.
Базовые принципы компонентного подхода
Компонентный подход мы создали для приведения ансибл-кода в полное соответствие перечисленным ниже правилам. Стоит отметить, что эти правила будут иметь смысл в любой методологии, но для нашего подхода они строго обязательны.
1. Никакой ручной конфигурации — всё через гит и через ансибл
Очевидный пункт, но не указать его нельзя. В нашем подходе мы добавляем ещё одно ограничение: любая модификация в инфраструктуре должна применяться командой вида:
ansible-playbook -i <inventory> <playbook> [-l ..] [-t ..]
То есть применением какого-то плейбука к конкретному инвентарю, возможно, с ограничениями по требуемым действиям для ускорения процесса. Но без каких-либо других дополнительных хитростей, вроде определения переменных в командной строке.
2. Система конфигурируется через инвентарь
Операционный инженер при совершении рутинных действий должен править только инвентарь, но никак не плейбуки/роли. Инвентарь — это декларативное описание конфигурации системы, а плейбук — инструмент для её применения. Плейбук, как инструмент, может быть написан, отлажен, задокументирован, обкатан и переиспользован на множестве окружений (инвентарей). Инвентарь сам является документацией к той системе, которую описывает.
3. Инвентарь должен иметь типовую структуру
Нам приходится оперировать множеством окружений, часть из которых примерно одинаковые (например, тестовые стенды), а некоторые отличаются довольно сильно (в частности, on-prem установки). И чтобы в них не запутаться и легко переключаться между инвентарями, хочется иметь типовую структуру инвентарей и одинаковое наименование хост-групп. Следуя нашей концепции, хост-группа становится отражением понятия типа компонента.
Имена хост-групп в инвентаре должны иметь строго заданный смысл, смысл этот приравнивается к понятию типа компонента. Для закрепления соответствия компонент-хостгруппа компоненты у нас представляются плейбуками, а не ролями, именно потому, что плейбук имеет привязку к хостгруппе, а роль — нет. Если нам требуется использовать стороннюю роль, то мы пишем плейбук-компонент, придумываем для него имя, это же имя указываем в hosts внутри этого плейбука, внутри пишем инклюд требуемой роли.
Про компоненты
В этом разделе описываются правила написания плейбуков, которые позволяют превратить плейбук в компонент. По сути, все эти правила относятся к структурированию кода, именованию и разделению ответственностей.
Принцип единственной ответственности
Один плейбук = один компонент = один элемент конфигурации.
Например: установка постгреса — один компонент, создание БД — другой, настройка бэкапов — третий.
Плейбук должен называться в соответствии с задачей, которую он выполняет
Каким бы удивительным ни был этот факт, но с файлами гораздо удобнее работать, когда они называются максимально точно и лежат в папках с говорящими названиями. Поэтому над системой именования нужно работать целенаправленно, согласовывать именования с командой, не оставлять файлов «назову_как_нибудь_другого_не_придумал». Если такие появляются, то хотя бы обсудите это с командой и донесите смысл этого названия до других. Отчасти этим требованием по именованию мотивируется предыдущий пункт про единственность ответственности — чем меньше людей делает компонент, тем проще для него подобрать название.
Один компонент = один хост
Один компонент = один хост. Или так: один компонент всегда должен иметь свой уникальный inventory_hostname, но при этом он может быть установлен на один физический хост вместе с другими компонентами. Достигается это путём указания параметра ansible_host при описании хоста-компонента.
Типовой фрагмент инвентаря, устанавливающий экземпляр какого-либо компонента куда-нибудь, выглядит так: <hostgroup(component_name)>:
hosts:
<inventory_hostname(component_id)>:
ansible_host: <адрес физического хоста, на котором расположен компонент>
Другими словами: хостгруппа — это тип компонента, хост (inventory_hostname) — экземпляр компонента, id компонента. ID компонента должен быть уникальным в пределах инвентаря, при этом много компонентов может быть установлено на одном физическом сервере, это реализуется посредством указания ansible_host.
Про именование папок на диске и именование любых других ресурсов
Т.к. иногда нам приходится устанавливать на сервер по несколько компонентов одного типа, нам важно, чтобы папки нескольких инстансов одного и того же компонента не пересекались. Для именования папок рационально использовать component_id, он же inventory_hostname - т.к. это всегда уникальное имя в пределах одного окружения.
Стандартный шаблон имени папки из компонента deploy_compose выглядит так: {{component_name}}__{{component_id}}
Это предложение скорее рекомендация и относится, по большей части, к продуктовым микросервисам. Для инфраструктурных компонентов, например, postgres, mongo, prometheus и им подобных, нет прямой необходимости менять стандартные пути установки, особенно, если это требует существенных усилий.
О недопущении именования двух компонентов одинаковыми именами
Нужно внимательно относиться к уникальности компонент-хостнеймов, т.к. нарушение этого правила может вызвать трудно диагностируемые проблемы. Если в инвентаре появятся два хоста с одинаковым именем (в разных хост-группах), то их переменные смешаются и перетрут друг друга.
О раскладывании компонентов по папкам
Из принципа единственности ответственности вытекает то, что плейбуков у нас будет очень много. Чтобы не запутаться, надо структурировать, раскладывать их по папкам. У нас они такие: /cmp/app/<product-name>, /cmp/db/<engine_name>, /cmp/infra/certs и т.п.
Получается, что обычные для ансибла соглашения о стандартных путях к плейбукам/файлам/темплейтам использовать не получится, поэтому мы сделаем свои соглашения и будем указывать пути всегда абсолютные, строя их от {{root_dir}}. root_dir — «магической переменной», указывающей на корень гит-репозитория того инвентаря, который мы сейчас обрабатываем. У нас она объявляется так:
root_dir: "{{ lookup('pipe', 'git -C ' + (ansible_inventory_sources[0] | dirname) + ' rev-parse --show-toplevel') }}"
Про cmp-resources
Для удобства навигации по списку компонентов, файлы плейбуков и файлы ресурсов разделены. Разделены они примерно как java-исходники и файлы ресурсов в maven-проектах. Плейбуки у нас лежат в папке cmp, а файлы ресурсов в папке cmp-resources. Если компонент использует какие-либо шаблоны или другие файлы-ресурсы, то они должны быть помещены в cmp-resources по пути, повторяющему путь к плейбуку в папке cmp. Т.е. в cmp-resources у нас появляется иерархия папок параллельная папкам в cmp.
Как выполнять действия, затрагивающие множество компонентов
Для этого у нас есть так называемые корневые плейбуки — обычно это длинный список инструкций import_playbook, в котором присутствуют все компоненты для конкретной задачи. Примером такого плейбука у нас является jaicp.yml. В сочетании с соответствующим инвентарём он выполнит полную установку нашего продукта JAICP, который состоит из нескольких баз данных и множества микросервисов.
Корневой плейбук отличается от компонентов тем, что он не инклюдится куда-либо еще, а также находится либо в корневой папке репозитория, либо в папке plays/.
Ограничение выполнения
Для лимитирования выполнения, т.е. для выполнения определённых действий и только на определенных компонентах делаем следующее:
Катим корневой плейбук;
Ограничение по хостам задает список компонентов, которые хотим затронуть. Не надо забывать о возможности использования звёздочек и запятых. Например: -l bs01 -l bs* -l ed*,ba*. В сочетании со строгими правилами именования хостов-компонентов получается очень удобным;
Ограничение по тэгам задает действие, которое мы собираемся выполнить. Например: только срендерить конфигурации, или только перезапустить компонент, или остановить\ удалить компонент. Например: -t render, -t push, -t restart.
Чек-лист разработки компонента
В этом разделе приводится список пунктов, которым должен удовлетворять качественно сделанный компонент-плейбук. При полноценном внедрении компонентного подхода (и наличии ресурсов для этого), этот список можно использовать как чек-лист для внутренней приемки вновь разрабатываемых плейбуков, например, на этапе код-ревью.
Чек-мод
Чекмод важен, а идемпотентность — обязательное свойства хорошего плейбука. Любой разрабатываемый компонент-плейбук обязан:
При повторной прокатке показывать changes=0;
При изменении конфигурации в инвентаре и последующей прокатке с -D показывать диф по конфигурационным файлам, которые он меняет;
В некоторых случаях, например, при установке кластера базы данных, чек-мод может быть реализован несколько иначе, чем непосредственно накатка сервиса. Например, мы можем подключиться к кластеру и проверить количество узлов в кластере, и основываясь на этом сделать вывод, что реальная конфигурация соответствует требуемой. Аналогично при создании баз данных: создание выполняется командой create_db, а в check mode мы пропускаем create_db, но пытаемся залогиниться в созданную БД с указанными кредами.
Документация
На каждый плейбук-компонент должна быть страничка документации (или секция комментариев), объясняющая, что этот компонент делает, зачем нужен, как работает, а также должны быть перечислены примеры применения.
Чек-лист документации:
Общее описание — что за компонент, зачем нужен;
Список конфигурационных переменных, которые может определять пользователь;
Приведённый фрагмент инвентаря, показывающий типовой вариант использования;
Перечисление тегов, если они есть и их назначение.
Все это нужно записывать документацию в шапке, в начале плейбука. Для корневых плейбуков должно быть указано, к каким инвентарям их можно применять и как лимитировать выполнение.
Дефолтные переменные
Список переменных, доступных для переопределения пользователем, определяется в начале плейбука в секции vars в следующем виде: _componentName_varName: componentName_varName | default(def), при том, что далее в тасках и темплейтах используются переменные с префиксом _. Переменная с префиксом _ трактуется как приватная, внутренняя переменная. Без префикса — глобальная, внешняя.
Дефолтные значения можно (но не рекомендуется) указывать в месте использования, но при условии, что:
а) Переменная используется только в одном месте;
б) Список переменных, доступных для переопределения пользователем, описан в документации.
Универсальность в плане конфигурации, адаптация при установке
Компоненты должны вставать на все поддерживаемые ОС, со всеми типами развертывания. Соответственно, чем меньше у вас ОС и способов развертывания, тем проще будет ваш IaC. По возможности, размер зоопарка надо уменьшать.
Регулярные проверки
Чтобы быть уверенными в том, что конфигурация на наших серверах, управляемых через ansible, действительно соответствует тому, что закодировано в плейбуках и инвентарях и залито в гитхаб, мы регулярно выполняем прокатку плейбуков с опциями -CD.
Делать это стоит не только при обновлении плейбуков или инвентарей, а по расписанию. И крайне желательно сделать так, чтобы отсутствие зелёного прогона check-mode являлось блокером для выполнения автоматических модификаций на серверах. Если это правило ничего не блокирует, то его легко проигнорировать.
Регулярные проверки 2
Для того, чтобы быть полностью уверенными в работоспособности своих инструментов, недостаточно выполнять только чек-мод. Крайне желательно построить CI-стенды, на которых на регулярной основе будет выполняется прокатка основных конфигураций, начинающаяся с полного форматирования или пересоздания хоста. Такие проверки также стоит проводить по расписанию, потому что иногда они ломаются. Не от изменений в вашем коде, а из-за того, например, что во внешнем мире вышла новая версия какого-нибудь пакета, а у вас версия не была зафиксирована. Или наоборот, какой-нибудь старый пакет удалился из публичных репозиториев.
Заключение
Со временем проблемы, обозначенные в начале статьи, уже перестали быть для нас проблемами, со сложностями мы успешно справились. Сейчас мы все более активно используем kubernetes, и некоторые проблемы решает сам kubernetes и helm. Тем не менее, статья не теряет актуальности, потому что и сервера и кубер-кластер мы сетапим все также через terraform+ansible.
Эта версия статьи сокращена примерно на треть в угоду краткости. В частности, здесь я исключил секцию про инфраструктурные компоненты используемые для начального сетапа серверов, они у нас имеют свои особенности. Если вам будет интересно, то опубликую и остальной материал отдельной статьей.
Прошу вас поделиться мыслями о том, какие из приведенных правил кажутся вам полезными, какие сомнительными. Также буду благодарен за ссылки на другие существующие методологии для построения большого объёма кода на ансибл.
Комментарии (22)
Thomas_Hanniball
14.11.2023 16:18+1"Бессчетное количество девелоперских, тестовых, интеграционных окружений. Их количество уже подбирается к 100;"
По-моему, здесь что-то пошло не так. Некоторые проблемы можно решить только организационными, а не техническими мерами.
"Плейбуки некоторых компонентов правятся при каждом использовании (потому что не отлажены)"
Когда в компании появляются люди, которые умеют в автоматизацию, то они начинают автоматизировать текущий бардак, а не упрощать работу, поэтому в итоге получается не порядок, а автоматизированный бардак. Чтобы такой проблемы не возникало, надо осознать, что первый этап оптимизации - это стандартизация, т.е. обычный листок с ручкой или статья на wiki, где пошагово указано, что нужно сделать, чтобы получить нужный результат. Затем оттуда удаляются все ненужные шаги, без которых можно обойтись, т.е. происходит упрощение процесса. Далее по этой инструкции несколько разных инженеров пробуют реализовать задуманное, параллельно дополняя эту инструкцию своими комментариями и замечаниями. И вот когда 10 раз были выполнены работы с использованием этой инструкции и в неё перестали вносить правки, вот тогда можно браться за автоматизацию, т.е. перекладывать шаги из инструкции в Ansible и другие оркестраторы.
gersp Автор
14.11.2023 16:18+1Количество окружений у нас значительно уменьшить не получится. Как минимум есть окружения для некоторых заказчиков, выделенные отдельно по соображениям ИБ. Плюс каждой команде разработки нужен хотя бы один свой интеграционный стенд. Мы решаем эту сложность путём стандартизации конфигов и плейбуков и распределением ответственности по владению окружениями.
С этим утверждением сложно не согласится. Но можно. Во первых, если девопс-разработчик хорошо умеет писать скрипты автоматизации, то написать, а главное отладить скрипт становится гораздо проще, чем отладить инструкцию. Инструкцию надо выполнять руками, а скрипт можно прогнать автоматически за секунды или минуты на тестовом сервере. Во вторых, при автоматизации возникают дополнительные сложности. В основном связанные с тем, что скрипт не может применить "экспертное знание" системного администратора, если оно в него не закодировано. А инструкция какие-то "очевидные всем" шаги может пропускать. В третьих, инструкция будет устаревать со временем и на поддержку 100 инструкций вам ни хватит ни сил ни желания (мне точно не хватит). Так что моё мнение - можно и нужно сразу писать плейбуки и сразу выводить их на CI стенды.
Но, на этапе обучения работе с автоматизацией, инструкции конечно придётся делать.
Thomas_Hanniball
14.11.2023 16:18"Инвентарь должен иметь типовую структуру"
С учётом количества ваших окружений у вас получится километровый инветарь. Я, например, категорически против такого подхода. Каждый инженер должен создавать свой собственный инвентарь и указывать там только те серверы, с которыми сейчас будет работать. Никаких глобальный инвентарей и никаких километровых инвентарей с указанием всех серверов окружения. При таком подходе мне всё равно как выглядят инвентари моих коллег.Если будет использоваться глобальный инвентарь или километровый, то есть большой риск, что ранбук начнёт выполняться на всех серверах из инвентаря, а не только тех, что реально нужны для работы. А я не хочу раньше времени заиметь седые волосы или ночами напролёт заново поднимать сломанную инфраструктуру и думать, как мне вернуть потерянные данные.
"Никакой ручной конфигурации — всё через гит и через ансибл"
Надо исповедовать подход: Чем проще, тем лучше. Даже сами создатели Ansible его придерживаются, поэтому освоить Ansible достаточно просто. Есть пословица: Зачем что-то делать за 15 минут, если это можно автоматизировать за 6 часов. К сожалению, многие люди, которые в DevOps приходят из разработки совершенно забывают о том, что многие вещи в принципе не надо автоматизировать."Основная звучит так: в некоторых случаях подход IaC не уменьшает суммарные трудозатраты на управление инфраструктурой, а делает этот процесс даже более сложным, чем при работе «вручную»."
Ну хоть кто-то озвучил эту мысль, что подход IaC сильно добавляет сложности в работе и требует найма более дорогих инженеров. Полностью тут с вами согласен.gersp Автор
14.11.2023 16:18у вас получится километровый инветарь
У нас отдельные инвентарь на каждое окружение, у каждого окружения (и соответственно инвентаря) есть владелец. Про один общий инвентарь я вроде бы не писал.
Есть ещё пара простых трюков касаемо инвентарей: один инвентарь можно разбивать на отдельные файлы и инвентари можно как-бы наследовать друг от друга с помощью симлинков.При таком подходе мне всё равно как выглядят инвентари моих коллег.
А это вот деструктивное утверждение. А как же обмен опытом и взаимозаменяемость сотрудников? А если ваш коллега заболет на долго или уволится, кто будет разбирать его код, до которого никому не было дела?
Практики код-ревью и совместного владения кодом не просто так придумали. Они верны при любом подходе, если только речь не о личном пет-проекте.многие вещи в принципе не надо автоматизировать
Приведите пример?
IaC сильно добавляет сложности в работе и требует найма более дорогих инженеров
Я всё же адресовал это высказывание к ранней фазе внедрения IaC, когда ещё не все практики изучены и не все процессы налажены (а руки растут там где вырасли, а не там где надо). Сейчас мы не мыслим своей жизни без IaC и этот код не то чтобы экономит нам время, но делает возможным поддержание работоспособного состояния всех систем в принципе.
Thomas_Hanniball
14.11.2023 16:18+1"сервера и кубер-кластер мы сетапим все также через terraform+ansible"
Всегда хотел узнать, а зачем людям terraform, если у них есть ansible? У Ansible есть куча модулей от сообщества, благодаря чему его можно использовать для развёртывания инфраструктуры в облачных средах. Иными словами, Ansible можно использовать, чтобы создать инфраструктуру, а потом его же использовать, чтобы её настроить, т.е. нет необходимости использовать 2 разных инструмента (terraform+ansible). Возможно, для каких-то узкоспециализированных задач у Ansible не будет готовых моделей, но я уверен, что они в ближайшем будущем появятся, т.к. Ansible очень популярен.
gersp Автор
14.11.2023 16:18зачем людям terraform, если у них есть ansible?
Довольно интересный вопрос. Я тоже было придерживался такого мнения и пару раз писал управление инфраструктурой (читай как создание VPC в hetzner), а потом пытался применить похожий подход на других провайдерах. Но потом отказался от этой затеи.
Вместо долгих рассуждений - попробуйте создать вирутальный сервер в Selectel (указав кастомный базовый образ и подключив пару сетевых дисков) с помощью ансибл. Думаю вопросы быстро отпадут.
Более рациональный ответ - у терраформа другая модель выполнения, терраформ поддерживает некое внутреннее состояние и при каждом запуске вычисляет план действий, которые надо применить к инфраструктуре. Наличие такого планировщика 1. снимает часть сложности с разработчика, на ансибл вам придётся чекать изменения самостоятельно, 2. делает выполнение скрипта быстрее, потому что в результате построения правильного плана будут применены только те действия, которые действительно необходимы.
gersp Автор
14.11.2023 16:18Было бы интересно услышать мнение насчёт вопроса "зачем на ансибл, если есть террафом") Теоретически я слышал о том, что терраформ можно применять гораздо шире, чем только для создания серверов, но на практике мы не пробовали.
Thomas_Hanniball
14.11.2023 16:18Спасибо всем за комменты по теме terraform vs ansible. Тема интересная, а оба инструмента сильно похожи друг на друга. Значит, всё таки придётся мне учить ещё и terraform.
high_panurg
14.11.2023 16:18+1Самое главное удобство для меня terraform`a, в том, что он хранит tfstate с информацией о всех объектах, которые он создал из конфигурации и если этот объект пропадает из конфигурации - он его удаляет.
Соответственно гораздо проще удалять то, чего больше нет в конфиге.
NekoYos
14.11.2023 16:18+1Terraform - декларативный, ansible - императивный. Я бы наоборот везде использовал тф а не ансибл, потому что у тф-а есть стейт и он сам знает что нужно сделать. Простой пример: в тф'е описаны виртуалки ес2, он их создал. Когда нужно удалить виртуалку - просто удаляешь ее с конфигурации и тф знает что ее нужно пойти и удалить. А как анзибл узнает что нужно удалить то, что ты убрал с инвентаря?
gersp Автор
14.11.2023 16:18В ансибл можно удалять записи из инвентаря не сразу, а путём добавления аттрибута remove: true. Но это конечно не так удобно как при работе с terraform. И обработку этого аттрибута придётся писать самостоятельно
tommyfrozen
14.11.2023 16:18-1Установка некоторых компонентов вручную, например, БД всё ещё происходит быстрее, чем с помощью плейбуков (потому что не доделаны);
Вопрос зачем для этого использовать плейбуки, где скорее всего будет потеряна идемпотентность и скорость, если можно использовать классический bash-скрипт?
gersp Автор
14.11.2023 16:18Потому что подобный сетап баш-скриптом автоматизировать довольно сложно - https://github.com/vitabaks/postgresql_cluster.
Ради единства инструментов и стандартизации управления конфигурацией. Даже если у вас найдётся подходящий баш-скрипт, который ещё и обеспечивает идемпотентность и корректно показывает changes, то обернуть его в плейбук не составит проблем. И это вполне нормальный путь с точки зрения нашего Компонентного подхода. Потому что не важно, как плейбук выполняет свою работу, а важно чтобы он её выполнял качественно и с соблюдением принятых стандартов.
alexk24
14.11.2023 16:18Можете рассказать почему вы пришли к созданию "компонентов" в виде плейбуков, а не задействовали для этих целей имеющийся механизм "ролей"?
gersp Автор
14.11.2023 16:18Это самый важный вопрос) И весьма холиварный.
Началось всё с необходимости установить несколько однотипных компонентов на один хост. Т.е. как бы применить одну роль к одному хосту несколько раз. Чтобы сделать это с ansible придётся извернуться и мы придумали свой способ.
Мне хотелось создать абстракцию, которая сделает инвентарь самодостаточным. Для того, чтобы "пристегнуть" Роль к Инвентарю, вам понадобится написать плейбук, который, как минимум, определит привязку роли к имени хост-группы. А это означает, что читателю не достаточно прочитать инвентарь, а нужно ещё изучать что же делается в соответствующем плейбуке. А если мы поверх роли пишем плейбук, то со временем он будет разрастаться начнёт вбирать в себя всё больше вспомогательной логики.
Напротив: плейбук-компонент это атомарная единица конфигурации, которая прицепляется только лишь добавлением строчки в инвентарь (и в корневой плейбук, если это совсем новый компонент). На мой взгляд это удобнее для чтения и поддержки.Я смотрю на инвентарь, в первую очередь (как разработчик), со стороны архитектуры системы, т.е. программных компонентов, которые он описывает. Стандартный подход с ролями как бы говорит нам о том, что сначала есть Сервера, а потом они выполняют определённые Роли. Компонентный подход говорит о том, что сначала есть Система (окружение, инвентарь), она состоит из набора Компонентов, которые мы потом распределяем по Серверам.
alexk24
14.11.2023 16:18По тому коду что я вижу выше мне кажется у вас не совсем "декларативный подход" и не совсем "IaC". Ну и общее впечатление - "слишком усложнено"
я лично предпочитаю немного другой подход. более декларативно, и зачастую вся инфраструктура - одним плейбуком при самодостаточных ролях которые и выступают "компонентами".
gersp Автор
14.11.2023 16:18По тому коду что я вижу выше мне кажется у вас не совсем "декларативный подход" и не совсем "IaC". Ну и общее впечатление - "слишком усложнено"
Тут каждое утверждение требует пояснения. Что не декларативно, что не не IaC и как должно быть проще?
> зачастую вся инфраструктура - одним плейбуком
Здорово, если у вас так получается. Но рискну предположить, что система у вас на порядок проще. На моём опыте, подход с одним плейбуком изживает себя примерно на уровне 10 серверов и 20 программных компонент, которые требуют установки и настройки.
> я лично предпочитаю
Если эта фраза акцентирует внимание на индивидуальной работе, то это много объясняет. При отсутствии большого количества внутри- и меж-командных коммуникаций многие из этих "сложностей" действительно не нужны.
Пользуется ли вашим "единственным плейбуком" кто-нибудь ещё? Пользуются ли им разработчики того приложения, которое вы сетапите? У нас пользуются.alexk24
14.11.2023 16:18Не декларативно и не IaC - например https://github.com/vitabaks/postgresql_cluster/blob/master/
просто по структуре не предполагается что это будут использовать в "цельной" инфраструктуре.
да оно решает задачу "выкатить кластер". но не предполагает что этот код будет выкатываться на нее например на протяжении года.
И не предполагает что там кроме серверов посгреса будет что-то еще. Оно вероятно будет. но это будет уже другой набор плейбуков. в другой репе.
Насчет объемов системы - да в общем то нет. у нас типовая задача - развернуть что-то под заказчика. это "что-то" бывает как одним сервером так и сотней. причем с диким дрефом технологий вида - у нас там "mysql был, теперь нам посгрес нужен, и мемкешей. и ребит меняем на кафку."
Но у нас специфика несколько другая - мы работаем над чужими проектами.
Моя фраза "я предпочитаю" в первую очередь акцентирует внимание что я по умолчанию не экстраполирую своё мнение на всю команду и избегаю считать что оно единственно верное.
По поводу использования плейбуков разработчиками - обычно ими пользуется девопс-команда. влияние разработчиков на инфраструктуру обычно оборачивается в CI/CD.
Собственно мне и интересно как вы реализуете проект например из таких компонентов:
посгрес
кафка
эластик
мемкеш
php
десяток сервисов на golang
фронт на NUXT
балансировщик нагрузки
на три среды (prod, stage, test) с задачей сделать среды идентичными по настройкам (чтобы банально не окзалось "ой а на проде у нас по другому").
в нашем подходе это будет выглядеть как 1 репозиторий, 3 инвентаря + 1 плейбук + набор ролей. и возможность гонять ансибл через CI/CD для нивелирования дрейфа конфигурации.
как это будет выглядеть в вашей реализации?
gersp Автор
14.11.2023 16:18И надо оговориться, что Компонентый подход не противоречит использованию ролей. Роли можно использовать внутри Компонентов. Т.е. довольно типовой вариант, то что Компонент просто перевызывает внутри себя Роль с определёнными параметрами. По сути, определяя лишь Имя компонента, определяемое как имя хост-группы на которую он нацелен. На моей практике, такой подход можно применять если в сети уже есть готовая подходящая роль. Новые же скрипты, как мне кажется, проще сразу писать в Компонентной архитектуре.
ky0
К сожалению, некоторое ПО так не умеет (слава богу, в основном это разная второстепенная мелочь). Приходится городить всякие сигнальные файлы или вовсе использовать
changed_when: false
.gersp Автор
Даже если сторонние ансибл-модули не умеют сами корректно обрабатывать change'ы, мы почти всегда можем сделать это сами, расставляя правильные changed_when, а иногда и создавая более сложные конструкции. Например:
сначала запросить текущую конфигурацию
потом сравнить её с той конфигурацией, которую мы собираемся применить.
и если она отличается, то выполнить обновление
а если нет, то ничего не менять, чтобы не было changes.
В более интересных случаях, мы можем даже специальным образом дизайнить API сервисов так, чтобы сервис возвращал разные коды ответа в случае если изменения были применены или если дифа не было. Можно ещё создавать отдельные ветки в плейбуках для dry run режима.
В этом пункте правил, я хотел обратить внимание на то, что проверкой наличия изменений, диагностируемой по changes=0, можно пользоваться только если все используемые плейбуки и роли будут придерживаться этого правила. Достичь этого не так то просто, но можно. А польза и удобство от идемпотентности весьма большие.