Привет, Хабр! Меня зовут Игорь Анохин, я руковожу платформенной разработкой K2 Cloud. В общении с друзьями и коллегами мне периодически приходится пояснять, что я разработчик, а не DevOps-инженер. Когда я рассказываю, как мы поднимаем платформенные сервисы (PaaS), например, базы данных, Kubernetes и другие, «разработчик» — это последнее, что приходит людям в голову.
Поэтому в статье я расскажу, что такое PaaS и как он работает. Опишу его жизненный цикл от создания до мониторинга и решения проблем с кластером, поделюсь планами развития.
Как устроены приложения
На базовом уровне веб-приложения выглядят похоже — всё начинается с простого бэкенда и фронтенда, который мере развития обрастает стандартным набором сервисов. Как правило, это база данных, кэш-фреймворк, система для сбора метрик и брокер сообщений. Например, Postgres, Redis, Prometheus и Kafka.

Если мы хотим поднимать приложения проще и быстрее, то очевидное решение — Docker. Это отличный способ стандартизировать и упростить процесс развёртывания. С его помощью мы быстро упаковываем приложение в контейнер, обеспечивая повторяемость и надёжность его работы в разных средах. Когда возникает потребность в сервисах, мы переводим всё на Docker Compose и поднимаем базы данных, кэширование, мониторинг и так далее.
Если нужно горизонтальное масштабирование, то появляется Kubernetes со своими деплойментами, репликасетами и подами, внутри которых тоже есть контейнеры.

В Kubernetes достаточно просто развёртывается stateless-приложение. Оно не хранит данные между запросами, а для масштабирования достаточно увеличения подов. Поэтому написанный нами бэкенд загружается в управляемый Kubernetes и хорошо работает в контейнерах.
Всё сложнее со stateful-приложениями — базами данных и вспомогательными сервисами. По нашему опыту, в контейнерах они немного теряют в управляемости, и их труднее траблшутить в случае проблем. Платформенные сервисы — это способ быстро поднять вспомогательные приложения и устранить проблемы, с которыми сталкиваешься в Kubernetes. Наши приложения поднимаются не в контейнерах, а на виртуальных машинах. Это помогает не терять скорость и управляемость.

Создание сервиса
На примере PostgreSQL я объясню, как создаётся, мониторится и управляется сервис. Прежде чем развернуть кластер PostgreSQL или любой другой сервис, нужно создать виртуальные машины одним из двух способов:
Terraform — для стандартных сценариев с поддержкой мультиоблачности и повторяемостью инфраструктуры.
Собственный движок — для сценариев с возможностями кастомизации или интеграции со специфичной инфраструктурой.
Сейчас наше облако поддерживает Terraform-провайдер, но когда мы начинали разрабатывать свою платформу, его ещё не существовало. Поэтому мы написали собственный движок для создания виртуальных машин из готовых образов.
Ansible или Puppet
После создания виртуальные машины нужно настроить. В Kubernetes мы делали это с помощью собственного решения, а для PaaS решили выбрать средство для управления конфигурациями, потому что количество пакетов для баз данных и прочего оказалось слишком большим, чтобы устанавливать всё руками и следить за этим. Более или менее подходящих для нас оказалось два: Ansible и Puppet.
Ansible работает по push-модели через SSH. Чтобы с его помощью настроить виртуальные машины, к ним нужен доступ. Это не всегда возможно, так как пользователь зачастую его ограничивает из-за групп безопасности и других сетевых правил.
В отличие от Ansible, Puppet работает через клиент-серверную модель и не требует прямого SSH-доступа для настройки. На каждую виртуальную машину устанавливается легковесный Puppet-клиент. Он сам обращается к Puppet-серверу за конфигурацией, репозиториями и обновлениями.
Вдобавок у нас уже был опыт работы с Puppet. Наша команда Professional Services эксплуатирует крупные инфраструктуры заказчиков. Управление ими автоматизировано с помощью Puppet-манифестов, поэтому использование уже написанной автоматизации даёт хороший старт и основу для начала работы. Мы выбрали Puppet.
Настройка виртуальных машин
Чтобы начать работать через Puppet, его нужно установить. За первоначальную настройку ВМ при запуске отвечает облачный сервис Cloud-init. У нас он выполняет три основных действия:
Устанавливает наш самописный PaaS-агент для работы с виртуальными машинами.
Устанавливает Puppet-клиент для управления конфигурацией и настройкой виртуальной машины.
Генерирует сертификаты для Puppet, чтобы изолировать клиентов друг от друга. Сертификаты обеспечивают взаимодействие сервисов одного клиента между собой и изолируют их от сервисов других клиентов.
Когда Cloud-init завершает свою работу, запускается PaaS-агент и подключает Puppet. Первым делом Puppet обращается к External Node Classifier (ENC). Это сервис, который возвращает информацию о версии окружения, номерах репозиториев и их версиях, доступных возможностях. Мы постоянно наращиваем количество возможностей, поэтому, чтобы получать как можно больше функций на наших PaaS, клиентам нужна актуальная версия окружения.
После обращения к External Node Classifier (ENC) Puppet идёт в сервис Metadata. Через него виртуальная машина получает информацию о своём IP-адресе, названии и тегах.

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

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

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

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

Теперь мы не только мониторим процессы, но и можем быстро подключаться для решения кризисных ситуаций. Сейчас Teleport, как и мониторинг, работают во внутреннем доступе. В ближайшем времени они появятся у всех пользователей и помогут нам с другими задачами.
О будущем PaaS в К2 Облаке
Опыт показал, что потребление наших сервисов за год выросло на 80%. Большая часть из них — это базы данных, в том числе PostgreSQL, о котором я рассказал в статье.
PaaS — востребованное решение, особенно для небольших команд с типовым окружением, когда важна скорость развёртывания. PaaS даёт возможность быстро поднять стандартизированную систему без лишних настроек и ручного труда.
Если же у компании уникальная инфраструктура, собственные требования к безопасности или своя сильная команда DevOps, которая знает, как кастомизировать свои сервисы, то достаточно стандартного IaaS.
С помощью Puppet и Teleport мы гибко управляем сервисами: масштабируем и поддерживаем их работу. Проактивный мониторингпомогают нам оперативно реагировать на инциденты. Мы планируем и дальше развивать мониторинг и управление сервисами, добавлять новые инструменты на основе Big Data и ML. В ближайших планах — GitLab ClickHouse, Data Lake, ML Flow и Jupyter.