Как стать автором
Поиск
Написать публикацию
Обновить

Компонентный подход к Ansible или как навести порядок в инфраструктурном коде

Уровень сложностиСложный
Время на прочтение10 мин
Количество просмотров9.8K
Всего голосов 5: ↑4 и ↓1+3
Комментарии26

Комментарии 26

Любой разрабатываемый компонент-плейбук обязан:

  1. При повторной прокатке показывать changes=0;

К сожалению, некоторое ПО так не умеет (слава богу, в основном это разная второстепенная мелочь). Приходится городить всякие сигнальные файлы или вовсе использовать changed_when: false.

Даже если сторонние ансибл-модули не умеют сами корректно обрабатывать change'ы, мы почти всегда можем сделать это сами, расставляя правильные changed_when, а иногда и создавая более сложные конструкции. Например:

  1. сначала запросить текущую конфигурацию

  2. потом сравнить её с той конфигурацией, которую мы собираемся применить.

  3. и если она отличается, то выполнить обновление

  4. а если нет, то ничего не менять, чтобы не было changes.

В более интересных случаях, мы можем даже специальным образом дизайнить API сервисов так, чтобы сервис возвращал разные коды ответа в случае если изменения были применены или если дифа не было. Можно ещё создавать отдельные ветки в плейбуках для dry run режима.

В этом пункте правил, я хотел обратить внимание на то, что проверкой наличия изменений, диагностируемой по changes=0, можно пользоваться только если все используемые плейбуки и роли будут придерживаться этого правила. Достичь этого не так то просто, но можно. А польза и удобство от идемпотентности весьма большие.

"Бессчетное количество девелоперских, тестовых, интеграционных окружений. Их количество уже подбирается к 100;"

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

"Плейбуки некоторых компонентов правятся при каждом использовании (потому что не отлажены)"

Когда в компании появляются люди, которые умеют в автоматизацию, то они начинают автоматизировать текущий бардак, а не упрощать работу, поэтому в итоге получается не порядок, а автоматизированный бардак. Чтобы такой проблемы не возникало, надо осознать, что первый этап оптимизации - это стандартизация, т.е. обычный листок с ручкой или статья на wiki, где пошагово указано, что нужно сделать, чтобы получить нужный результат. Затем оттуда удаляются все ненужные шаги, без которых можно обойтись, т.е. происходит упрощение процесса. Далее по этой инструкции несколько разных инженеров пробуют реализовать задуманное, параллельно дополняя эту инструкцию своими комментариями и замечаниями. И вот когда 10 раз были выполнены работы с использованием этой инструкции и в неё перестали вносить правки, вот тогда можно браться за автоматизацию, т.е. перекладывать шаги из инструкции в Ansible и другие оркестраторы.

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

  2. С этим утверждением сложно не согласится. Но можно. Во первых, если девопс-разработчик хорошо умеет писать скрипты автоматизации, то написать, а главное отладить скрипт становится гораздо проще, чем отладить инструкцию. Инструкцию надо выполнять руками, а скрипт можно прогнать автоматически за секунды или минуты на тестовом сервере. Во вторых, при автоматизации возникают дополнительные сложности. В основном связанные с тем, что скрипт не может применить "экспертное знание" системного администратора, если оно в него не закодировано. А инструкция какие-то "очевидные всем" шаги может пропускать. В третьих, инструкция будет устаревать со временем и на поддержку 100 инструкций вам ни хватит ни сил ни желания (мне точно не хватит). Так что моё мнение - можно и нужно сразу писать плейбуки и сразу выводить их на CI стенды.
    Но, на этапе обучения работе с автоматизацией, инструкции конечно придётся делать.

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

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

"Никакой ручной конфигурации — всё через гит и через ансибл"
Надо исповедовать подход: Чем проще, тем лучше. Даже сами создатели Ansible его придерживаются, поэтому освоить Ansible достаточно просто. Есть пословица: Зачем что-то делать за 15 минут, если это можно автоматизировать за 6 часов. К сожалению, многие люди, которые в DevOps приходят из разработки совершенно забывают о том, что многие вещи в принципе не надо автоматизировать.

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

