Привет! Меня зовут Игорь Анохин. Я руковожу платформенной разработкой K2 Cloud хотя большую часть времени трачу на объяснения, что я не DevOps. Когда рассказываю, как мы поднимаем базы данных, Kubernetes и другие сервисы, «разработчик» – это последнее, что приходит людям в голову.
Поэтому в статье я расскажу, что такое платформенные сервисы, и как они работают. Мы погрузимся в их жизненный цикл и пройдём весь путь нашего сервиса: от создания до мониторинга и решения проблем с кластером. В заключение поговорим о наших планах развития платформенных сервисов.

Что такое PaaS
Все мы в той или иной мере пишем сервисы. У нас есть веб-приложение с кодом и более или менее уникальный Backend и Frontend. По мере развития это обрастает набором стандартизированных сервисов. Есть какая-то база данных (например, Postgres), кэш-фреймворк (типа, Redis), cистема для сбора метрик (Prometheus) и брокер сообщений (Kafka).
Мы поднимаем такой набор сервисов от проекта к проекту. Поэтому хочется поднимать такие сервисы быстрее и проще. И в качестве инструмента напрашивается Docker.
Сейчас многие помещают в него свои проекты. Это особенно заметно, если зайти на GitHub. Docker — отличный способ стандартизировать и упростить процесс развертывания. С его помощью можно быстро упаковать приложение и все его зависимости в единый контейнер, чтобы обеспечить ему повторяемость и надежность работы в разных средах. А если нужен тот самый стандартизированный набор: поднять базу данных, кэши и так далее, можно перевезти своё приложении на Docker Compose.

Когда возникает потребность в горизонтальном масштабировании, чтобы вывести приложение за пределы одной виртуальной машины, появляется Kubernetes со своими деплойментами, репликасетами и подами, внутри которых тоже есть контейнеры.
У Kubernetes много преимуществ, но это тоже платформа, и её нужно содержать. Поэтому он стал одним из первых платформенных сервисов, которые мы сделали. Вот так он выглядит на нашем веб-интерфейсе.

Поставить stateless-приложение в Kubernetes достаточно просто. Оно не хранит данные между запросами, а для масштабирования достаточно увеличения подов. Поэтому написанный нами BackEnd можно загрузить в управляемый Kubernetes, и будет хорошо работать в контейнерах.
Но вот со stateful-приложениями, такими как базы данных, всё сложнее. По нашему опыту, stateful-приложения в K8S немного теряют в управляемости в рамках контейнеров, и их сложнее траблшутить в случае проблем. Поэтому сегодня мы будем говорить о наших платформенных сервисах, которые внутри поднимают приложения на виртуальных машинах без использования контейнеров.
В следующих главах мы посмотрим, как всё это работает на примере Postgres. Как он создается, мониторится, управляется и что мы вообще с ним делаем.
Создание сервиса
Для создания кластера PostgreSQL, как и любого другого сервиса с использованием виртуальных машин (VM) есть два подхода:
-
Для стандартных сценариев с поддержкой мультиоблачности и повторяемостью инфраструктуры лучше использовать Terraform. Он автоматически создает необходимые виртуальные машины.
-
Если требуется максимальная кастомизация или интеграция со специфичной инфраструктурой, можно написать собственный движок.
Когда мы начинали разрабатывать нашу платформу, Terraform-провайдера еще не было. Поэтому мы написали собственный движок для создания виртуальных машин на основе шаблонов.
Но просто создавать виртуальные машины с заданными характеристиками мало. Не важно автоматизировано или руками. В K8S мы делали это вручную, но в рамках PaaS много разных сервисов для управления конфигурациями. Есть из чего выбрать, но более или менее подходящих для нас оказалось два: Ansible и Puppet.
Ansible или Puppet
Ansible работает по push-модели через SSH. Это требует прямого доступа к нодам, который может быть закрыт сетевыми правилами. Например, у K2 Cloud есть внутренняя инфраструктура, которая скрыта от пользователя, и есть пользовательский проект. Пользователь может заходить туда по SSH и видеть в консоли свои виртуальные машины. В соответствии с группой безопасности и сетевыми правилами может отключить нам доступ к своей инфраструктуре.

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

В отличие от Ansible, где для настройки «пустых» виртуальных машин требуется прямой SSH-доступ, Puppet работает через клиент-серверную модель. На каждую виртуальную машину устанавливается легковесный Puppet-клиент. Он сам обращается к Puppet-серверу за конфигурацией, репозиториями и обновлениями.
Также у нас уже был опыт работы с Puppet. Наша команда Professional Services автоматизировала всё с помощью Puppet-манифестов, разработанных для управления крупными инфраструктурами заказчиков. Поэтому можно было использовать написанную автоматизацию для сервисов. Мы решили, что это даст нам старт и основу для начала работы. Поэтому выбрали Puppet.
Настройка VM
Для настройки наших виртуальных машин, работающих на AlmaLinux (или RedOS), мы использовали Cloud-init. Этот облачный сервис позволяет выполнять первоначальную настройку виртуальной машины при запуске. Он может запускать bash-скрипты, которые выполняют необходимые задачи.
В нашем случае Cloud-init выполняет три основных действия:
-
Установка PaaS-агента, который позволяет работать с виртуальными машинами.
-
Установка Puppet-клиента, который управляет конфигурацией и настройкой виртуальной машины.
-
Генерация сертификатов для Puppet, которые позволяют сервисам одного клиента взаимодействовать между собой и доверять друг другу, в то время как сервисы разных клиентов остаются изолированными.

