Контейнеризация понятным языком: хранение данных и безопасность в Kubernetes, зачем нужен Ansible. Часть 2
2020-12-07 17:00
В чём проблема с базами данных и как позаботиться о безопасности в Kubernetes? Как врубиться в Ansible? Ответы на эти и другие вопросы читайте в продолжении интервью Лекса АйТиБороды со старшим инженером Southbridge Николаем Месропяном и СТО «Слёрма» Марселем Ибраевым.
В чём проблема с базами данных в Kubernetes? Для меня как для разработчика проблем нет: зафигачил базу в Docker-контейнер и работаешь.
Проблемы нет для половины, наверное, комьюнити. Это вообще холиварный вопрос. В чатике kubernetes_ru (официальном чате русскоязычного сообщества Kubernetes) постоянно эти вопросы поднимаются.
А может, Docker не нужен? Зачем мне Docker для базы?
И это тоже. Были даже такие разговоры, что если база данных удовлетворяет 12 факторам, то это обычное приложение, запускайте и всё, 2020 год на дворе. Зачем вы её выносите отдельно, туда-сюда.
Но «12 факторов» — это не совсем про базу данных, там больше про stateless говорится. У нас все-таки stateful приложение, то есть приложение, которое должно сохранять статус и результат работы. После перезапуска оно этот результат терять не должно, в отличие от stateless.
И вот именно здесь краеугольный камень. Чтобы научить Kubernetes сохранять статус приложения (базы данных, таблички — вот это всё), нужно присыпать ещё несколько абстракций сверху: PersistentVolume, PersistentVolumeClaim, StorageClass — не буду сильно углубляться. То есть просто нужно иметь в виду, что повышается планка работы с Kubernetes. Нужен человек, который умеет настраивать кластер этой базы данных. Потому что Kubernetes не важно, что запускается в поде (Pod): stateless приложение или stateful приложение — он с ними работает одинаково. Он не соберёт магическим образом кластер баз данных и не будет за ним следить. Вы напишете манифест с тремя инстансами базы данных, Kubernetes его запустит, и это будет три отдельных инстанса базы данных, которые будут не в кластере.
Но с приложениями он умеет это делать?
Тоже нет. По факту это как бы отдельные инстансы, отдельные реплики приложения. Они по идее самостоятельные.
То есть на уровне записи и чтения данных всё равно нужно будет абстракцию какую-то свою клепать, Kubernetes ни при чём?
Да, и поэтому здесь нужны компетенции какого-то DBA, который на монолитах это уже 1000 раз настраивал, и скажет: «Да, без проблем! Настроим!».
Следующий вопрос возникает с запуском базы данных, когда нам нужно выставить request/limit. Request/limit — это инструмент, который позволяет работать с ресурсами.
Request — это запрос на ресурсы. Например, мы знаем, что этому приложению минимально нужно 200 МБ ОЗУ. Если будет меньше, оно просто крашнется и не запустится. Мы выставляем request 200.
limit — максимальная планка. Мы говорим, что 512 максимум, а если выше, то оно явно потекло и надо с ним что-то делать.
А вот как это сделать для базы данных — хрен его поймёшь! В понедельник мы работаем на типовой нагрузке, в пятницу пришла баба Люда из бухгалтерии и запустила выгрузку из 1С миллиона строк, и нагрузка в потолок.
На моей истории было дважды, когда приложение (не база данных, для базы данных это будет суперкритично) начало утилизировать очень много ресурсов, и на сервере ресурсов не хватило — всё померло. Kubernetes умный, он всё понял: взял и перетащил это приложение на другой сервер, но потреблять много ресурсов оно не перестало. Новый сервер упал, и как домино всё сложилось. Может повезти, что это всё в цикл замкнётся и будет переезжать, но ничего хорошего всё равно не будет. А с базой данных это критично, потому что вам нужно сохранять свои данные.
Есть 4 способа, как это сделать:
очень стабильно, но дорого,
не очень производительно, но дёшево,
более или менее по производительности, но без гарантии,
и дёшево, и круто.
Очень стабильно, но дорого — это когда мы покупаем энтерпрайз, какую-нибудь железку с поддержкой. Там стоит такая бандура, SaaS всякие, и все работает. Здорово, но не у всех есть такие возможности.
Не очень производительно, но дёшево — это использование облачных сервисов, которые предоставляют диски. Но здесь нужно понимать, что у многих есть в инструкции (в том числе и в документации баз данных) такая сноска: «Ребята, если вы будете использовать какие-нибудь диски Google или Digital Ocean, например, то поставьте галочку “using local SSD”, чтобы использовать локальные диски, а не какие-то там через сеть, и вот тогда всё с базой данных будет OK».
Более-менее по производительности, но без гарантии (этот вариант нативно поддерживает Kubernetes) — это когда мы просто берём, грубо говоря, прокидываем диск сервера в контейнер. Мапим папочку прям. У нас на сервере могут быть SSD, и мы SSD внутрь контейнера и получаем. Минус в том, что мы тем самым прибиваем базу данных к этому серверу. Спрашивается, зачем мы запускаем её в Kubernetes, если что случись, она там и сдохнет. Ну и лежала бы на своём монолитовском сервере.
И дёшево, и круто — это собственная СХД, которую построил и поддерживает какой-то бородатый админ. При этом он ещё должен вывести уровень производительности этой СХД на такой уровень, чтобы она сравнялась как-то с SSD. Первое, что приходит в голову, — это Ceph. Но пока мы делали курс по Ceph, пообщались с теми, кто много с этой технологией работает, они сказали: «Не, чувак, там столько костылей, что это всё равно придётся закрывать мощностями оборудования». То есть такого нет.
Вот такие варианты: даже не 2 стула, а 4 — выбирай, куда садиться. Если у вас возникает вопрос: «Могу ли я запустить базу данных в Kubernetes?», значит, конкретно вы не можете пока.
В Kubernetes есть чарты (charts) и хельм (Helm), что это такое?
Сначала расскажу, как возникла потребность в их создании. Вот у нас есть кластер, всё хорошо, но мы его поднимали не просто так, а чтобы запустить приложение.
Приложение может состоять из множества микросервисов, которые надо как-то деплоить в кластер. Конечно, мы можем каждый раз править манифесты руками. Но одно приложение может содержать от пяти и до бесконечности манифестов.
Манифесты — это yaml-файлики, которые отвечают за самые разные абстракции. В том числе сам под (Pod), хотя это будет не под, а абстракция, которая называется либо Deployment (именно в ней запускают приложение), либо StatefulSet (если это stateful-приложение), либо DaemonSet, ну неважно. К нему добавляется абстракция типа Service — внутренний кубернетский балансировщик. К нему же добавится абстракция Ingress — внешний балансировщик. Там же ещё могут быть PV/PVC — короче, много всего.
Когда мы приложение разрабатываем, нам надо сделать какой-то релиз. Что-то поменялось в приложении, что-то поменяться могло в манифестах. Например, версия image была 2, стала 3. Нам придётся каждый раз ходить в репы и менять руками 2 на 3. Таких реп может быть сотни, и осталось только не забыть везде нужное поменять. В этот момент типичный админ задумывается об инструментах, которые позволяют менять все файлы внутри папки с одного паттерна на другой. И в принципе, это будет работать.
Только проблема в том, что это решение kubectl based, то есть всё равно весь вот этот вывод, который мы в папке перелопатили, мы через пайп отправляем в kubectl. Говорим: «kubectl, вот теперь всё, что у нас есть новое в папке, отправляй в кластер». И это тоже будет работать. Но если что-то пойдёт не так (циферку не ту поставили или приложение просто не поднялось), нативного способа откатиться через kubectl нет. Там есть команда kubectl rollout undo, но она работает только с deployment, а у нас могут быть и другие абстракции, с которыми она не работает. И тут возникает вопрос, а как откатываться? Потому что шаблонизация вроде более-менее понятна, да и шаблонизаторов много всяких (и Kustomize, и json.net), но как откатываться — большой вопрос.
Явный ответ на это дает Helm, потому что у него из коробки есть возможность cделать rollout, то есть откат, в случае, если какие-то есть проблемы. Работает он достаточно просто. Он все манифесты из папки, что мы нагенирировали, сохраняет в артефакт. Например, в config какой-то. Прям вот как оно есть, так и сохраняет подряд. Назвал его «артефакт версия 1», сохранил в кластер. Потом мы выпустили новый релиз. Он — ага, «артефакт версия 2». И вот так они копятся. Потом мы выкатили новый релиз, какой-нибудь версии 9, и поняли, что это не то. Он говорит: «Ок, у меня есть версия 8», достает все манифесты оттуда и фигачит в кластер.
Возвращаясь к вопросу, как вменять всё, когда этих манифестов становится за сотню и просто невозможно в голове удержать, что где поменять. Тогда на помощь приходят шаблонизаторы, темплейтирование и Helm это тоже умеет. Набор темплейтированных манифестов называется Chart.
Ты уже не раз говорил про stateful и stateless приложения. Разработчикам эти термины знакомы. И понятно, что со stateless просто: развернул и не паришься. Есть какие-то особенности разворачивания stateful приложений? И что для этого используется внутри Kubernetes? Какие-то может быть особенные штуки для хранения состояния приложения? Как оно хранится? Где оно хранится?
Сам по себе Kubernetes никакого решения не предоставляет. Нет внутренней системы хранения данных. В любом случае нужно использовать стороннее решение. Сейчас появился CSI Driver, и стало проще.
CSI Driver — это ещё один интерфейс, который позволяет унифицировать подключение внешних инструментов. Как есть Container Runtime Interface, который не только Docker может подключить, но всё, что CRI поддерживает, так и CSI может подключить всё, что его поддерживает. Технология не так давно вышла в прод, развивается и допиливается, но в целом это хороший шаг. Остаётся подключить, например, Ceph как систему хранения данных и через абстракции Kubernetes запрашивать диски или файловые системы в этой СХД и подключать себе. То есть по сути приложение будет работать с этой СХД, а Kubernetes будет в роли посредника.
А до появления этого механизма в Kubernetes нельзя было разворачивать stateful приложения?
Можно было. Есть такая штука, как storage class. Это ещё одна абстракция, и там это всё настраивается. Скажем так, объём тех решений, которые можно применять в Kubernetes, будет расти. Уже есть всякие облачные хранилища, которые можно подключать, и множество всего другого.
Как построен механизм безопасности всего процесса поднятия, слежения за твоими приложениями? Общение наверняка ведётся по https, но есть ли ещё что-нибудь? Что, например, будет, если API Kubernetes завладеет злоумышленник? Как этого избежать?
Не открывать API наружу! Достаточно просто.
А как не открывать? Выстраивать приватную сеть?
Ну да, VPN, двухфакторная авторизация — что-нибудь такое. API нужно закрывать очень хорошо, потому что каждый год находят уязвимости. В 2018 году, например, обнаружили критическую уязвимость с индексом 9,8, если я не путаю. Она позволяла поднять привилегии в API, и всё — делай, что хочешь. Были уязвимости в 2019 году. Мне запомнилась возможность подменять файлы на хосте через контейнер.
И всё это можно сделать, если авторизоваться в API. Естественно, API не дураки писали, и там есть аутентификация, тот же самый TLS, просто так ничего не сделаешь. Но уязвимости бывают, поэтому API надо всеми возможными способами закрывать.
Вопрос безопасности Kubernetes достаточно широкий, потому что на него можно взглянуть под разными углами и рассмотреть множество аспектов безопасности.
Например?
Глобально можно посмотреть в первую очередь как раз с точки зрения уязвимостей, получения прав и так далее. Решение — закрытие API. Если Kubernetes развёрнут не в облаках, а на своих виртуалках, то надо регулярно обновлять свои сервера. Про iptables и SSH наружу, я думаю, говорить не надо. Это общие и достаточно банальные требования, но их надо выполнять. Kubernetes не панацея, это дополнительный слой абстракции. Сверху намазали, и за этим теперь тоже надо как-то следить.
Второй аспект безопасности — это уровень привилегий пользователей. В Kubernetes это хорошо сделано. Есть так называемый Role Based Access Control или RBAC сокращенно. Мы можем разграничить пользователей по тому, какие действия им доступны: ты можешь работать только здесь, а ты — только здесь. Но там есть достаточно простые хаки, которые позволяют это всё очень легко обойти.
Например, в Kubernetes есть namespace — пространство имен. Условно, у нас есть Kubernetes, есть два namespace: стейджинг и продакшн. В стейджинг мы пускаем разработчиков, они там работают, но мы им не даем что-то править на проде. В прод ходит только CI/CD. Но просто имея возможность запускать свои приложения хоть где и ничего больше, я могу за 2 минуты стать админом кластера. Я запущу под (Pod), которого отправлю на запуск на master, где у нас находятся все доступы и креды, и подмонтирую их себе в контейнер. Потом я посмотрю токены, скопирую себе на компьютер и стану админом.
Это такая штука, о которой не все задумываются. Но эту штуку тоже уже прикрыли. Есть инструмент Pod security policy, который позволяет чётко сказать, какие поды и где и как могут запускаться, какие порты могут использовать.
Ещё один аспект безопасности касается работы нескольких команд в Kubernetes. Особенно, если есть на аутсорсе команды, которые пишут свою часть приложения. По умолчанию, любой человек с доступом в namespace может постучаться во все эндпоинты (endpoints), которые есть. Прикрыть это позволяет Network policies. Это своего рода firewall внутри кластера. Когда мы внешнюю команду к себе зовём, мы спрашиваем, к каким эндпоинтам она должна иметь доступ, чтобы взаимодействовать с приложением, и только эту часть открываем. В основной кластер они зайти не могут.
Если говорить про безопасность, то в Kubernetes есть ещё одна интересная штука — защита от человеческого фактора. Известно много громких факапов, произошедших по вине человека (история с GitLab, когда они положили пол своего продакшена, с Yandex.Cloud и другими компаниями). Прод базу данных снёс и оказалось, что в шести местах бэкапов нет или они неконсистентные, и случайным образом где-то там дампик лежал несколько часовой давности и типа: «Ух! (перекрестился) Слава Богу! Восстановили!»
Чтобы этого избежать, в Kubernetes тоже есть инструменты. Например, limit ranges, resource quotas. Они позволяют держать в узде тех, кто работает с Kubernetes. Например, не дают заскейлить свое приложение в какое-то очень большое количество. А такое один раз было. Коллега рассказывал: админ хотел поставить единицу. Клавиша залипла, интернет лагнул, вместо одного раза он нажал несколько — и всё. Правда, это было не на Kubernetes, а на Nomad. Но факт такой, что он: «Вам надо 111 млн приложений? Хорошо. Сейчас будет!» И всё начало лагать, естественно. Чтобы этого избежать, мы можем сказать: «Здесь максимум 10 реплик, и всё».
Ты упомянул Nomad. Какие ещё есть аналоги Kubernetes?
Самый яркий аналог, про который все говорят, — это Docker Swarm. Docker Swarm — это разработка от Docker, их нативное решение. Не требует никакой дополнительной установки: переходим в swarm-режим, и у нас появляется возможность Docker Swarm.
Docker Swarm — это тоже оркестратор, поэтому многие сравнивают его с Kubernetes. Но если говорить глобально, то Kubernetes — это более мощный комбайн, в нём больше возможностей. В Docker Swarm их чуть поменьше, но я бы сказал так: если у вас супертребований нет, то можно пользоваться и Docker Swarm. Но в отличие от него, Kubernetes, например, хорошо умеет автоскейлить. Причем, как ноды автоскейлить, заказывать, так и поды, то есть реплики приложений. В облаках это работает вообще шикарно. Пришла нагрузка с рекламной кампании — мы не сидим, не заказываем руками, а он сам сходил и заказал. Когда нагрузка ушла, он обратно их схлопнул до нужного количества.
На каких операционных системах обычно запускается Kubernetes?
Ну обычно на Linux: CentOS, Debian.
А на винде умеет?
Они делают сейчас движение в сторону Windows. В принципе, стало лучше, но я бы сказал, что не надо пока этого делать.
Какими навыками нужно обладать, чтобы успешно работать с Kubernetes? Программирование, может, какое-то нужно? YAML, понятно.
Знание YAML, некоторое понимание, как работает контейнеризация… Я не знаком лично, но слышал о таких людях, которые не имеют базового бэкграунда, но с Kubernetes работать умеют. Они могут налажать с сетью, например, но при этом с Kubernetes работают.
Я бы добавил, что нужны базовые знания по сетям, по Linux (если Kubernetes будет запускаться на Linux-машинах, их надо будет тюнить) и умение читать документацию.
В каких случаях Kubernetes вообще не стоит использовать?
Не стоит использовать, если рассчитываете, что поставите его, и все вопросы решатся. К внедрению надо подходить системно, и ставить Kubernetes только тогда, когда есть реальная необходимость.
Есть ещё ситуация «Kubernetes ради зарплаты». Это когда технический специалист зашёл на Хедхантер, посмотрел на зарплаты, и пошёл пропихивать это решение в компании, чтобы потом попросить повышение: «Я же теперь специалист по Kubernetes, платите мне больше». Это вряд ли к чему-то хорошему приведёт.
С технической точки зрения Kubernetes не нужно применять для приложений, которые не удовлетворяют «12 факторам». Для приложений, которые неправильно работают на уровне микросервисов, которые не допилены до конца. В одном из проектов мы пилили и переносили, пилили и переносили, и это было сложно, потому что часть проекта работала в Kubernetes, а часть продолжала работать на монолите и надо было выстраивать взаимосвязь.
В идеальном мире, конечно, Kubernetes ставится сразу же… Сначала приложение готовится, потом уже туда. Но чаще всего нет.
Переходя к следующему блоку, спрошу: в чём различие Kubernetes и Ansible?
Ansible — это, в первую очередь, система управления конфигурацией. Kubernetes — это оркестратор контейнеров. Знание Ansible не будет лишним, когда вы начнёте работать с Kubernetes, и наоборот. Потому что Kubernetes нужно как-то управлять, и Ansible для этого хорошо подходит. Если мы говорим, что Kubernetes — это Infrastructure as code (IaC), то вот этим IaC нужно как-то управлять. Руками туда лезть? В идеальном мире не лезут, всё прогоняют через пайплайны: надо что-то сделать в Kubernetes — запускаешь плейбук — он пошёл сам сделал. Поэтому знание Ansible и Kubernetes, наверное, важны в равной степени.
Далее отвечает Николай Месропян
Что такое Ansible и для чего он нужен?
Это средство управления конфигурациями. Для чего нужно? Когда у вас один сервер, им можно управлять руками: просто ходить на него, набирать команды или тыкать мышкой, что-то руками править, настраивать. Если их 2, 10 — это ещё реально. Но в конце концов серверов может стать и 20, и 100, и 1000. Вот тогда возникает необходимость управлять ими более автоматизированно и централизованно.
Мы говорим про управление конфигурациями операционной системы или вообще любыми? Я задеплоил 25 приложений разных на хост, и я хочу…
Обычно я начинаю рассказ про Ansible и свой курс с концепции, которая называются питомцы и стадо — «Pets vs Cattle». Pets — это питомцы. Ты за ними ухаживаешь, лечишь, даёшь им уникальные имена. Cattle — это стадо. Если кто-то заболевает, ты его забиваешь и создаешь вместо него новый сервер. У тебя их очень много, и ты их в лицо не знаешь, а знаешь только по номерам. Питомцы превращаются в стадо, когда их становится много.
Но когда они заболевать начинают!
Когда они заболевают, это жесть, я согласен. И чтобы с этим стадом управляться, создана система управления конфигурациями. Да, разумеется, она используется и для деплоев приложений, но начинаем мы обычно с системы управления конфигурациями, операционной системой на наливке каких-то приложений, конфигурирования их в том или ином порядке — оркестрацией как бы.
Jenkins вроде как тоже кроме того, что поддерживает пайплайны, умеет конфигурировать приложения. Это в чём-то похоже на Ansible?
Нет, это совершенно разные вещи. Ansible похож на Chef, Puppet, SaltStack. Jenkins больше похож на GitLab CI и TeamCity. Это совершенно разные классы программного обеспечения для разных целей, но они могут работать вместе.
Я встречался с решениями, когда через пайплайн на GitLab CI производился деплой приложений посредством приложения, использующего внутри себя Chef. Сам я писал пайплайны для GitLab CI на Ansible. То есть они вместе работать могут. Иногда и просто должны.
Из чего состоит Ansible, и как он вообще работает? Это какое-то приложение над приложениями или отдельный сервер, что это вообще?
Cначала расскажу про ПО для управления конфигурациями. Расскажу, чем они друг от друга отличаются, и потом перейдём конкретно к Ansible.
ПО для управления конфигурациями можно разделить на два основных класса: pull и push. Когда работает система принципа pull, на клиентских хостах, то есть на управляемых хостах, выполняется некий daemon (сервис), который подтягивает конфигурацию с центрального репозитория и на месте уже её раскатывает.
push — это когда управляющий процесс выполняется на одном центральном сервере, а на конечные ноды доезжают только конечные изменения (по сети). То есть конфигурирование производится не на месте, а по сети.
По сети — SSH, FTP?
Как правило, SSH. Вот Ansible относится именно к push. На центральном сервере выполняется процесс, который использует конфигурацию, чаще всего хранящуюся в репозитории.
Зачем вообще нужен pull, если push выглядит достаточно красиво и ненапряжно?
Push медленный.
В случае с pull процесс работает на клиентской ноде, стягивает конфигурацию в виде файликов, а потом уже на месте, через местные вызовы API очень быстро создаёт или поддерживает нужную конфигурацию.
При модели push центральный процесс каждый раз идёт к ноде по SSH, что-то там делает, возвращает результат, здесь он оценивается, и всё это повторяется.
Тогда чем push лучше pull?
На конечных нодах не хранится никакой конфигурации, что называется нулевой footprint (след).
Бывает важно, чтобы на клиентских машинах никто не мог понять, как и что конфигурируется. Расскажу про нашу компанию, Southbridge. У нас аутсорсинг, множество клиентов, и информация под клиентов хранится в репозитории. Потому что мы не хотим, чтобы клиент мог своими руками что-то подкрутить и сломать. Он может сделать это без злого умысла, просто: «Программист сказал, что можно сделать так, давай-ка сделаем!» А потом раз — продакшн снесся, и он приходит к нам: «У вас все плохо работает!»
В действительности таких случаев у нас не было, когда мы использовали pull-системы. Но тем не менее. К тому же лучше подстраховаться, чтобы конфигурация одних клиентов не попала каким-то другим. Именно поэтому мы используем push.
Плюс нам понравилась сама парадигма, что всё лежит в одном месте. Сами клиентские процессы иногда написаны на тяжёлых языках (Ruby, например) и тоже подъедают системные ресурсы: и память, и процессор. Поэтому это в каких-то случаях может быть нежелательным.
А клиентские ПО, клиентские ресурсы, которые в push работают, они пишутся руками или есть готовые какие-то решения?
Вот теперь я могу рассказать, из чего Ansible состоит (без слайдов и доски сложно, но попробую объяснить на пальцах).
Для Ansible мы пишем конфигурацию, состоящую из сущности под названием плейбуки (playbook), ролей и инвентаря. Плейбуки и роли — это определение конфигурации. Инвентарь — это параметризация.
Плейбук — это файл, состоящий из набора так называемых плеев (play). Сразу скажу, что вся конфигурация пишется на YAML. Ansible написан на Python, поэтому для него родной язык разметки YAML.
Ansible, если что, лучше всего ставить в virtualenv, чтобы быть независимым от мейнтейнера… А, да, начнём с того, где Ansible работает: управляющие хосты поддерживаются только Linux.
А если это виртуалка?
Всё равно. Всё-таки используются системные вызовы, и чтобы не адаптироваться, в качестве управляющего хоста поддерживается только Linux. Управлять можем Linux-системами, Windows-системами и Mac OS. Но для Mac, насколько я знаю, оно пока в зачаточном состоянии.
Android, мобильные платформы?
Наверняка что-то есть, но пока оно большого распространения не получило.
У нас выполняется центральный процесс, который этот плейбук читает.
Плейбук — это набор плеев. Каждый плей состоит из двух элементов: описание хостов, на которых выполняется данный участок кода, и tasks — задачи, то есть именно что делать.
Если совсем примитивно, с помощью Ansible можно сделать на хостах всё то же, что мы делаем руками, только автоматизировано, массово, детерминировано и т. д. Поэтому элементарный task или элементарная задача — это выполнение какой-нибудь команды, например. Но чаще всего используются специализированные модули: для шаблонизации файлов, для запуска сервисо
А команды — это просто команды консоли или…?
Да, есть такой модуль command, который просто выполняет консольную команду.
Он на клиенте?
На клиенте, да.
А почему нельзя просто через SSH напрямую в терминал фигачить?
Модуль command — это последний шанс, если у тебя нет специализированного модуля для выполнения определённой задачи.
Чтобы запустить сервис, мы не делаем command systemctl start engine, мы используем модуль service или модуль systemd, который приводит систему, в данном случае сервис, в заданное состояние. То есть он запущен и включён либо остановлен и выключен.
Вообще, управление конфигурациями задумывалось как декларативное. То есть мы описываем желаемое состояние системы, потом эта самая управлялка приводит систему в заданное состояние. Если система уже находится в нужном состоянии, то не производится никаких действий. Это свойство называется идемпотентность.
Понятно, в не зависимости от того, сколько раз ты запускаешь…
Результат всегда один и тот же.
Это прям как в REST API, разработчикам должно быть понятно.
Да!
Поэтому мы описываем в задачах желаемую конфигурацию и Ansible приводит (если это грамотно написано) систему в заданное состояние. Если система уже в заданном состоянии, то действие не производится. Потом мы в выводе команды всё это видим.
Что такое роли? Роли можно уподобить подключаемым библиотекам. То есть мы пишем роль, которая, допустим, настраивает веб-сервер nginx, и потом из разных плейбуков для разных хостов с разными параметрами мы эту роль подключаем.
Роли могут храниться как на диске вместе с плейбуками конфигурации, так и во внешних репозиториях. Есть такая штука, называется Ansible Galaxy — это что-то вроде центрального хранилища ролей. Туда можно свою роль подать на рассмотрение. Это не то чтобы пакетный менеджер, а как GitHub, допустим. Только специализированный, исключительно для Ansible.
То есть роли — это готовые куски конфигураций, которые ты можешь у себя встраивать и переиспользовать?
Это описание конфигурации, которому ты можешь сам задавать разные параметры из инвентаря.
Отлично. И тут включается механизм параметризации?
Да. При запуске команды ansible-playbook, которая и делает нужные изменения на хостах, мы задаём ей параметром так называемый инвентарь — откуда брать параметры. В инвентаре у нас чаще всего список хостов, объединенных в группы (чтобы Ansible знал, на каких хостах выполнять те или иные задачи), и набор переменных для них, которые подставляются в роль и меняют поведение в нужную сторону.
Jinja2 это из этой темы?
Jinja2 — это весьма мощный язык шаблонизации. Jinja2 используется в ролях.
Это параметризация, как я понимаю, нет?
Это шаблон. А параметры — это то, что ты уже в этот шаблон вставляешь. Вообще, это очень мощная вещь, с помощью этого языка шаблонов можно описывать конфиги практически любой сложности и при выполнении уже непосредственно роли из своего инвентаря подставляются нужные переменные и тебе формируется нужный конфиг.
Как гарантировать, что твой конфиг работоспособный, что он не устарел, не сломался — вдруг на сервере что-то поменялось, и у тебя команда ansible-play не сработала. Это как-то тестируется?
Да, разумеется, это тестируется.
В правильно построенной инфраструктуре мимо Ansible и вообще любого средства управления конфигурациями ничего меняться не должно. То есть никто не должен ходить руками и там что-то делать и настраивать. Если он это делает, то за это нужно по рукам бить. Потому что правильный подход — это вносить нужные изменения в инвентарь. Если в используемых ролях нет нужных действий, ты дописываешь.
Это подход «инфраструктура как код» (Infrastructure-as-Code, IaC). Код в каком смысле? Код должен проходить ревью, то есть твои коллеги должны видеть, что ты написал и подтвердить, что это вменяемо.
Речь про конфигурации?
Да. То есть точно также весь инфраструктурный код хранится в репозитории GitLab, GitHub — чем вы там пользуетесь.
Рядом с продуктовым кодом? Или это отдельная репа?
Разумеется, отдельная. Это много реп чаще всего. И потом перед тем как что-то изменить, ты делаешь merge request с изменениями (в зависимости от того, какая схема у вас в компании принята). В простейшем случае пишешь в телегу коллеге: «Вот, я написал, посмотри» и кидаешь ссылку. Он говорит: «Всё нормально, идём на тестирование».
Раз инфраструктура как код, у нас код проходит пайплайн. Всегда самый первый или второй job — это lint. Команда ansible-lint просматривает код на синтаксические ошибки и дает дельные советы. Если ты ставишь пакет, вызывая его как command "yum" или "apt-get" install, то тебе линтер напишет: «Используй специализированный модуль yum или apt». То есть тут не только синтаксические ошибки, но и семантика.
Разумеется, кроме проверки синтаксиса, есть инструмент полного тестирования. Потому что синтаксический анализатор не знает общего замысла твоего плейбука и не может знать, что будет в конце, идемпотентна данная конфигурация или нет. Есть такой инструмент, называется Molecule. Она запускает виртуальную машину...
Главный вопрос: не на проде же ты это будешь делать?
Да, разумеется, запускается виртуалка, в ней согласно описанной для Molecule конфигурации запускается Ansible плейбук.
А где гарантия, что виртуалка похожа на прод?
Надо самому следить.
Продакшн у нас же изначально чистый, как я понимаю?
Продакшн у нас изначально чистый. То есть с момента рождения он у нас сделан с помощью Ansible. Поэтому тестовая виртуалка обязана быть похожа на прод. Если нужно, туда можно и приложения вкатить отдельным job’ом.
А по ресурсам она тоже должна быть такая же, как прод, или необязательно?
Это зависит от конкретного случая. Если после запуска Molecule ты тестируешь ещё что-то с помощью каких-то внешних инструментов… Условно говоря, выкатываешь сайт, тестируешь Selenium (такое тоже бывает), то, естественно, ресурсов должно быть больше. Если он у тебя просто проходит набор ролей, то ресурсов нужно ровно столько, чтобы этот Python в память влез.
Тебе как человеку, который работает с Ansible, нужно программирование? Ты говорил, язык разметки YAML, написан на Python, нужно ли дописывать какие-то модули, ещё что-то. Просто, чтобы понимали девелоперы: нужно ли владеть языком при работе с Ansible?
Можно не владеть. Но лучше владеть.
Каким и для чего?
Python. Потому что, если под конкретную задачу нет нужного модуля, конечно, можно написать это всё в виде скрипта и дёргать его из task, но это плохой тон, это называется «bashsible» в комьюнити, связанных с Ansible. И это прям фу-фу.
Чаще советуют написать специализированный модуль под свою задачу. Его можно написать на Shell, да и вообще на любом языке, потому что там просто система вызовов, но лучше всего писать на нативном для этого дела.
Нативном для Ansible, да?
Да.
Потому что можно на C попробовать это сделать.
Ну, если затраченные усилия будут оправданы, почему бы и нет.
Какие есть более или менее популярные аналоги Ansible? Или это монополист на рынке?
Нет, это, конечно, не монополист. Значительно более старые решения (в смысле прошедшие более длинный путь развития) — это Chef и CFEngine. Последний сейчас уже мало используется.
Они все работают примерно одинаково?
Они работают в основном как pull: на настраиваемой ноде выполняется сервис, который тянет конфигурацию.
Ansible — единственный, кто делает push из крупных?
Ansible умеет pull, но это не основной его режим работы. SaltStack тоже умеет push, но это не его основной режим работы. Ansible, можно сказать, в этом плане единственный. Ну и видимо, народу нравится, потому что я смотрю время от времени Google Trends по запросам, относящимся к Chef, к Ansible, и вижу, что Ansible набирает всё большую популярность. Более старых решения на пятилетнем промежутке её чуть-чуть теряют. Хотя есть энтузиасты, которые за них топят и их поддерживают.
Может, есть какие-то книги, что можно почитать, чтобы базово познакомиться с Ansible?
Знакомство с Ansible лучше всего начать с документации. На docs.ansible.com расписаны и самые основы, и примеры простых плейбуков, и вообще всё что нужно. Я еще скажу про книгу Ansible: Up and Running. Книга неплохая, но Ansible достаточно быстро развивается и поэтому многие вещи в ней уже устарели. Поэтому лучше всего начинать именно с документации на сайте, она там всегда актуальна.