у вас получится километровый инветарь

У нас отдельные инвентарь на каждое окружение, у каждого окружения (и соответственно инвентаря) есть владелец. Про один общий инвентарь я вроде бы не писал.
Есть ещё пара простых трюков касаемо инвентарей: один инвентарь можно разбивать на отдельные файлы и инвентари можно как-бы наследовать друг от друга с помощью симлинков.

При таком подходе мне всё равно как выглядят инвентари моих коллег.

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

многие вещи в принципе не надо автоматизировать

Приведите пример?

IaC сильно добавляет сложности в работе и требует найма более дорогих инженеров

Я всё же адресовал это высказывание к ранней фазе внедрения IaC, когда ещё не все практики изучены и не все процессы налажены (а руки растут там где вырасли, а не там где надо). Сейчас мы не мыслим своей жизни без IaC и этот код не то чтобы экономит нам время, но делает возможным поддержание работоспособного состояния всех систем в принципе.

"сервера и кубер-кластер мы сетапим все также через terraform+ansible"

Всегда хотел узнать, а зачем людям terraform, если у них есть ansible? У Ansible есть куча модулей от сообщества, благодаря чему его можно использовать для развёртывания инфраструктуры в облачных средах. Иными словами, Ansible можно использовать, чтобы создать инфраструктуру, а потом его же использовать, чтобы её настроить, т.е. нет необходимости использовать 2 разных инструмента (terraform+ansible). Возможно, для каких-то узкоспециализированных задач у Ansible не будет готовых моделей, но я уверен, что они в ближайшем будущем появятся, т.к. Ansible очень популярен.

зачем людям terraform, если у них есть ansible?

Довольно интересный вопрос. Я тоже было придерживался такого мнения и пару раз писал управление инфраструктурой (читай как создание VPC в hetzner), а потом пытался применить похожий подход на других провайдерах. Но потом отказался от этой затеи.

Вместо долгих рассуждений - попробуйте создать вирутальный сервер в Selectel (указав кастомный базовый образ и подключив пару сетевых дисков) с помощью ансибл. Думаю вопросы быстро отпадут.

Более рациональный ответ - у терраформа другая модель выполнения, терраформ поддерживает некое внутреннее состояние и при каждом запуске вычисляет план действий, которые надо применить к инфраструктуре. Наличие такого планировщика 1. снимает часть сложности с разработчика, на ансибл вам придётся чекать изменения самостоятельно, 2. делает выполнение скрипта быстрее, потому что в результате построения правильного плана будут применены только те действия, которые действительно необходимы.

Было бы интересно услышать мнение насчёт вопроса "зачем на ансибл, если есть террафом") Теоретически я слышал о том, что терраформ можно применять гораздо шире, чем только для создания серверов, но на практике мы не пробовали.

Спасибо всем за комменты по теме terraform vs ansible. Тема интересная, а оба инструмента сильно похожи друг на друга. Значит, всё таки придётся мне учить ещё и terraform.

Спасибо за внимательные комментарии!

