Вступление

Это история о том как небольшая группа IT-шников хотела автоматизировать свою работу, а в результате решила переизобрести DevOps-заново, и (с точки зрения этой группы) добилась прогресса.

Кто мы

Если кратко, то мы небольшая группа хорошо знакомых друг с другом людей, которые последние несколько лет занимались разными проектами. Туда входило и создание культурных центров в Москве, и чтение лекций, и IT-разработка. Сейчас мы называемся «Цифровая Протопия». В 2021 году большинство проектов у нас завершилось (удачно или не удачно), а в 2022 году мы решили запустить новые – в основном open source проекты для автоматизации горизонтальных команды и сообществ. Но сначала было нужно разобраться с наследием прошлого.

В истории, про которую я пишу в этой статье, занимались два человека из этой команды – я (Дмитрий Лейкин) и Федор Моросеев. Мы занимались и занимаемся описываемым в статье проектом в свободное от основной работы время.

Наследие

От предыдущего этапа нашей деятельности у нас осталось множество IT-разработок. Какие-то остались недоделанными, какие-то были выпущены в продакшен, какие-то были опубликованы как open source, какие-то остались закрытыми, какие-то нужно было архивировать, а какие-то опубликовать как демо-версии. Всё было свалено в кучу на нескольких VPS-ках. И чтобы продолжать деятельность, стало важно привести все это во вменяемый вид.

Надо все контейнеризировать

Одна из проблем, с которой мы столкнулись на предыдущем этапе – это сложность разработки и поддержки большого числа проектов одновременно. Мы поняли, что нам не хватало автоматизации нашей деятельности – разработки, тестирования, деплоя и т. д. Короче говоря, мы поняли, что нам нужен DevOps. Необходимо контейнеризировать все проекты, автоматизировать работу с ними и дальше вести разработку нормально.

DevOps-инженер со стороны

Мы начали решать задачу с того, что попросили знакомого DevOps-инженера настроить нам среду для работы. Он попросил доступы, попробовал развернуть DevOps-инструменты (kubernetes, runner и gitlab на своем железе), помог составить список текущих проектов и дальше дело встало в тупик. В течении двух месяцев он настраивал и правил среду развертывания, но так и не смог развернуть на ней наши проекты. Поэтому мы решили искать другой путь.

Самостоятельная попытка DevOps и что нас не устроило.

К этому моменту Федор сам глубже погрузился в DevOps и предложил мне настроить все самому по его инструкциям. Он предложил установить связку Gitlab/Ansible/Docker Swarm, а дальше постепенно переходить на Кубернетес.

Установить первую связку оказалось несложно, и я не очень понимал, что именно заняло столько времени у нашего знакомого.

Но установив все необходимое, я сразу увидел, в чем проблема – ну написал я playbook, который собирает образ, выкатывает на сервер, настраивает nginx и certbot. А если я хочу изменить доменное имя? А если я хочу удалить одну из инсталляций? Что, каждый раз придется писать скрипты для более-менее стандартных задач?

Тогда было принято решение попытаться перейти на Кубернетес. По опыту работы Федора, он знал что в кубернетесе рутинные задачи (работу с доменами, сертификатами, репозиториями и прочим) можно автоматизировать.

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

Тогда было предложено установить надстройку над кубером – систему k3s. Она в отличии от голого кубера установилась сразу. Но далее надо было прикрутить к куберу необходимый для наших задач функционал и интегрировать его с gitlab. Судя по описанию, это все требовало написания множества yml-конфигов, на порядок более сложных, чем docker-compose.

Еще упоминались системы типа helm и werf, которые умели шаблонизировать конфиги. Они были установлены, но, как будет видно далее, не помогли решить задачу.

Была попытка настроить кубернетес, чтобы хотя бы установить backend и frontend для одного из наших проектов. То есть решить ту же задачу, которую до этого решали через ansible + docker swarm. Конфиги были написаны, но по нужному адресу проекты так и не открывались. После нескольких часов вопросов и ответов было выяснено в чатах, что в k3s работает два балансировщика одновременно и один из них надо было отключить. После этого проект как-то открылся.

Но далее я попытался привязать кубернетес к docker registry из гитлаба. Чтение мануала заставило меня подумать, что это шутка. Там буквально было написано: «Возьмите логин и пароля от реестра, склейте их в строчку, зашифруйте в base64, вставьте в большой yml файл, зашифруйте его в base64, вставьте в еще больший yml-файл и загрузите в кубер».

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

Что не так с существующим DevOps

