Нынче в каждой приличной организации, разрабатывающей серьезное программное обеспечение, принято делиться, какими путями создавались и развивались ее проекты. Мы считаем это отличной тенденцией и готовы поведать свой вариант развития одного из внутренних проектов компании «СБИС». Он влияет самым серьезнейшим образом на все ее остальные продукты, и его ласково называют — «Хоттабыч», ибо делает волшебство!
Каждые 100 секунд он обновляет какое-нибудь приложение в боевом или в тестовом окружении. Приложений у нас только в «продакшн» около 200, а на тестовых стендах — больше 1000. Количество виртуальных серверов, на которых развернуто каждое приложение – от двух до нескольких сотен. Итак, по порядку…
До Большого взрыва
Online CБИС вышел недавно — в 2010 году. До этого мы были как все приличные приложения — desktop. Сначала жили под DOS, потом под консолью Windows, затем стали полноценным Windows приложением, ну все как у людей. Данные клиентов хранили либо в СУБД «Pervasive», либо в базе собственного изготовления с загадочным названием «Muzzle». Обновлением своего СБИС тогда занимались сами клиенты. Самым сложным в обновлении была конвертация базы. Для этой операции использовалась специальная утилита Jinnee.
Jinnee — был умницей. Он брал новое описание структуры базы из дистрибутива, автоматически вычислял разницу между старой и новой структурой и накатывал ее на базу. В процесс конвертации можно было вмешиваться с помощью механизма конвертеров. Нужный конвертер срабатывал только для той версии, для которой был написан. Идеи, заложенные тогда в Jinnee, были так хороши, что сохранились и по сей день, как и сам Jinnee. Его аналогов в программной индустрии мы не смогли обнаружить, а потому с гордостью считаем его уникальным.
Большой взрыв вот-вот начнется
Внезапно в 2010 году СБИС стал web-сервисом под IIS и сменил хранилища данных на PostgreSQL. Клиентам от этого, конечно, стало проще — у них уже не болела голова об обновлении, а мы еще не осознавали, во что вляпались и пока по старинке обновляли свой web-сервис, как это делали с desktop-приложением. Все так же ручками запускали Jinnee, научив его конвертировать PostgreSQL. И как-то это не напрягало, сервис был практически один, база одна, online-клиентов еще было немного. Все усилия были направлены в тот момент на совершенствование конвертации PostgreSQL с помощью Jinnee.
Но скоро web-сервисы стали плодиться, и число клиентов стремительно выросло до 300 тыс. Нужно было обновлять уже десяток сервисов, располагавшихся на полусотне серверов. Вручную это делать невыносимо, да и обновления проходили по ночам в выходные. Тогда еще во время обновления клиенты работать не могли, получая парковочную страницу с текстом «Извините, приложение временно недоступно, идут технические работы, ожидаемое время разблокировки 6:00».
Налицо нарисовались три проблемы:
- исключить ручную работу при обновлении, а значит, ускорить саму работу и уменьшить ошибки из-за человеческого фактора;
- минимизировать простои клиентов во время обновления;
- дать выспаться командам.
Тут нужно немного рассказать о нашей архитектуре хранения данных клиентов, чтобы дальнейшая часть стала понятной.
Данные клиентов мы храним в базах Postgres. К одной базе обычно прикреплено 1.5 тыс. клиентов. Внутри базы данные каждого клиента хранятся в отдельной схеме.
Такой необычный способ хранения дает нам следующие плюшки:
- можно конвертировать данные одного клиента независимо от другого;
- можно легко перегонять данные клиента из одной базы в другую;
- клиент никогда не сможет увидеть чужие данные.
Когда клиент выполняет запрос, то сначала попадает на диспетчер. Диспетчер играет роль балансировщика и маршрутизатора, он реализован на основе nginx. Вычислив маршрут клиента, он бросает его на один из серверов бизнес-логики группы, в которой находится база клиента. Бизнес-логика, если нужно, уже обращается к нужной базе, внутри базы адресуясь к схеме клиента. И это все.
Большой взрыв начинается
К концу осени 2012 года мы задумали новый сервис для реализации обновления «с человеческим лицом». Сформировали команду из трех человек, и, помолясь, приступили…
Архитектура системы обновления сложилась сразу из следующих компонент:
- управляющий обновлениями сервис;
- сервис метаинформации и конфигурации сервисов облака;
- диспетчер;
- агенты обновления;
- хранилище дистрибутивов приложений.
Управляющий сервис получил название «Хоттабыч», это также была аллюзия на утилиту «Jinnee», умевшую конвертировать базы.
Сервис метаинформации и конфигурации сервисов облака был у нас с самого начала и неплохо функционировал, мы называем его «Управление облаком». Для нужд обновления его задача — выдать информацию, где топологически расположены роботы, web-сервисы и какие у них настройки. Именно он командует диспетчерами, чтобы они выдавали парковочные страницы о недоступности сервисов. Использование диспетчеров изначально задумывалось как простых балансировщиков http-запросов, но потом их функции стали существенно шире.
Агенты обновления(агенты Хоттабыча) — это службы на хостах сервисов, призванные как раз выполнять команды на их обновление.
Хранилище дистрибутивов — название говорит за себя. Дистрибутивы тоже существовали к этому моменту, не было только хранилища.
Взаимодействовать это примерно должно было так:
- Хоттабыч получает команду на обновление приложения на нужную версию;
- Обращается в сервис управления облаком за топологией сервиса;
- Скачивает на расшаренный диск дистрибутив нужной версии;
- Делит все сервера сервисов пополам — одна часть будет обновляться, пока вторая будет работать;
- Рассылает команды агентам первой части серверов на обновление с указанием, где лежит дистрибутив;
- Агенты закачивают себе дистрибутив, распаковывают его и меняют старую версию на новую
- Затем управляющий сервис делает то же самое для оставшейся части серверов;
- Когда все агенты отчитались об окончании работы, управляющий сервис переходит в состояние ожидания подтверждения обновления, ведь его могут и откатить.
Оставалось разработать сам сервис с интерфейсом, агентов обновления и организовать хранилище дистрибутивов с выкладкой туда.
Первое детище..
На первой итерации нам требовалось только обновление без конвертации данных. Хранилище дистрибутивов решили делать на SVN — вот дураки!
Структурно организация хранилища была иерархической:
дистрибутив такой-то
|- версия такая-то
|- сам дистрибутив + файлы информации о нем
|- версия сякая-то
|- сам дистрибутив + файлы информации о нем
Хоттабыча решили писать на своей же платформе СБИС. Как раз это мы хорошо умели делать, тут сомнений не было. С агентом обновления были сомнения, но в итоге его тоже стали делать на платформе СБИС (это С++). Для агентов было поставлено жесткое условие — они должны регулярно и максимально информативно отчитываться Хоттабычу о своей работе.
Обновление разбили на три основные фазы:
1. Фаза подготовки — во время нее доставляются файлы дистрибутивов на хосты приложений, делаются различные подготовительные действия, которые никак не нарушают работы сервисов.
2. Фаза обновления — на ней, собственно, выполняется обновление, вот здесь работа сервиса как раз иногда может быть приостановлена.
3. Фаза подтверждения — здесь доделываются второстепенные задачи обновления, зачищаются ненужные файлы, и т.п. Сервисы в этот момент уже работают.
Весь процесс контролируется с фронтенда ответственным, и его всегда можно приостановить, продолжить или откатиться. По каждому узлу обновления во фронтенде можно увидеть, в каком состоянии он находится, что на нем происходит, происходило, а может уже и все…
Большой взрыв состоялся
Наконец, весной 2013 мы смогли провести первое обновление в боевом окружении, затем второе, третье, и это так всем понравилось, что на радостях обновления стали делать днем в рабочее время. Такой вид обновления мы назвали «легким» т. к. оно проходило без особых проблем для всех — как для нас, так и для клиентов. Это был прорыв!
Вселенная расширяется
После первых успехов у нас вылезла первая проблема. На тестовых стендах дистрибутивы загружали в хранилище так часто и размера до 1.5Гб, что SVN затрещал по швам. Конечно, он не годился для хранения больших бинарных файлов, и мы быстренько ушли на хранение дистрибутивов на linux-сервере. Тоже так себе решение, т. к. пришлось с windows-серверов держать шару на linux-диск, но стало уже намного лучше.
Впереди ждало обновление сервисов с конвертациями баз. В этой итерации Jinnee, умевший конвертировать базы, доставлялся агентом на один из серверов обновляемого. Агент его запускал, командуя, какую базу конвертировать.
При конвертации баз, к сожалению, работать с ними затруднительно. Работу сервиса нужно приостанавливать. Однако, клиенты на фронтендах не должны пугаться, и для них в этот момент появляется парковочная страница. На ней, обычно, сообщается, что происходит сейчас, какие новые функции будут после обновления, а после окончания парковочная страница снимается.
Летом 2013 года мы выкатили в рабочее использование уже и этот вариант обновления. И все бы было неплохо, но у нас есть некоторые сервисы, работающие не с одной базой, а с множеством, например с сотней. И когда вся эта куча баз начинала одновременно конвертироваться, на хранилища ЦОД прилетала приличная нагрузка. Пришлось делать конвертацию не такой агрессивной, и не всего сразу. Так, на текущий момент мы никогда не конвертируем сразу всех клиентов: обычно конвертируется одна группа клиентов, через несколько дней — другая.
И вновь о конвертации баз
Базы клиентов конвертировались подолгу — несколько часов. Это нас совсем не устраивало. Мы в состоянии конвертировать данные одного клиента в базе независимо от данных другого. Тогда клиент вместо часов простоя может получить приостановку работы от нескольких секунд до нескольких минут. И нужно быть очень несчастливым, чтобы нарваться на эти секунды. При таком подходе часть клиентов во время обновления в базе будет жить на старой версии данных и обслуживаться старой логикой, а другая часть — на новой версии данных и обслуживаться новой логикой. Обновляемый клиент должен просто ждать.
Остановились на следующем решении. Предварительно фронтовые диспетчера переводятся в режим «поклиентного обновления». Это значит, что на каждый запрос от клиента они обращаются в Redis за информацией, в каком состоянии клиент: обновлен, не обновлён или еще обновляется. Информацию туда выкладывает Хоттабыч, когда принимает решение об обновлении клиента. Если клиент не обновлен, диспетчер бросает запрос на пул не обновленных серверов; если обновлен, то на пул обновленных серверов; а если обновляется, выдает клиенту парковочную страницу.
Этот вариант мы выкатили в «продакшн» в конце 2014-го. Он так прижился, что стал практически основным вариантом конвертации. Конечно, не все наши базы можно так конвертировать, но не все наши базы конвертируются так долго.
Сейчас у нас более 1 млн. клиентов, и они располагаются более чем на 650 базах. И конвертации проходят достаточно незаметно.
И пришел Linux …
Годы эксплуатации сервисов под IIS, привели нас к устойчивой мысли — IIS нам не нужен, да и Windows тоже, т.к., по сути, стек технологий Microsoft мы не использовали. А под Linux меньше накладных расходов на серверную ОС, лучше оптимизируется код на С++, больше выбора серверного оборудования, например, IBM Power, и т.п.
И вот в 2015 году наступило крупное событие. Команда, разрабатывающая ядро наших сервисов, подготовила почву для миграции на Linux, и миграция началась. Мы этим воспользовались для отказа от SSH при обновлении сервисов на Linux и портировали на него агента Хоттабыча.
Тут другая наша команда тоже сделала прорыв и разработала аналог Google Диска — СБИС Диск. Мы и этим воспользовались. Отказались от хранения дистрибутивов на выделенном Linux-сервере, стали хранить их в СБИС Диске. Процесс скачивания дистрибутива резко упростился и ускорился. Теперь каждый агент Хоттабыча по команде напрямую из СБИС Диска весело качал нужный дистрибутив. Мы вздохнули свободнее, и системные администраторы тоже задышали, но не так свободно, как мы.
И снова возвращаемся к конвертации баз
Простой клиента мы минимизировали и это здорово. Но все-таки как его вообще убрать? Частенько конвертацию можно проводить, вообще не останавливая работу базы. Например, добавляя индекс или какое-нибудь поле. Мы назвали такую конвертацию «легкой». Но вот вопрос, а как узнать можно ли «легкую» конвертацию проводить? На это нам дает ответ все та же чудодей-Jinnee. Ему на вход подается новый дистрибутив и базы-кандидаты на конвертацию, он по ним проводит анализ ожидаемых изменений и выдает рекомендации, какую конвертацию проводить.
Увы, неизбежны ситуации, когда базу нужно таки конвертировать с остановкой. Однако многие базы в основном работают на чтение, и если конвертация ожидается недолгой, то можно на время конвертации завернуть все запросы на чтение на реплику базы, а по окончании конвертации переключить на мастер. Запросы на изменение данных в этом случае будут отброшены с ошибкой. Оказалось, такой вариант очень подошел. Некоторые критичные сервисы конвертируются теперь всегда в таком режиме, продолжая отвечать на большинство запросов.
О патчах
Часто в работе сервиса обнаруживается ошибка и ее исправление нужно получить как можно быстрее. Правка обычно небольшая — один файлик, а сборка нового дистрибутива может быть достаточно долгой, да и нет в этом необходимости. Такой вид обновления мы назвали «патчем». Исправленный файл передается Хоттабычу, он заменяет его в существующем дистрибутиве, поднимая его версию, и накатывает на сервис. Все происходит очень быстро.
Упорядочение неупорядоченного
Очень часто вообще не требуется обновлять код сервиса и структуру данных его баз. Нужно просто единожды поменять данные. Раньше это делали скриптами и передавали их администраторам ЦОД на исполнение. Те их запускали ручками и глазками следили за работой. Скрипты множились, в день их могло поступать от 10 до 20. Многие нужно было выполнять на нескольких сотнях баз. Признаки нарастающего бардака налицо.
В итоге, мы остановились на таком решении. Разработчик реализует логику скрипта в виде обычного кода запроса, исполняемого на сервисе. Так он получает всю внутреннюю инфраструктуру сервиса. Хоттабыч доставляет код запроса на сервис и вызывает его, отслеживая исполнение на каждой из баз сервиса. Администратору ЦОД остается только нажимать кнопку в интерфейсе Хоттабыча, всю остальную логику он берет на себя. Сейчас это самый популярный вид обновления.
После большого взрыва
Хоттабыч продолжает развиваться, переход на Linux открыл нам новые возможности, на очереди контейнеризация наших сервисов. Также мы планируем добиться, чтобы клиенты даже при агрессивной конвертации не получали минимальных простоев в работе. Из-за внедренной сети агентов Хоттабыча на сервера нашего ЦОД, мы получили дополнительные преимущества на которые сразу не рассчитывали. Например, агенты Хоттабыча теперь не только обновляют, но и выполняют перезагрузки процессов сервисов по требованию, конфигурационные задачи, некоторый мониторинг.
Сейчас в «хозяйство» Хоттабыча входит ~ 5 тыс. серверов, и он с этим неплохо справляется. Число баз, которые периодически конвертируются ~ 2 тыс. Все это обновляется с минимумом простоя для более чем 1 млн. клиентов вот уже длительное время.
Автор: Алексей Терентьев
Автор: tensor_sbis