Самое главное удобство для меня terraform`a, в том, что он хранит tfstate с информацией о всех объектах, которые он создал из конфигурации и если этот объект пропадает из конфигурации - он его удаляет.
Соответственно гораздо проще удалять то, чего больше нет в конфиге.

Terraform - декларативный, ansible - императивный. Я бы наоборот везде использовал тф а не ансибл, потому что у тф-а есть стейт и он сам знает что нужно сделать. Простой пример: в тф'е описаны виртуалки ес2, он их создал. Когда нужно удалить виртуалку - просто удаляешь ее с конфигурации и тф знает что ее нужно пойти и удалить. А как анзибл узнает что нужно удалить то, что ты убрал с инвентаря?

В ансибл можно удалять записи из инвентаря не сразу, а путём добавления аттрибута remove: true. Но это конечно не так удобно как при работе с terraform. И обработку этого аттрибута придётся писать самостоятельно

  • Установка некоторых компонентов вручную, например, БД всё ещё происходит быстрее, чем с помощью плейбуков (потому что не доделаны);

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

  1. Потому что подобный сетап баш-скриптом автоматизировать довольно сложно - https://github.com/vitabaks/postgresql_cluster.

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

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

Это самый важный вопрос) И весьма холиварный.

  1. Началось всё с необходимости установить несколько однотипных компонентов на один хост. Т.е. как бы применить одну роль к одному хосту несколько раз. Чтобы сделать это с ansible придётся извернуться и мы придумали свой способ.

  2. Мне хотелось создать абстракцию, которая сделает инвентарь самодостаточным. Для того, чтобы "пристегнуть" Роль к Инвентарю, вам понадобится написать плейбук, который, как минимум, определит привязку роли к имени хост-группы. А это означает, что читателю не достаточно прочитать инвентарь, а нужно ещё изучать что же делается в соответствующем плейбуке. А если мы поверх роли пишем плейбук, то со временем он будет разрастаться начнёт вбирать в себя всё больше вспомогательной логики.
    Напротив: плейбук-компонент это атомарная единица конфигурации, которая прицепляется только лишь добавлением строчки в инвентарь (и в корневой плейбук, если это совсем новый компонент). На мой взгляд это удобнее для чтения и поддержки.

  3. Я смотрю на инвентарь, в первую очередь (как разработчик), со стороны архитектуры системы, т.е. программных компонентов, которые он описывает. Стандартный подход с ролями как бы говорит нам о том, что сначала есть Сервера, а потом они выполняют определённые Роли. Компонентный подход говорит о том, что сначала есть Система (окружение, инвентарь), она состоит из набора Компонентов, которые мы потом распределяем по Серверам.

По тому коду что я вижу выше мне кажется у вас не совсем "декларативный подход" и не совсем "IaC". Ну и общее впечатление - "слишком усложнено"

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

По тому коду что я вижу выше мне кажется у вас не совсем "декларативный подход" и не совсем "IaC". Ну и общее впечатление - "слишком усложнено"

Тут каждое утверждение требует пояснения. Что не декларативно, что не не IaC и как должно быть проще?

> зачастую вся инфраструктура - одним плейбуком

Здорово, если у вас так получается. Но рискну предположить, что система у вас на порядок проще. На моём опыте, подход с одним плейбуком изживает себя примерно на уровне 10 серверов и 20 программных компонент, которые требуют установки и настройки.

> я лично предпочитаю

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

Пользуется ли вашим "единственным плейбуком" кто-нибудь ещё? Пользуются ли им разработчики того приложения, которое вы сетапите? У нас пользуются.

Не декларативно и не IaC - например https://github.com/vitabaks/postgresql_cluster/blob/master/

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

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

И не предполагает что там кроме серверов посгреса будет что-то еще. Оно вероятно будет. но это будет уже другой набор плейбуков. в другой репе.

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

Но у нас специфика несколько другая - мы работаем над чужими проектами.

Моя фраза "я предпочитаю" в первую очередь акцентирует внимание что я по умолчанию не экстраполирую своё мнение на всю команду и избегаю считать что оно единственно верное.

По поводу использования плейбуков разработчиками - обычно ими пользуется девопс-команда. влияние разработчиков на инфраструктуру обычно оборачивается в CI/CD.

Собственно мне и интересно как вы реализуете проект например из таких компонентов:

  1. посгрес

  2. кафка

  3. эластик

  4. мемкеш

  5. php

  6. десяток сервисов на golang

  7. фронт на NUXT

  8. балансировщик нагрузки

на три среды (prod, stage, test) с задачей сделать среды идентичными по настройкам (чтобы банально не окзалось "ой а на проде у нас по другому").

в нашем подходе это будет выглядеть как 1 репозиторий, 3 инвентаря + 1 плейбук + набор ролей. и возможность гонять ансибл через CI/CD для нивелирования дрейфа конфигурации.

как это будет выглядеть в вашей реализации?

https://github.com/vitabaks/postgresql_cluster - это не наш модуль) Этот пример я привёл в другом контексте. У нас скрипты деплоя постгреса выглядят немного по другому.

Специфика задач у нас чуть-чуть другая, но не принципиально. Возможно у нас более тесная интеграция с разработкой, в том смысле что разработка порой сама вносит правки, например, в балансировщик или пробует поставить новую версию базы, чтобы проверить работоспособность. Не говоря уже о правках конфигурации на тестовых стендах. Над таким задачами удобнее работать из консоли, а не через CI/CD. Но это утверждение субъективно и зависит уровня компетенции по девопс-скриптам.

Собственно мне и интересно как вы реализуете проект

  1. Один репозиторий, два vault-ключа - один общедоступный для test и stage, другой для prod, доступный только прод-админам. И два CI-раннера, один с доступом к прод-серверам, другой без.

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

  3. Соответственно два корневых плейбука plays/infra.yml, plays/app.yml. Оба они стоят под CI/CD и периодически прогоняются с -C, если обнаруживаются чейнджы, что в чат отправляется алерт. Оба этих плейбука можно запускать из CI/CD. Эти корневые плейбуки состоят только из набора import_playbook.

  4. Кроме "простых" корневых плейбуков, обеспечивающих раскатку, могут быть и дополнительные, предназначенные для типовых процедур обслуживания. Например: перенос дампа с прода на стейдж, промоут версий со стейджа на прод с расстановкой нужных тэгов в гите и т.п. Подобные действия можно реализовать и в виде CI-пайплайнов, но этот вопрос я считаю делом личных предпочтений. На практике у нас бывает и так и так.

  5. Рядом с корневыми плейбуками лежит папка cmp с плейбуками компонентов (если у нас много схожих проектов, то эта папка выносится в отдельный реп и подтягивается с помощью ansible-collection). Компоненты разделены по плейбукам в соответствии с правилами из статьи. Некоторые плейбуки, могут использовать говотовые роли, обеспечивая только их "привязку" к нашему типовому инвентарю и к общим правилам. Обычно, таких ролей не много и используются они только для особо сложных компонентов (kubespray например).

  6. Будет ли отдельный плейбук-компонент на каждый golang-сервис зависит от того, насколько они типовые. Если все сервисы +-одинаковы с точки зрения развёртывания и конфигурации - то у нас будет компонент GoLang-Приложение и много его инстансов. Если сервисы сильно разные, то будут отдельные компоненты типа Приложение1, Приложение2 и т.п.


Пример инвентарей

Спасибо.

По примеру +/- так же получается, кроме того что мы не вводим сущность "компонента" и ограничиваемся ролями.

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

Сдвиг парадигмы разработки, который предлагает компонентный подход - очень не большой. Можно его ещё раз сформулировать так: в стандартном подходе - у вас есть Роли, поверх которых вы пишите Плейбук(код)+Инвентарь(данные), а в нашем Компонентном - у нас есть библиотека Компонентов, поверх которых мы пишем только Инвентарь. Т.е. чуть меньше императивщины в ежедневных операциях.

Но мне кажется важным ещё один момент. В Компонентном подходе я "нанизываю" на понятие Компонента много дополнительных best practies: от нейминга и правил работы с переменными до CI/CD. Эти правила у меня плохо клеились в единую картину без понятия Компонента.

Буду признателен, если читатели покидают в комментарии ссылки аналогичные руководства-методики-бест-практисы по "стандартному" ансибл.

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

Зарегистрируйтесь на Хабре, чтобы оставить комментарий