Когда Cloud-init завершает свою работу, запускается наш агент и подключает Puppet. Первым делом Puppet обращается к External Node Classifier (ENC) — специальному сервису, который возвращает информацию о версии окружения, номерах репозиториев и их версиях, а также о функциях, которые мы постоянно улучшаем. Поэтому, чтобы получать как можно больше возможностей на платформенных сервисах, держите версию окружения как можно более актуальной.
После обращения к External Node Classifier (ENC), Puppet идет в сервис Metadata. С его помощью виртуальная машина может получить информацию о своем IP-адресе, названии, а также о тегах, которые на ней расположены.

Компоненты
Собрав всю информацию, Puppet устанавливает компоненты для кластера PostgreSQL, включая инстанс Postgres, HAProxy, Consul и Patroni. И сообщает об этом PaaS-агенту, который передает информацию через сервис Metadata.
Как только все виртуальные машины подтверждают готовность, пользователь видит в своем веб-интерфейсе состояние кластера «Запущено» и адреса, по которым может общаться с Postgres-ом.

Прелесть такого подхода заключается в том, что он работает в самых разных инсталляциях. Будь это инстанс Postgres в одной зоне доступности, высокодоступный кластер или кластер в трех зонах доступности. Флоу работы останется одинаковым во всех случаях. Кроме того, сверху можно накладывать различные облачные функции, такие как интеграция с облачными балансировщиками нагрузки. Это позволяет эффективно распределять трафик между нодами кластера.
Управление сервисом
Рано или поздно пользователь захотел бы что-то изменить в своем сервисе. Например, количество воркеров. Но у нас не было прямого доступа к виртуальным машинам, которые стояли в его пользовательском проекте. Поэтому мы снова решили использовать pull-модель.
PaaS-агент периодически обращается к сервису Metadata, чтобы проверить, не изменились ли данные. Если обнаруживает новый action, то запускает Puppet. Который тоже смотрит в сервис Metadata, видит тот же action и обращается к External Node Classifier для получения информации о классах и параметрах узла. После выполнения всех задач Puppet обновляет конфигурационный файл через pgsql.conf и сообщает об этом нашему сервису.

Конечно, всё это можно сделать и руками, но у нашего подхода к управлению кластером есть несколько преимуществ перед ручным.
Можно параллельно выполнять обновления на нескольких воркерах, что ускоряет процесс. Или последовательно, если обновление считается опасным. Это позволяет минимизировать риски, что изменения в таком случае можно откатить обратно.
И вроде бы всё хорошо — сервис создан, пользователь может им управлять, но рано или поздно могут возникнуть проблемы. Поэтому нужны инструменты и процессы для их оперативного решения.
Мониторинг сервиса
Однажды в нашей системе произошел критический инцидент. Из-за обрыва сети мастер-нод в кластере начал копить WAL-логи (write-ahead logs), что в конечном итоге привело к заполнению дискового пространства и недоступности нод. Но известно об этом стало только от заказчика через тикет в Jira. А доступа к виртуальным машинам у нас нет. Поэтому, чтобы не запускать что-то руками и не просить пользователей открывать SSH доступ инженерам службы поддержки, мы начали искать другое решение.
Экспортеры
Первое, что мы начали делать — это наш внутренний мониторинг. У нас уже был сервис Prometheus, но его использование могло быть неудобным для пользователей из-за дополнительных затрат. У нас уже были выведены экспортеры практически на все сервисы, чтобы мониторинг мог туда подключаться как отдельный PaaS Prometheus. Наш агент научился работать с экспортерами и отправлять данные через сервис Metadata в Victoria Metrics. Это позволило оперативно обнаруживать и решать подобные проблемы.

У нашей службы эксплуатации появились красивые графики потребления и возможность видеть, когда с сервисом что-то идет не так. Оставалось только научиться подключаться к пользовательской VM. И тут помог Teleport.
Обратная SSH-сессия
Несмотря на появление метрик, что-то проактивно делать мы всё равно не могли. Для доступа службы эксплуатации к виртуальным машинам пользователей всё ещё было необходимо просить SSH доступ. Поэтому, как и в остальных случаях, мы воспользовались решением с PULL системой для удалённого доступа — Teleport. На каждую виртуальную машину ставится клиент, который сам может ходить в Teleport сервис. Мы добавили в веб-интерфейс кнопку «Поддержка». При ее нажатии появлялся новый action, который передавался в Puppet. Puppet устанавливал teleport-agent, так как по умолчанию он не установлен, чтобы избежать лишних уязвимостей. А teleport-agent получал одноразовый токен, подключался к серверу и предоставлял полный доступ службе эксплуатации.
При этом с помощью аудитов Teleport можно было прозрачно отслеживать действия инженеров службы эксплуатации на виртуальной машине пользователя.

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

Потребление наших PaaS выросло на 80% за год. И большая часть из них базы данных — как раз тот самый PostgreSQL, о котором мы говорили. Поэтому решение востребованное, особенно для небольших команд с типовым окружением, когда важна скорость развёртывания.
Если же у вас уникальная инфраструктура, собственные требования к безопасности или, например, своя сильная команда DevOps, которая знает, как кастомизировать ваши сервисы, то вам подойдет стандартный IaaS.
Итоги
PaaS — это возможность быстро поднять стандартизированную систему с уже готовыми ручками. Production-ready решение, которое можно использовать без лишних настроек и ручного труда. Благодаря инструментам вроде Puppet и Teleport у нас появилась гибкость в управлении сервисами, включая масштабирование, мониторинг и поддержку. А с оперативным реагированием на инциденты помог проактивный мониторинг через экспортеры. Так у пользователей появляется готовое решение минимумом усилий, но с высоким уровнем безопасности.
Мы планируем и дальше развивать мониторинг, управление сервисами и добавлять новые инструменты на основе Big Data и ML. В ближайших планах: GitLab Clickhouse, Data Lake, ML Flow и Jupiter.

Автор: IgorAnokhin