Миграция на новую версию Elasticsearch с нулевым временем простоя
Доводилось ли вам переводить крупную систему на современную версию без потери данных и простоя? Рассматриваем пример реального проекта на Elasticsearch.
В этом туториале вы узнаете,
как перейти на новую версию Elasticsearch. В рассматриваемом проекте осуществлялся переход с 1.6 на 6.8, но общие идеи справедливы и для других версий. Конечно, требовалось не просто перейти, но и соблюсти ряд жёстких ограничений: нулевое время простоя, отсутствие ошибок и потерь данных.
1. Зачем понадобился перенос данных?
В рассматриваемом примере проекта были следующие проблемы, повлиявшие на необходимость переноса:
Проблемы с производительностью и стабильностью – наличие большого количества перебоев с длительным MTTR. Это отражалось в частых задержках и высокой загрузке процессора.
Отсутствие поддержки для старых версий Elasticsearch.
Негативное влияние dynamic mapping на работу кластера.
Нехватка инструментов экспорта и метрик, встроенных в новую версию.
2. Рассмотрим условия
Какие условия нужно было выполнить:
Миграция с нулевым временем простоя. Поскольку в обновляемой системе имеются активные пользователи, нельзя допустить падения системы.
План восстановления. Нельзя потерять или испортить данные, независимо от их ценности. Нужно иметь план восстановления на случай, если что-то пойдёт не так.
Отсутствие ошибок. Для конечных пользователей привычные функции поиска не должны измениться.
3. Обдумаем план
Ошибки.Для удовлетворения требования по отсутствию ошибок необходимо изучить все возможные результаты и запросы, которые
получает работающая система, а также обеспечить тестами ответственные куски
кода. В качестве проверки
результата нужно добавить метрики для отслеживания задержки, пропускной
способности и производительности, чтобы проверить динамику.
План восстановления. На данном этапе
подготовьтесь к ситуации, когда сервис будет работать не так, как ожидалось. В
общих чертах нужно создать копию кластера и перенести на него инфу незаметно
для юзеров.
Миграция с нулевым
временем простоя. Работающий сервис
всегда онлайн и не может быть недоступен более 5-10 минут. Чтобы сделать всё правильно, поступаем так:
Храним логи всех выполняемых действий (в продакшене используется Kafka).
Запускаем процесс миграции в офлайне с отслеживанием смещения с момента начала миграции.
Когда миграция завершится, запускаем новую службу, учитывая логирование и «догоняем» отставание.
Когда отставание сведено к нулю, изменяем версию фронтенда.
4. План действий
Текущий сервис имеет
следующую архитектуру:
Event topic содержит события, созданные другими приложениями (например, UserId 3 created);
Command topic содержит трансляцию этих событий в конкретные команды, используемые приложением (например: Add userId 3);
По плану необходимо
добавить еще одного клиента (new Indexer) для Command topic, который будет параллельно читать и
записывать данные в Elasticsearch 6.8.
С чего начать?
Вот несколько полезныхвещей,
которые помогут:
Документация. Найдите время, чтобы прочитать о Mapping и QueryDsl.
API. Всё крутится на CAT API. Это очень полезный инструмент для локального дебага и проверки ответа от Elastic.
Опишем подробнее пример
использования, вот наша модель:
Пример сообщения по
вышеописанной схеме:
Эта модель поддерживает
запросы по значению, а также по тегу со значением. Динамический шаблон вызывал постоянные
сбои. Схема выглядела так:
Предположительно, причина сбоев состояла в использовании вложенных документов.
В обновлённой схеме
были применены следующие запросы:
Количество документов в
кластере составляло около 500 млн, с использованием вложенных документов в
новой схеме цифра может вырасти до 250 млрд документов. Этот пост
объясняет, что всему виной проблемы с использованием кучи, так как она может
вызвать высокую задержку в запросах.
Как уйти от вложенных
документов. Создаётся поле,
содержащее комбинацию ключа и значения, и всякий раз, когда пользователю требуется
совпадение ключа и значения, его запрос транслируется в соответствующий текст.
6. Миграция
Для миграции потребуется:
Перенести данные из старого Elastic в новый.
Закрыть отставание между началом миграции и её окончанием.
С первым всё понятно, а
вот решения второго шага:
Система основана на Kafka, поэтому используем текущее смещение до начала миграции, а после завершения миграции переносим данные из точки смещения. Это решение подразумевает гору ручного труда.
Другой подход к решению этой проблемы – начать перенос сообщений из Kafka и сделать все действия в Elasticsearch неизменяемыми, то есть если изменение уже было «применено», в Elastic store ничего не изменится.
Второй вариант более
предпочтителен, так как не требует ручной работы.
Перенос данных
Среди массы вариантов переноса данных самый подходящий основан на передаче сообщений между старым и новым Elastic.
Для этого создаём скрипт на Python, который будет ходить на старый кластер, таскать оттуда
инфу и параллельно преобразовывать её под новую схему.
Заключение
Перенос данных в живом
продакшене – сложная задача, требующая большого внимания и тщательного
планирования. Мы рекомендуем потратить время, проработать шаги на тестовых
машинах и выяснить, что лучше всего подойдёт для вашей конкретной ситуации.
Всегда старайтесь
максимально снизить влияющие факторы. Например, требуется ли нулевой простой
или можно “тормознуть” некоторый функционал сервиса? Не станет ли потеря данных
проблемой?
Обновление
любого хранилища с данными обычно является не спринтом, а марафоном, поэтому
сделайте глубокий вдох, запаситесь попкорном и наслаждайтесь поездкой.