«Канарейка» для данных: как мы добавили безопасную доставку снапшотов поверх оркестратора снапшотов


Привет, Хабр! Меня зовут Артём Букин, я разрабатываю инфраструктурные проекты в VK, и сегодня продолжу рассказывать как мы повышаем надежность ядра подбора VK Рекламы. В этой статье разберем как мы построили в облачной инфраструктуре канареечное развёртывание снапшотов данных в эксплуатацию.
Канареечную выкладку обычно используют для обновления кода: новую версию приложения сначала проверяют на небольшой части серверов или пользователей, а затем распространяют на остальных.
Но поведение сервиса зависит и от данных. Приложение может остаться прежним, а обновлённые конфигурации, справочники или модели — заставить его работать иначе. Данные умеют менять поведение сервиса не хуже нового релиза кода, и готовиться к их обновлению стоит так же серьёзно: проверять не только то, что снапшот собран, скачан и распакован, но и то, что сервис с новыми данными корректно подбирает рекламу.
Поэтому поверх существующей системы доставки оркестратора снапшотов мы решили сделать «канарейку» для данных.
Что такое «канарейка» и где её используют?
«Канарейка» — способ безопасно проверить изменения на небольшой части работающей системы перед их массовым применением. Слово пришло от шахтеров, которые брали под землю канареек: птицы раньше людей реагировали на опасный газ и предупреждали об угрозе.
В информационных системах ранние сигналы о возможных изменениях ловят с небольшой части рабочей среды, которая работает под реальной нагрузкой.
Сначала изменение проверяют на ограниченной группе экземпляров, пользователей или запросов — отслеживают технические и продуктовые метрики работы сервиса. Если показатели остаются в норме, выкладку продолжают. Если выходят за допустимые границы, группу откатывают к предыдущей версии.
Канареечный подход применяют не только к коду, но и к конфигурации инфраструктуры, правилам маршрутизации, feature flags, моделям машинного обучения, поисковым индексам и справочникам. Принцип один: проверить изменение под реальной нагрузкой и только после этого продолжить выкладку.
«Канарейка» вовремя обнаруживает некорректную работу сервиса, но не отменяет остальные меры предосторожности. Для полноценной «канарейки» нужны три условия:
действительно репрезентативная группа;
метрики, чувствительные к отклонениям в работе сервиса и отражающие любую значимую динамику;
возможность откатиться.
Почему «доставлено» не всегда означает «безопасно»?
Рассмотрим корнер-кейс, в котором данные формально будут корректными, успешно собираются и без ошибок проходят весь транспортный контур, но после применения меняют поведение сервиса. При этом серверы продолжают отвечать, а отклонение заметно только по продуктовым показателям .
Такой кейс позволяет обнаружить разрыв между технической доставкой и смысловой корректностью данных. Система подтверждает, что файл дошёл и загрузился, но до массовой раскладки не может ответить на более важный вопрос: продолжает ли сервис с этим файлом выдавать ожидаемый результат.
Мы сразу расширили инструменты реагирования: добавили возможность останавливать раскладку, принудительно пересобирать снапшоты и возвращаться к предыдущей версии, а заодно донастроили алерты под этот сценарий. Обнаружение отклонений и быстрая реакция команды закрывали большую часть сценариев. Следующим шагом стала превентивная защита: не распространять новые данные на весь прод, пока они не проверены на небольшой группе реальных экземпляров.
Что такое снапшот и почему он похож на релиз?
Снапшот — подготовленный слепок данных, который сервис может быстро загрузить в память и использовать при обработке запросов. Сервису подбора рекламы не нужно при каждом запросе обращаться за всеми настройками к основной базе данных. Отдельный компонент, Seeder, периодически собирает из базы согласованный набор данных, упаковывает его и передаёт в контур доставки.
Мы разделили данные на две части:
глобальный снапшот — содержит базовые данные сервиса;
снапшот кампаний — содержит данные рекламных кампаний.
Такой подход локализует возможные нарушения — они не затронут весь набор, но это не гарантирует корректность обновления данных рекламных кампаний. Если такой снапшот автоматически расходится на все машины, изоляция помогает локализовать тип данных, но не ограничивает масштаб воздействия.
По своим характеристикам снапшот обладает всеми признаками релиза:
Меняется поведение системы.
Изменение распространяется сразу на множество экземпляров.
Эффект от изменения проявляется только на реальных запросах и становится заметен по продуктовым метрикам .
Поэтому есть простое правило: если данные меняют поведение рабочей среды, их нужно выкатывать с теми же предохранителями, что и код.
Как работала доставка через оркестратор снапшотов?
После перехода на систему доставки и оркестрации снапшотов до появления «канарейки» путь снапшота выглядел так:
Сервис сборки снапшотов за 30–40 минут формировал снапшот из базы данных.
Готовый файл загружался в S3.
Метаданные публиковались в индекс снапшотов с определённым type_id — логическим именем потока данных.
Мастер оркестратора снапшотов находил новую версию и сообщал экземплярам, подписанным на этот тип данных, что её нужно скачать
Инстансы загружали данные. Для ускорения они могли обмениваться ими по peer to peer.
Мастер оркестратора снапшотов давал команду применить новую версию.
Оркестратор снапшотов уже решал сложную транспортную задачу: хранил информацию о версиях, находил потребителей, организовывал скачивание и применение. Поэтому строить рядом ещё одну систему доставки означало бы дублировать уже отлаженную логику и тратить дополнительное время.
Нужно было добавить в существующий процесс точку принятия решения: когда конкретный снапшот можно считать безопасным для массовой раскладки.
Какие подходы обсуждали?
В ADR рассматривали два варианта.
Первый вариант — напрямую управлять группами раскладки через API оркестратора. Все потребители делились бы на две группы: canary и full. Канареечная группа автоматически получала бы новую версию, а доставка основной группе блокировалась до завершения проверки. Это явная и универсальная модель: для неё нужно доработать API управления оркестратором и глубже встроить его в существующий контур .
Второй вариант — использовать уже существующую маршрутизацию по type_id. В этом случае один и тот же файл проходит через два логических имени:
Базовый тип предназначен для канареечной группы.
Тип с суффиксом _verified — для остальной эксплуатации.
Со вторым подходом мы могли быстрее получить работающий MVP, который почти не менял критический путь оркестратора снапшотов: система по-прежнему видела очередную версию известного типа данных и доставляла её подписчикам. Новая логика оставалась снаружи и управляла только публикацией признака «проверено». Мы выбрали второй.
Общая схема взаимодействия компонентов
Прежде чем переходить к алгоритму, посмотрим на систему. API-сервер хранит желаемую конфигурацию и текущий статус процесса. Оператор управляет снапшотом по этапам, читает и публикует версии в индекс снапшотов, а также запрашивает результаты измерений. Мастер оркестратора снапшотов получает информацию о новых версиях из индекса и управляет их применением на нужной группе инстансов сервиса подбора рекламы. Канареечная и основная группы отправляют телеметрию в общую систему метрик, замыкая контур обратной связи.

