А вот я хочу продолжить думать умное про ансибл, ci/cd и прочую рабочую фигню между гитом и сервером.
Классическое программирование стремится абстрагироваться от ввода-вывода. Идеалом программы (имплементации алгоритма) является полностью определённая функция, которая для заданных входных значений выдаёт определённые выходные. При этом такая программа работает в полностью определённой среде (мы знаем список доступных состояний машины Тьюринга, либо используем только хорошо нам известные функции, если работаем в функциональной модели).
Теперь посмотрим на "best practice для Ansible". Всё, что происходит - это сайд-эффекты. Некоторые из них предсказуемы, некоторые условно предсказуемы, некоторые не совсем предсказуемы. Часть сайд-эффектов имеют в качестве зависимости внешний мир (т.е. могут фейлиться по каким-то внешним причинам не связанным с нашим кодом).
В программировании существует модель описания этого, посредством интерейсов. Мол, у нас неконтролируемое "нечто" снаружи, консистентность внутри, и есть код, задача которого описать "нечто снаружи" в терминах, которые устраивают модель внутри. Если мир расходится с моделью, это это EnvironmentError, который мы можем пытаться описывать в терминах нашей модели (и может, даже, исправлять).
Но у такого подхода есть фатальная проблема: она не следует за реальностью. В момент разработки модели кто-то должен придумать модель, которая достаточно хорошо описывает реальность. Если количество "отклонений" станет слишком большим, то (пусть и консистентная) модель внутри будет неактуальной будет всё портить (или просто не работать).
И вот тут вот появляется парадигма, условно, админская. Девопс, ансибл, iaac, называйте как хотите. Это форма компьютерного экзестенциализима, которая принимает систему "as is", без чрезмерной строгости. Модель стремится к уменьшению и упрощению, и чем проще модель, тем меньше она отличается от реальности. От модели оставляют только то, что минимально необходимо для решения задачи. Интерпретация происходящего предельно близка к утиной типизации (если у нас получилось сделать то, что нужно, то это хорошо, а что это именно - уже не совсем важно).
В отличие от питона, в котором утиная типизация позволяет экономить на продумывании структур и типов, этот подход решает другую проблему: упрощение интерпретации внешнего мира и взаимодействия с ним.
Почему это упрощение неизбежно?
Потому что сисадминский код взаимодействует с необъятно сложными системами. Каждый второй модуль того же Ансибла, за собой тащит необъятную сложность. Вот, предположим, мы поднимаем виртуалку с постгрес и настраиваем в ней пользователя.
Сначала мы общаемся с пакетным менеджером. Модель пактного менеджера - это граф зависимостей, который разрешается в непротиворечивую комбинацию пакетов, либо оставляет систему в broken state. Взаимодействие с пакетным менеджером в общем случае должно предусматривать графы как структуры данных. Это очень много. Пакетный менеджер так же зависит от системы доверия ключам, которая в свою очередь зависит от даты, установленных ключей и чужих серверов. Ещё у нас могут быть transient проблемы: например, блокировка доступа к пакетному менеджеру из-за параллельного процесса (cloud-init, unattended updates, etc).
Потом мы должны создать образ виртуальной машины. Это в себя влючает тот же пакетный менеджер, но ещё нам надо понимать структуру дисков, взаимоотношения разделов, блочных устройств, mountpoint'ов, трансляцию оных их текущего представления в будущее представление внутри виртуальной машины.
Где-то тут же у нас есть модель файловой системы, поскольку образ будущей VM куда-то положен в файловой системе.
Потом мы погружаемся в мир XML для libvirt'а с одной стороны (надо правильно расставлять атрибуты и ноды внутри xml tree), и в мир устройства виртуальных машин с другой.
Виртуальные машины в свою очередь имеют несколько domain specific областей - концепцию "машины", внутри которой есть PCI-шина (ещё одно дерево с крайне запутанной и сложной логикой), сети (которая включает в себя целые пласты знаний про бриджи, раутинг, macvtap, pci passthrough, nat, etc).
Потом мы оказываемся в опять с пакетным менеджером.
Потом с нами здоровается мануал по postgres, где для правильного описания прав пользователя нужно знать какие базы данных, схемы, таблицы и т.д. нужно давать. И всё это весьма и весьма нетривиально.
Заметим, это всё находится поверх сетевой абстракции хоста, и транспортной абстракции ssh, через (очень хорошо работающий) слой абстрации по запуску питона через ssh внутри ansiballZ.
Это не сложно, это необъятно сложно. Любая попытка "сделать правильно" (т.е. формировать XML как осмысленное дерево, конструирование дерева PCI-устройств, поддержка полной модели прав пользователей postgres или графа зависимостей в apt) приведёт к одному или комбинации нескольких результатов:
* Непозводительно медленная разработка (десятки тысяч строк кода), закончившаяся ничем.
* Ошибки в описании предметной области, которые критичеки нарушают точность модели (не всё предусмотрено).
* Перегрузка получившегося кода излишними понятиями, которые не нужны для достижения результата.
Таким образом, "реализовать правильно" (т.е. реализовать честный интерфейс из предметной области в модель) мы не можем из-за слишком высокой сложности и разнообразности предметной области.
Какие альтернативы? Начать реализовывать правильно и не дореализовать (см список последствий выше).
Или... Не реализовывать вообще, а использовать компьютерный экзестенцианализм в чистом виде. Что нас, на самом деле, и приводит к best practices Ансибла:
* Список пакетов для установки с retries для защиты от локов.
* Хардкоженный список элементов для image builder'а.
* template из XML, в котором мы руками пишем львиную долю, заменяя минимальное количество элементов для интерполяции.
* Минимальный по сложности код заведения пользователя в postgres, возможно, не обладающий максимальной точностью по правам (если нет такой потребности).
Такой поверхностный подход позволяет сделать быстро и всё, что нужно. При этом он всегда страдает неполнотой и неожиданностями (теперь к transient ошибкам добавляются ошибки ожиданий, потому что мы делаем, например, не xml, а текстовый файл похожий на xml).
Что делать для повышения надёжности? Вместо уточнения и усложнения модели (что приведёт нас в трясину, описанную выше), мы просто стараемся покрыть тестами наши простые сценарии использования, зафиксировать зависимости.
Это всё работает не до конца, потому что IRL может оказываться отличным от наших ожиданий в момент тестов. В конце там ещё один комплект тестов, на этот раз инфраструктурных, которые проверяют, что сделанное одним образом соответствует ожиданиям, проверенным другим образом (поставили postgres apt'ом, проверили, что порт слушает).
Таким образом, философия сайд-эффектов кардинально отличается от философии программирования (как полу-математической дисциплины). Принятие этого факта позволяет избежать довольно распространённого феномена "программировать на ансибле", который бывает двух видов: принятие решений и попытка "понять" предметную область. Есть борьба с принятием решений в ансибле - это героический подвиг во имя улучшения сопровождения (всё время подвиги и не всегда успех), то отказ от "понимания" предметной области (на самом деле - трансляции предметной области в структуры данных), это философская максима, которую легко реализовать, для которой легко заметить отклонения, и в целом, реализуемая задача.