И вот здесь я и Федор задались очевидным вопросом. Почему все так сложно? Почему для таких стандартных задач как развертывание контейнеров, управление ими, подключение к ним базы данных, доменного имени, ssl-сертификита, бэкапов и других абсолютно стандартных вещей, нужно так много усилий? Почему концепция «инфраструктуры как код», которая выглядит очень разумной, на практике достаточно неудобна? Почему нужно так много инструментов для решения одной и понятной задачи?

Тут же всплыл пример обратной ситуации. Вы знаете что такое «виртуальный хостинг»? Это когда за минимальные деньги вы получаете небольшой кусочек сервера с предустановленным, например, вордпрессом, куда уже подключены доменное имя и база данных MySQL. Да-да, то что называется LAMP-стэком. При этом управлять этим может даже непрограммист, через удобную панель управления хостингом. Почему для контейнеризированных проектов нельзя сделать то же самое?

Начались попытки проектирования и изучения существующих альтернатив. В процессе выяснились удивительные вещи. Например то, что кубернетес создавался как эксперимент с единственной целью – проверки гипотезы о том, что докер-контйнеры можно запускать на кластере, и больше ни для чего. Или то, что докер и кубер создавались для работы исключительно со stateless-приложения, и как следствие, к докеру даже volume нельзя добавить, не уничтожив контейнер и не создав его заново. И многие другие абсурдные вещи.

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

При этом было ощущение, что все эти проблемы в истории IT-индустрии уже когда-то возникали. И что они уже были когда-то решены.

Операционная система второго порядка

И тут, в процессе исследования всего этого, меня и Федора осенило.

Мы поняли на что похожи задачи, которые пытаются решить существующие инструменты.

Это задачи которые давным-давно умеет решать такой класс IT-приложений как... операционные системы.

Что делают операционные системы? Они являются прослойками между отдельными программами и железом, на котором они запущены. Они позволяют делать стандартные операции одинаково – устанавливать и удалять программы, давать им через API доступ к друг другу и к оборудованию, абстрагируют разнообразие «железа» под единым интерфейсом HAL (Hardware abstraction layer), позволяют запускать и закрывать программы, следят за ресурсами процессора и памяти которые они используют... Разве не то же самое мы хотим для серверных контейнеризированных приложений?

И стало понятно что нам нужно. Нам нужна операционная система второго порядка!

Что значит второго порядка?

Это значит, что эта операционная система работает поверх обычной операционной системы (или нескольких – если на кластере). Программами для ОС второго порядка являются контейнерами. А «железом» (и это самое удивительное!) – приложения операционной системы первого порядка!

Что имеется в виду?

На серверах существует ПО, которое не рекомендуется класть в контейнеры. Такое как СУБД или nginx. Потому что оно обычно работает постоянно, в ограниченном числе экземпляров и по сути предоставляет общий ресурс для любого числа контейнеров.

Это ПО с точки зрения ОС второго порядка – то же самое, что железо для ОС первого порядка. И, как и для обычного железа, для этих программ нужны «драйвера» – специальные программы, которые позволят ядру нашей ОС единообразно общаться с любым «устройством типа БД» или «устройством типа веб-сервер».

И тогда получается, что стандартные операции типа «собрать образ», «развернуть контейнер», «свернуть контейнер», «подключить к контейнеру БД», «сделать бэкап», «восстановиться из бэкапа», «подключить доменное имя», «получить SSL-сертификат» становятся стандартными и могут быть автоматически выполненными в нужном порядке.

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

Так мы поняли, что нам нужно написать такую ОС. Мы ее назвали - «МегаполОС».

Как устроен МегаполОС

Разработка МегаполОСа ведется уже более 9 месяцев. Проектировали его я и Федор, до публикации в open source (в мае 2023 года) я его писал в одиночку. Сейчас к его разработке подключились и другие программисты, но этого недостаточно для скорого выхода стабильного релиза. Сейчас проект находится в альфа-версии.

Мегаполос объединяет множество машин (серверов) в единый кластер. Каждый сервер является узлом (нодой) этого кластера.   

МегаполОС на данный момент состоит из ядра, драйверов и веб-GUI.

Ядро – это приложение, которое запускается на каждой ноде, включает ее в кластер и берет его под свой контроль. Оно управляет серверной инфраструктурой и контейнерами, развернутыми в кластере.

Драйвера – это самая интересная часть. Я ранее описывал, что мы относимся к серверной инфраструктуре (причем в первую очередь к программной) как к «устройствам» в операционной системе. А для управления устройствами нужен слой абстрагирования устройств (HAL) и драйвера.