На схеме нет сервиса сборки снапшотов и S3: они отвечают за создание и хранение файла, но не решают, допускать ли его к раскладке. Здесь важен замкнутый контур управления: оператор публикует версию-кандидат через существующий контур доставки, наблюдает за результатом на сервисах и только после этого разрешает перейти к следующему этапу.
Как устроена «канарейка» поверх оркестратора?
Для управления процессом появился оператор снапшотов. Это не ещё один сервис доставки, а небольшой оркестратор, который следит за жизненным циклом снапшота и допускает его в эксплуатацию только после успешной проверки.
Сам production-кластер логически разделён на две части. Канареечная часть обслуживает небольшую часть реального трафика и первой получает новый снапшот-кандидат. Основная часть рабочей среды продолжает работу с последней проверенной версией и служит контрольной группой. Так изменения можно проверить в настоящем окружении, ограничив их возможное влияние небольшой долей запросов.
Метрики канареечной части сравниваются с показателями основной части рабочей среды. Причём не абсолютные значения — объём трафика у групп разный, — а нормализованные показатели с учётом допустимых отклонений. Если показатели канареечной группы остаются в заданных границах, снапшот получает статус verified и доставляется остальным экземплярам.
Процесс выглядит так:
Сервис сборки снапшотов формирует файл, загружает его в S3 и публикует в индекс снапшотов под базовым type_id
На базовый тип подписана канареечная группа, через которую проходит часть рабочего трафика
Сначала оператор проверяет, что нужная версия дошла до основной части canary-инстансов и что после применения они остаются работоспособными. Если раскладка ещё не завершена или инстансы не подтверждают готовность, оператор приостанавливает процесс до выполнения условий.
Затем оператор наблюдает за техническими и продуктовыми метриками канареечной группы и сравнивает их с контрольными метриками основной части рабочей среды.
Если показатели выходят за допустимые пределы, verified-версия не публикуется, а массовая раскладка не начинается.
Если проверка пройдена успешно, оператор добавляет в индекс снапшотов запись с теми же метаданными и ссылкой на тот же файл, но с суффиксом
verifiedвtypeid.Основная production-группа получает проверенный снапшот с помощью штатного механизма оркестратора снапшотов.

