Sitar-agent: доставка конфигураций в тысячи подов Airbnb

Три человека в шлемах на винтажном мотоцикле с коляской на брусчатой площади

Введение

В предыдущей статье мы рассказали о системе динамической конфигурации Airbnb — Sitar, сосредоточившись на архитектуре сервиса и безопасности изменений конфигурации. Теперь перейдём к более сложному вопросу: когда изменение конфигурации зафиксировано — а это происходит несколько раз в минуту — как оно надёжно и быстро доходит до тысяч экземпляров сервисов Airbnb без их перезапуска?

В этой статье описывается sitar-agent — лёгкий Kubernetes-сайдкар (sidecar), работающий рядом с каждым подписанным pod-ом сервиса. Он непрерывно синхронизирует последние конфигурации из бэкенда и делает их доступными на локальной файловой системе для чтения. Сначала мы рассмотрим жизненный цикл доставки конфигурации, а затем обсудим ключевые архитектурные решения при проектировании sitar-agent.

Жизненный цикл доставки конфигурации

Диаграмма ниже иллюстрирует сквозной путь изменения конфигурации — от уровня, с которым работают разработчики, до парка production-сервисов.

Схема потока данных конфигурации: от разработчиков через Sitar Service и S3 до production-подов

Жизненный цикл доставки конфигурации Sitar

Шаг 1 — Создание/обновление конфигурации

Разработчики создают или обновляют значения конфигурации через Git flow или веб-интерфейс. Изменения фиксируются в Sitar Service, где хранятся с полной историей версий, журналом изменений и контролем доступа (ACL).

Шаг 2 — Почасовая загрузка снапшота

Сервис снапшотов (Snapshot Service) периодически упаковывает полное состояние всех групп конфигурации и загружает сжатые снапшоты в AWS S3.

Шаг 3.1 — Предзагрузка снапшота из S3 (при старте pod-а)

Когда production-pod запускается, сайдкар sitar-agent стартует первым. Он скачивает последний снапшот конфигурации каждого подписанного тенанта из S3 на примонтированный диск, общий для sitar-agent и основного контейнера. Это позволяет агенту начать работу с заведомо корректного состояния, не запрашивая каждую конфигурацию у Sitar Service с нуля при каждом перезапуске. Предзагрузка снапшотов из S3 ускоряет перезапуски, делает сервис устойчивым к временной недоступности Sitar Service и предотвращает пиковые нагрузки во время деплоев.

Шаг 3.2 — Предзагрузка последней конфигурации из Sitar Service (при старте pod-а)

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

Шаг 4 — Периодическое обновление

После старта агент входит в непрерывный цикл опроса (порядка нескольких секунд с джиттером). В каждом цикле sitar-agent запрашивает у Sitar Service изменения по всем подписанным группам конфигурации.

Шаг 5 — Чтение конфигурации

Основной контейнер приложения читает конфигурации с примонтированного диска через клиентскую библиотеку Sitar, которая поддерживает кэш в памяти. Клиент обнаруживает изменения файлов и прозрачно обновляет кэш.

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

Ключевые архитектурные решения

В 2024 году sitar-agent был полностью переписан с Ruby на Java — основной JVM-язык в Airbnb, — что дало команде возможность модернизировать архитектуру параллельно с миграцией языка. Предзагрузка снапшотов из S3, описанная выше, — один из результатов этой работы: она существенно сокращает время холодного старта pod-а и отвязывает надёжность запуска от доступности Sitar Service. Кроме того, переписывание повлекло ряд других осознанных решений в области надёжности, производительности и операционной безопасности. Каждое из них рассматривается в следующих разделах.

Требования к системе Sitar

Прежде чем перейти к конкретным решениям, важно понять ограничения, которые определили каждое из них. В Airbnb доставка динамических конфигураций — не просто удобство: она управляет критически важными функциями тысяч сервисов. Это значит, что конфигурации должны быть доступны всегда, даже когда сам Sitar Service недоступен. Слегка устаревшее значение допустимо, но нечитаемая конфигурация — нет. При этом, когда инженер публикует изменение, оно должно доходить до каждого подписанного сервиса за десятки секунд, а не за минуты. Обеспечить это в масштабе непросто: десятки тысяч pod-ов одновременно запрашивают обновления, и система должна справляться с такой нагрузкой без деградации. Наконец, поскольку парк сервисов Airbnb охватывает Java, Python, Go, TypeScript и Ruby, решение должно работать со всеми ними, в идеале минимизируя усилия по поддержке отдельных реализаций для каждого языка.

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

Основной контейнер vs сайдкар

Вопрос о том, должен ли sitar-agent работать как сайдкар или как процесс внутри основного контейнера, стал ключевым архитектурным решением в ходе переписывания на Java. Мы оценили плюсы и минусы каждого варианта.

Преимущества встраивания в основной контейнер:

  • Снижение затрат. Это главный аргумент в пользу встраивания: запуск агента как библиотеки устраняет накладные расходы на JVM для каждого pod-а, позволяя совместно использовать память и CPU с основным контейнером.

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