Например: PostgreSQL – это «устройство» типа СУБД. Почему это устройство? Потому что оно предоставляет свои ресурсы (базы данных) произвольному числу контейнеров, и задача ОС - регулировать доступ контейнерных приложений к этому ресурсу.

С точки зрения ядра устройство типа СУБД – это стандартизированная сущность, у которой есть функции «создать БД», «удалить БД», «создать бэкап», «восстановить бэкап» и другие. Как конкретно СУБД это делает – решает драйвер. Драйвер – это контейнерное приложение с дополнительными привилегиями, которое занимается управлением своим устройством. На данный момент мы написали несколько драйверов для популярного серверного ПО, вроде mongodb, nginx, gitlab, certbot и других.

При этом, что является особо интересным – драйвер может «установить устройство», то есть поставить соответствующую программу на ноду или в контейнер. Последнее мы называем «виртуальным устройством».

Веб-GUI – это, естественно, интерфейс для управления МегаполОСом. Мы планируем сделать его приближенным к GUI операционной системы – то есть к простому способу управления сложной системой. Чтобы управлять кластером было не сложнее управления LAMP-хостингом.

Другая фишка, которая мне очень нравится – это то как megapolos управляет volume-ми. Как было указано выше, чтобы подключить или отключить volume в docker, надо уничтожить и заново создать контейнер. Это выглядит как бред – зачем полностью перезагружать приложение для такой простой операции?

Подобно хранилищам в обычных ОС, в МегаполОСе все volume управляются ядром. Пользователь может через API или GUI управлять ими. И в нем есть такое понятие как динамические volume. К каждому контейнеру монтируется volume «/megapolos» и система в любой момент может примонтировать в него директории с хоста как поддиректории. То есть мы имеем простое объектное хранилище, встроенное в МегаполОС.

В частности, можно указать, какое volume использовать для бэкапов СУБД, и МегаполОС сам будет его подключать и отключать в нужный момент.

Еще раз подчеркну то, что МегаполОС работает постоянно. Он постоянно отслеживает ситуацию и дает команды для ее изменения. Это в частности означает то, что связь между ядром и приложениями – двусторонняя. У контейнерных приложений есть доступ к API ядра, а у ядра есть доступ к приложениям. В частности, драйвера могут просить ядро выполнить shell-команды на нужной ноде для управления устройствами.

Теперь о технической части.

На данный момент МегаполОС находится в стадии альфа-версии. Уже написаны, как было указано выше, ядро, драйвера и GUI. Основные архитектурные элементы были реализованы, но пока что не была реализована поддержка множества нод. Также сам код еще будет рефакториться и отлаживаться.

Стэк МегаполОСа – это rqlite + Docker + node.js + GraphQL + React.

  • rqlite – это распредленный sqlite. Мы специально искали распредленную реляционную СУБД, поскольку у нас очень строгие взаимоотношения между сущностями. Планируется переход на более полноценную распределенную реляционную СУБД. Вы можете посмотритеть структуру БД здесь: https://gitlab.com/megapolos/megapolos-core/-/blob/main/install/newrqlite.sql

  • Docker – управление контейнерами. В будущем планируем перейти на containerd.

  • node.js – на нем написаны ядро и драйвера. Для не очень часто происходящих действий этого может быть достаточно. Высокопроизводительные части мы планируем переписывать на go. Мы выбрали этот стэк, потому что у нас происходит много параллельных и асинхронных процессов.

  • GraphQL – это язык API, через который происходит взаимодействие с ядром и драйверами. Мы выбрали это API, потому что оно типизировано

  • React – на нем написан GUI

Как пользоваться Мегаполосом

Для этого я захожу в GUI, который мне показывает список приложений (Applications). Приложение - это набор логически связанных образов, из которых можно создавать контейнеры.

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

Я могу подключить к контейнеру устройство типа builder - которое занимается сборкой контейнеров.  Этим устройством может быть gitlab ci, локального докера или что-нибудь еще. Вне зависимости от реализации, контейнер будет собран из образа. Я также могу указать специальные env-параметры и volume для этого контейнера.

При помощи драйверов я также могу подключать к контейнерам базы данных, доменные имена и SSL-сертификаты. Я в любой момент могу подключить или отключить что-либо  из них или поменять их параметры (например, сменить доменное имя). При этом МегаполОС произведет все необходимые для этого действия – поменяет конфиги, пересоберет контейнеры, пропишет ENV-параметры, получит сертификаты, проследив, чтобы не возникало конфликтов. Например, чтобы разным экземплярам приложения не было присвоено одно доменное имя.