Ключевой момент: большой файл не копируется и не пересобирается. В индекс добавляется запись — фактически снапшот получает «паспорт допуска». Доставка из S3, P2P-обмен и применение на экземплярах остаются в зоне ответственности оркестратора.
Почему оператор, а не cron-скрипт?
Логику можно было реализовать обычным скриптом: раз в минуту искать новый снапшот, проверять метрики и при успехе публиковать _verified. Для прототипа такой вариант выглядел бы проще, А вот хранить состояние, продолжать процесс после перезапуска и разруливать параллельный запуск двух копий пришлось бы вручную. В инфраструктуре уже внедрили API-сервер для пользовательских ресурсов, построенный на модели Kubernetes CRD. Поэтому мы описали каждый поток снапшотов отдельным конфигурационным ресурсом: в его спецификации задаются тип, расписание и правила проверки, а в статусе хранится фактическая фаза процесса.
Контроллер постоянно приводит фактическое состояние к желаемому. Если он перезапустился, то читает сохранённую фазу и продолжает с неё. Повторный запуск одной и той же операции не должен создавать дубликаты. Временные ошибки должны обрабатываться повторными попытками с задержкой, а при работе нескольких реплик активной должна быть только одна.
Такую модель удобно использовать для долгого процесса, где сначала десятки минут занимает сборка снапшота, затем нужно дождаться доставки, пройти два этапа проверки и принять решение. За процессом можно наблюдать по статусу ресурса, где указана текущая фаза.
Метрики становятся частью контракта данных
Самая важная часть «канарейки» — не разбиение серверов на группы, а критерий успеха. Одной проверки на отсутствие ошибок мало: важно убедиться, что приложение не только работает, но и выдаёт ожидаемый по качеству продуктовый результат
Поэтому оператор поддерживает несколько источников метрик, совместимых с Prometheus API, а также два последовательных окна анализа. Первое помогает убедиться, что нужная версия дошла до «канарейки» и была применена. Второе позволяет оценить состояние сервиса после применения данных.
Для каждой метрики создается запрос и ставится условие успешной проверки. Затем по результатам, накопленным за всё окно наблюдения, принимается общее решение. Это позволяет задавать разные политики: например, потребовать несколько успешных измерений, запретить любые ошибки при получении метрик или допустить единичные отклонения, если подавляющее большинство проверок прошло успешно.
В конфигурации глобального снапшота эти этапы настроены так:
Этап | Окно анализа | Условие успешной проверки |
Доставка и готовность | 5 минут, измерение раз в минуту | Новый снапшот появился на основной части canary-инстансов, а сами инстансы сохранили состояние ready |
Проверка влияния | 20 минут, измерение раз в минуту | Ошибки, задержки, отсутствие выдачи и продуктовые показатели, включая объём подобранной рекламы и CPM по ключевым площадкам, не отклоняются от показателей контрольной группы сильнее допустимого |
Итоговое решение принимается по нескольким измерениям. Конфигурация задаёт допустимую долю ошибок при чтении метрик и требует, чтобы за всё окно успешной оказалась заданная доля проверок. Так кратковременный шум не блокирует хороший снапшот, а устойчивое отклонение останавливает раскладку.
Работоспособность защиты мы проверяли так: намеренно вносили в данные изменение, заметно влияющее на результат на выбранной площадке, и наблюдали за ключевыми продуктовыми показателями. Если метрика исчезала или выходила за допустимый диапазон, «канарейка» не публиковала проверенную версию.
Так мониторинг стал не только способом узнать об уже случившейся проблеме, но и исполняемым контрактом, который определяет, какие данные допускаются в эксплуатацию.
Что предусмотрели дополнительно
Автоматическая проверка полезна, когда её поведение предсказуемо не только в штатном сценарии, но и при сбоях контура управления. Поэтому мы добавили в решение дополнительные механизмы:
Конфигурацию проверяем до запуска: проверяем интервалы, источники метрик, запросы и логические условия.
Текущая фаза и результаты измерений доступны в статусе ресурса и в собственных метриках оператора.
Процесс можно поставить на паузу.
При временных сбоях используются повторные попытки, а после перезапуска работа продолжается с сохранённого состояния.
История старых ресурсов очищается под контролем.
Для аварийной ситуации предусмотрели ручной force-verified, который позволяет уполномоченному инженеру обойти автоматическую проверку.
Последний механизм — осознанный компромисс: он даёт возможность обновить критические данные, даже если автоматическая проверка временно недоступна. Это инструмент для редких случаев, и решение о его использовании инженер всегда фиксирует.
Как мы уложились в сжатые сроки?
Быстро внедрить решение без отказа от надёжности помогла правильно выбранная граница системы.
Что это значит: мы не стали заново решать задачи, которые уже выполнял оркестратор. Сервис сборки снапшотов продолжил собирать данные, S3 — хранить файлы, а индекс снапшотов и мастер оркестратора снапшотов — доставлять версии по подпискам. Сервис подбора рекламы по-прежнему получает и применяет снапшоты через привычный механизм.
Новый компонент выполняет одну точечную функцию: наблюдает за результатом на ограниченной группе и проверяет базовую версию — всё остальное по-прежнему делает существующий оркестратор. Для первого варианта решения не потребовалось полноценное API блокировки и разблокировки групп оркестратора: роль переключателя выполнил уже существующий type_id. Именно это сделало изменение минимально инвазивным. Вместо переделки критического тракта доставки мы добавили к нему небольшой слой управления допуском.
Результат
Решение внедрили в production-кластере сервиса подбора рекламы. Новые снапшоты проходят ограниченную раскладку и двухэтапную автоматическую проверку, а после успешного решения получают допуск к основной группе.
Теперь это штатная часть процесса доставки данных в эксплуатацию.
Цена такого решения
У этого архитектурного компромисса есть своя цена.
Во-первых, появились два логических потока одного снапшота — базовый и проверенный. У каждого — своя чёткая конфигурация и подписка, что упрощает мониторинг и разграничение ответственности за каждый поток.
Во-вторых, доставка занимает больше времени из-за окна наблюдения, но так мы получаем уверенность в том, что данные готовы.
В-третьих, качество автоматического решения команда контролирует настройкой метрик: строже условия — требовательней проверка, при мягких условиях — быстрее раскладка.
Кроме того, реализация на type_id — менее универсальное решение, чем прямое управление группами в оркестраторе снапшотов: оно не даёт отдельного API для управления состоянием раскладки. Мы осознанно оставили его как следующий шаг в развитии системы, что добавит наглядности, какая доля экземпляров получил версию и на каком этапе она сейчас находится.
С учётом исходных ограничений баланс был разумным: небольшой объём изменений, повторное использование зрелого механизма доставки и возможность быстро ограничить последствия.
Что можно перенести в другие системы?
Похожий подход можно применять к любым данным, которые заметно меняют поведение сервиса: конфигурациям, моделям машинного обучения, поисковым индексам, каталогам правил, рекомендательным фичам и большим справочникам.
Несколько общих принципов:
Обновление данных следует считать релизом, если оно меняет продуктовый результат. Формальная корректность файла и успешная загрузка не гарантируют корректное поведение системы.
«Канарейке» нужны продуктовые сигналы. Загрузка CPU, использование памяти и количество ошибок важны, но могут ничего не говорить о работе сервиса с позиции бизнес метрик.
Проверку безопасности удобно добавлять как gate поверх существующей транспортной системы. Если в слое доставки уже есть версионирование, подписки и раскладка, достаточно ввести состояния «кандидат» и «проверено», не переписывая весь путь.
Состояние долгого процесса должно быть явным. Ожидание сборки, доставка на «канарейку» и анализ метрик занимают время. Конечный автомат с сохраняемым состоянием проще восстанавливать и сопровождать, чем цепочку несвязанных скриптов.
Принцип fail closed должен сочетаться с управляемым аварийным обходом. Пока автоматическая проверка не пройдена, система не допускает данные к массовой раскладке. В исключительной ситуации инженеры могут разрешить раскладку вручную, зафиксировав это решение.
Главный результат проекта — изменение модели доверия. Раньше новый снапшот считался готовым, когда его удавалось собрать и доставить. Теперь доставка лишь создаёт кандидата. Он готов к эксплуатации после того, как проверка на ограниченной группе покажет, что с этими данными сервис продолжает работать правильно.
KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.