Недостатки встраивания в основной контейнер:

  • Многоязычная сложность. Сервисы Airbnb написаны на Java, Python, Go, TypeScript и Ruby. Библиотечный подход потребовал бы реализации логики сайдкара на всех этих языках, что значительно увеличило бы трудозатраты на разработку и поддержку.

  • Отсутствие изоляции. Ошибки или скачки потребления ресурсов в логике Sitar могут привести к сбою или нехватке ресурсов в основном контейнере, и наоборот. Такая связность увеличивает радиус поражения при инцидентах и усложняет атрибуцию ресурсов при отладке.

  • Операционный шум. Если логи Sitar и его метрики CPU/памяти смешаны с логами и метриками основного процесса, отлаживать оба компонента становится сложнее.

  • Оптимизируемость. Отдельный контейнер можно оптимизировать под конкретную задачу, а также проще тестировать и отлаживать.

Решение:

Несмотря на потенциальную экономию затрат и снижение операционной поверхности при встраивании логики sitar-agent в основной контейнер, ожидаемый эффект оказался недостаточным, чтобы оправдать компромиссы в надёжности, операционные накладные расходы и затраты на поддержку логики сайдкара на нескольких языках. Поэтому было принято решение сохранить sitar-agent как изолированный сайдкар-контейнер.

Модель опроса и оптимизация на стороне сервера

Sitar-agent получает обновления конфигурации, опрашивая Sitar Service каждые 10 секунд. Это pull-модель (модель опроса): агент сам инициирует цикл обновления, периодически запрашивая сервер на наличие изменений. Такая архитектура проста в обслуживании, но создаёт на сервере лишнюю нагрузку, когда обновлений нет.

Переход на push-архитектуру существенно снизил бы нагрузку на сервер и ускорил распространение изменений, однако ценой более сложной архитектуры. Чтобы сохранить простоту текущего подхода и одновременно снизить серверную нагрузку, в системе Sitar реализованы следующие оптимизации:

  1. Поскольку конфигурации Sitar изменяются преимущественно вручную, а это занимает дольше нескольких секунд, небольшая задержка в доставке обновлений допустима. Поэтому серверный кэш с коротким TTL (10 с) — эффективный способ снизить нагрузку на Sitar Service. Большинство обращений sitar-agent к сервису попадают в кэш, не вызывая тяжёлых серверных вычислений или обращений к базе данных, что значительно сокращает потребление ресурсов при обработке запросов.

  2. При промахе кэша, когда запрос всё же обращается к базе данных, вместе с ним передаётся токен (указатель на последнюю просканированную строку в БД), который указывает сервису пропустить проверку изменений до момента последней выборки. Это существенно сокращает серверную обработку и время доступа к базе данных при каждом периодическом опросе.

Благодаря этим оптимизациям Sitar Service хорошо справляется с нагрузкой от pull-запросов всех pod-ов в Airbnb, сохраняя при этом простую, stateless-архитектуру с опросом.

Решение:

Для сценария использования Sitar задержка опроса порядка нескольких секунд приемлема: динамическая конфигурация — не механизм сигнализации реального времени, а большинство изменений вносятся вручную, поэтому несколько секунд задержки распространения не критичны. Статeless-простота pull-модели — серьёзное операционное преимущество в масштабах Airbnb. Команда решила сохранить pull-модель и сосредоточиться на снижении стоимости каждого отдельного опроса.

Выбор локального хранилища данных

Sitar-agent поддерживает локальное хранилище данных типа «ключ-значение» на диске, из которого читает основной контейнер. Устаревшее хранилище построено на базе Sparkeyвнутренней реализации с тонкой обёрткой над Sparkey-хранилищем для конкурентной координации. По мере роста и развития использования Sitar несоответствие Sparkey-хранилища требованиям системы стало очевидным:

  • Sparkey создан для сценариев «однократная запись — многократное чтение» и не поддерживает нативную координацию параллельных операций чтения и записи. Это требует написания обёртки для поддержки частых операций записи в хранилище, что усложняет систему и может стать источником скрытых ошибок.

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

  • Конструкция Sparkey требует переиндексации всего хранилища при каждой записи, что делает частые записи всё более дорогостоящими. Однако, поскольку Sitar стал широко использоваться практически во всех сервисах Airbnb, обновления в хранилище происходят очень часто — в почти каждом цикле опроса (каждые ~10 секунд).

  • Sparkey ограниченно поддерживает несколько языков: реализации существуют не для всех языков, используемых в сервисах Airbnb, а поддержка всех необходимых языков потребовала бы сложной межъязыковой интеграции.

Команда оценила и протестировала два кандидата на замену устаревшего хранилища на базе Sparkey: SQLite и RocksDB. Была проведена матрица экспериментов при варьирующихся размерах наборов данных, нагрузке на чтение (QPS) и объёмах выделенной памяти — при каждом запуске два из трёх параметров фиксировались, а третий варьировался. Дополнительно была изучена поддержка сообщества, актив

© 2026 meganuke