Когда я хочу добавить в систему драйвер, я добавлю его по сути как обычное приложение, но через другой раздел GUI - диспетчер устройств. У драйвера есть манифест, который помогает МегаполОСу определить его тип и параметры. Мы планируем также добавить поддержку манифестов для любых приложений, чтобы упростить их настройку.

Также есть раздел управления volume-ми, где я могу создать как статический volume, строго привязанный к каталогу на ноде, так и автоматический, расположение которого на ноде автоматически создается МегаполОСом.

На данный момент мне успешно удалось поставить МегаполОС на тестовой сервер, и благодаря ему развернуть на сервере несколько контейнерных приложений, подключить к ним БД, домены и SSL-сертификаты. Конечно, продукт еще сыроват, но реализуемый им способ управления инфраструктурой кластера выглядит проще и нагляднее, чем настройка ansible или kubernetes.

Текущий интерфейс Мегаполоса. Он очень сырой и будет полностью переработан, но дает представление о сущностях и операциях, с которыми он работает:

 

Итоги

Поскольку основной функционал МегаполОСа уже работает, мы решили опубликовать его в Open Source под лицензией Apache 2.0 и вести дальнейшую разработку открыто: https://gitlab.com/megapolos

Если вас заинтересовал этот проект и вы хотите с этим помочь – присоединяйтесь к нашему сообществу: https://t.me/megapolos

 

Комментарии (12)


  1. lagger
    19.06.2023 09:29

    А как насчёт воспроизводимости настроенного окружения? (Например надо развернуть точно такой же кластер, но на других серверах)


    1. freehabr Автор
      19.06.2023 09:29

      Эта функциональность тоже планируется. Вплоть до возможности переноса тестового окружения развернутого на локалхосте на любой реальный сервер.


  1. Win32Sector
    19.06.2023 09:29
    +5

    Разработчики снова придумали себе способ натыкать себе руками снежинку


  1. Thomas_Hanniball
    19.06.2023 09:29
    +3

    Мы хотели навести порядок со своими проектами


    Мы решили использовать инструменты DevOps


    Мы пишем свою ОС для контейнеров, которую назвали МегаполОС.

    Кажется, здесь что-то пошло не так. :)


    P.S. Касательно самой идеи ОС для контейнеров. Посмотрите в сторону СoreOS и RancherOS. Возможно, это то, что вы давно искали.


    P.P.S. полОС или полуОС — это название ассоциируется с OS/2 — операционной системой фирмы IBM.


    1. freehabr Автор
      19.06.2023 09:29

      Мы смотрели существующие решения. Они не автоматизируют полностью жизненный цикл приложения.


  1. nnstepan
    19.06.2023 09:29
    -1

    25 лет в айти и последние несколько лет тоже не могу понять, как айти свернуло не туда, почему в 21 веке надо писать ямл файлы и знать и помнить кучу не особо полезной информации. Напоминает мою первую сборку ядра в слаке году в 97. Так что мнение автора разделяю полностью.


    1. freehabr Автор
      19.06.2023 09:29

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


    1. dobropalka
      19.06.2023 09:29

      Ну, а какие варианты?

      комплексность систем возросла, количество слоев абстракций - тоже.

      Или вы пишете ямлы, или bash с .ini - принципиально разницы то нет


      1. freehabr Автор
        19.06.2023 09:29

        Пора перейти на следующий уровень абстракции.


  1. Strangerx
    19.06.2023 09:29
    +5

    На самом деле вы изобрели.. Internal Developers Platform Webhosting!

    На скриптах, как в свое время всякие ISPmanager’ы и cPanel VestaCP.

    Вопрос: зачем?

    Есть кубер, да и другие оркестраторы, которые отлично справляются со своей задачей.

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

    И ведь идея-то правильная, обучать разрабов, тем более сторонних, ямлу, хелму и куберу, не нужно, а может и вредно.

    Но реализация.. не стандартная, мягко скажем ;)

    Зачем уходить от стандартов?

    Зачем решать то, что уже решено?

    Зачем использовать технологии, от которых отказывается сообщество (docker)?

    Эти вопросы не требуют ответов, но стоит хорошенько подумать :)

    Удачи!


  1. MrFrizzy
    19.06.2023 09:29
    +2

    Может вам стоит попробовать другие оркестраторы вместо k8s\swarm, прежде чем дальше писать свой?
    - https://juju.is/
    - https://www.nomadproject.io/ + https://www.consul.io/


    1. freehabr Автор
      19.06.2023 09:29

      Мы смотрели существующие решения. Они не автоматизируют полностью жизненный цикл приложения.