Перенести терабайты канбан-досок в облако: опыт команды Yandex Tracker
Привет, меня зовут Мария Карпенко, я разработчик в команде Yandex Tracker — сервиса для управления процессами и проектами. Внутри Яндекса сервис используется для постановки задач практически во всех командах, так что общее количество событий по задачам исчисляется уже миллиардами.
Как внутренний сервис Tracker существует с 2012 года, и старые инстансы исторически использовали базы данных on‑premise. Но к 2023 году многие части даже из списка легаси должны были переехать в облако — и нам понадобилось продумать бесшовный переезд для достаточно объёмных БД.
В этой статье расскажу, как мы решили эту задачу, — рассказ будет интересен всем, кто планирует переезд в облачную инфраструктуру.
Предпосылки переезда: что перевозим и зачем
Как мы уже рассказывали в статье про Yandex Tracker, эта разработка изначально появилась как внутренний инструмент для рабочего взаимодействия, планирования и ведения внутренних проектов. Через 5 лет Tracker стал внешним сервисом.
Основным хранилищем данных в Tracker исторически была база данных MongoDB. Инстансы внешнего облачного сервиса уже сразу используют облачную базу данных, которую предоставляет сервис MDB. А вот некоторые старые инстансы внутри компании использовали on‑premise‑инсталляцию базы.
К 2023 году некоторые из таких «железных» баз стали достаточно объёмными. Например, база для инсталляции Яндекса весила около 1 TiB, содержала 100 млн задач, 250 млн комментариев и около 1 млрд событий по задачам. База нашего самого крупного внешнего клиента была примерно в 2,5 раза больше.
Чем больше данных, тем сложнее управлять кластером, сложнее его масштабировать. На команде Yandex Tracker полностью лежала поддержка кластера: бэкапы, масштабирование, мониторинг, обновление версии кластера.
Поломка реплики могла привести к необходимости наливать данные заново, — чем больше база, тем дольше и сложнее сходится этот процесс. Если бы мы решили шардировать такую базу, то управлять пришлось бы уже не 3–4 хостами, а на порядок больше.
В инсталляции MDB поддерживать базу стало бы проще: восстановление из бэкапа «по кнопке» в интерфейсе, а замена упавших реплик полностью автоматическая, можно и не заметить, что с кластером были какие‑то проблемы.
Поэтому у нас назрел переезд с «железных» инсталляций, который позволил бы решить сразу несколько задач:
Передать часть работы по поддержке кластера команде облака и за счёт этого снизить трудозатраты.
Облегчить задачу по будущему масштабированию кластера.
Сформировать единый тип источников для всех инстансов приложения: так не нужно следить за зоопарком инсталляций, что упрощает поддержку и разработку.
Подготовка к переезду
Итак, нам было необходимо быстро и безопасно перенести данные между двумя источниками одного типа и плавно переключить приложение. Первое, с чем важно было определиться, — инструмент для переезда. Выбор сводился к трем вариантам:
Найти инструмент в открытых источниках: это может быть быстрее, но не факт, что надёжно. Каждый найденный инструмент всё равно придётся тестировать и проверять по критерию безопасности. В этом контексте мы рассматривали такие инструменты, как py-mongo-sync, mongo-migration-stream и другие.
Написать собственный мигратор под конкретную задачу. Здесь точно будем уверены в безопасности инструмента, но потребуется время.
Использовать Yandex Data Transfer — облачный сервис для логического переноса данных между СУБД, объектными хранилищами и брокерами сообщений, который также находится в облачной инсталляции. Этот вариант безопасный и требует от нас наименьших усилий для использования.
Мы выбрали третий вариант. Помимо указанных причин, это возможность следовать практикам догфудинга и извлечь дополнительную пользу для наших продуктов.
В общих чертах схема переезда с помощью Transfer была простой: для переноса данных готовим облачный кластер MDB в качестве эндпоинта‑приёмника, а «железный» кластер используем в качестве эндпоинта‑источника. Далее создаём трансфер в режиме копировать + реплицировать из источника в приёмник и дожидаемся, пока все данные скопируются в облако.
Но за простотой схемы скрывалось несколько задач:
важно правильно организовать и спланировать весь процесс переезда по шагам, чтобы даунтайм был минимальным;
необходимо перенести данные без потерь и затем поддерживать базу в актуальном состоянии до финального переключения;
нужно сразу продумать параметры кластера в облаке, чтобы не столкнуться с уже известными проблемами (а по возможности — и с неизвестными тоже).
Пойдём по порядку.
Как организовать переезд по шагам
Мы зафиксировали все шаги переключения и согласовали их с командой. Чтобы чётко сформулировать каждый пункт и потренироваться, мы переключали тестовый кластер приложения несколько раз. Также на каждом шаге предусмотрели возможность отката в исходное состояние.
Наиболее подходящим для переключения было время 00:00, когда сервис нагружен относительно мало, а необходимые для переключение 15–20 минут нанесут наименьший ущерб рабочим процессам. Всех пользователей мы предупредили о времени начала работ через уведомление в интерфейсе приложения и объяснили, что в это время сервис будет доступен только в режиме readonly.
Вот какие этапы переключения на новую базу мы описали в плане:
Мы создаём Трансфер 1 в режиме копировать + реплицировать и дожидаемся завершения стадии копирования, чтобы трансфер перешёл в состояние репликации. Это может занять время: база размером 100 ГБ обычно копируется 2–3 часа, в зависимости от сетевой скорости.
Когда Трансфер 1 перешёл в состояние репликации, мы останавливаем запись в базу-источник: для этого отбираем у приложения права на запись.
Теперь дожидаемся, когда все новые изменения будут реплицированы в облачную базу. Это удобно делать на вкладке Мониторинг по графикам, которые предоставляет сервис Data Transfer.
После того, как репликация завершена и базы приведены к одинаковому состоянию, останавливаем Трансфер 1:
Включаем обратный подстраховочный Трансфер 2 в режиме репликации.
Такой трансфер позволит переключиться на старую базу без потери данных в случае проблем с новой базой.
Перезапускаем приложение, указав в качестве источника данных облачный кластер MDB.
А что с бэкапами?
В течение нескольких месяцев после переезда в облако кластеры MongoDB размером более 300GiB были доступны под специальным флагом, но бэкап такой базы не гарантировался. Для инстанса Yandex Tracker мы выделили кластер на 2TiB, что заметно превышало рекомендуемые размеры. Поэтому мы не выключали обратный трансфер: место на «железном» кластере ещё оставалось и бэкапы работали на нём.
Эти бэкапы оставались в качестве подстраховки, пока мы мы не совершили ещё один переезд, но уже внутри облака — на шардированный кластер. Этот переезд тоже требовал подготовки с учётом особенностей и ограничений шардирования.
Как масштабировать кластер
Выбор MongoDB в качестве основного хранилища данных позволяет гибко масштабировать кластер с помощью шардирования, к которому мы начали готовиться сразу после переезда в облако.
Шардирование подразумевает распределение данных одной коллекции на несколько физических узлов, каждый из которых представляет отдельный инстанс MongoDB и может быть реплицирован. Какие преимущества даёт шардирование:
Масштабируемость. При росте объёмов базы можно как увеличивать размер диска, так и добавлять новые шарды в кластер.
Производительность. Большое число узлов позволяет распределять нагрузку, тем самым повышая производительность.
Отказоустойчивость. Выход из строя одной или даже нескольких нод может пройти незаметно для кластера и не повлиять на его работу.
В MDB шардирование доступно из коробки, достаточно создать кластер, изменить его тип и добавить нужное количество шардов.
Оставалось определиться с коллекциями, которые мы будем шардировать, а также ключами для их шардирования. Мы выбрали самые большие коллекции таким образом, чтобы они с запасом поместились на шарды размером 300GiB.
Для выбора ключей шардирования мы ориентировались на статистику использования индексов (MongoDB предоставляет такую информацию) и бизнес‑логику приложения. Также при выборе ключей шардирования важно внимательно читать документацию: есть особенности и ограничения для работы с шардированными коллекциями. Например, в шардированных коллекциях нельзя использовать вторичные unique-индексы, на которые у нас была завязана определенная логика. Нам потребовалось внести значительные изменения в код приложения, чтобы сохранить логику и удалить несовместимые индексы из коллекции.
В марте 2023 года по отработанной схеме мы перевезли наш самый большой инстанс с «железного» кластера сразу же на шардированный.
Что доработали вместе с командой Data Transfer
Как я уже упоминала, наш переезд потребовал доработок инструмента миграции под задачу. Мы приносили команде Data Transfer необходимые для переездов фича‑реквесты.
Это помогло упростить будущие миграции клиентов. Например, кластер внешнего клиента, который мы готовили к аналогичному переезду, вырос настолько, что мы были сильно ограничены в возможностях увеличения размера оплога (так называется журнал изменений в MongoDB). Необходимо было перевезти данные достаточно быстро, чтобы успеть до ротации оплога. Здесь нам очень помогла возможность переноса больших коллекций в несколько потоков — за счёт доработок Data Transfer копирование сошлось за пару дней, и трансфер успешно перешёл в репликацию.
Итоги
Из‑за размеров перевозимых баз и требований к непрерывной работе сервиса, наш переезд стал вызовом не только для команды Yandex Tracker, но и для активно участвовавших в процессе сервисов — Data Transfer и MDB. Мы смогли выявить и решить несколько проблем и тем самым повысили удобство использования наших сервисов для внешних клиентов.
Разработчики Yandex Tracker успели оценить удобство managed‑кластера MDB: мы уже несколько раз меняли параметры кластера, обновляли версию, увеличивали размер отдельных инстансов и уровень репликации. А для инстанса приложения внешнего клиента удалось поднять лимит RPS на 40%, чего нам не позволяла сделать предыдущая «железная» база.
Описанная в статье схема подходит для миграции и более классических транзакционных баз данных, таких как PostgreSQL. Будет интересно, если в комментариях поделитесь своими историями переезда в облако или задачами с нестандартными сервисами или типами хранилища.