Первыми проблему на проде видят пользователи — а значит, вы уже теряете деньги или репутацию. Поэтому откатываться нужно быстро, а на наших масштабах — очень быстро: в RuntimeCloud, внутреннем облаке Яндекса, работает миллион контейнеров на ста с лишним тысячах серверов.Во времена tar-архивов откатывались со скоростью переключения symlink. В современном мире деплой умеет то, что раньше и не снилось, — он научился изоляции, декларативности и воспроизводимости. Только про быстрый откат многие забыли. Читать далее

Если проблему на проде видят пользователи — значит, вы уже теряете деньги или репутацию. Поэтому откатываться нужно быстро, а на наших масштабах — очень быстро: в RuntimeCloud, внутреннем облаке Яндекса, работает миллион контейнеров на ста с лишним тысячах серверов. Во времена tar-архивов откатывались со скоростью переключения symlink. В современном мире деплой умеет то, что раньше и не снилось, — он научился изоляции, декларативности и воспроизводимости. Только про быстрый откат многие забыли.
Привет, Хабр! Меня зовут Андрей Мичурин, я руковожу разработкой систем деплоя Yandex Infrastructure. RuntimeCloud (RTC) отвечает за выкладки, управление трафиком, изоляцию, платформизацию. Под нашим управлением находится 1 млн контейнеров, более 100 тысяч серверов и 8 млн ядер. В этой статье расскажу, почему для современного деплоя очень важно уметь быстро делать откат, какие решения мы рассматривали и как сделали своё.
Эта статья написана по мотивам моего доклада на infra.conf'26.
Если вам больше нравится слушать, а не читать — вот видео доклада. Эволюция деплояИтак, откатываться нужно очень быстро. Чтобы объяснить, почему скорость отката стала критичной, посмотрим на историю развития деплоя.

На заре деплоя не было никаких чётких правил, автоматизации и контроля: каждый делал, как хотел и когда хотел. Программа жила на физическом носителе. Собрал бинарь на одной машине — запустил на другой, в соседний город носитель отправил обычной почтой. Единого репозитория и стандартной сборки не существовало: что инженер принёс, то и попало на сервер.
Если инженер был хорошим, он запускал файл каким-то watchdog — программой, которая перезапускает процесс, если он упал. Если не очень хорошим, он его запускал в домашке (home directory) — личной папке пользователя на сервере. Там процесс мог случайно упасть, после чего его никто не найдёт, и он не будет автоматически перезапущен.
Позднее стали выкатывать бинари по сети. Например, в случаях, когда бинарь уже не запускается на том компьютере, на котором он собирался. Либо, например, этот компьютер не справляется с нагрузкой и нужен кластер. Если очень сильно повезло — компьютер большой, памяти много и предыдущий бинарь сохранился, — то на него можно откатиться. А если не повезло, нужно заново собирать бинарь, приносить файл на носителе или отправлять по сети.
Дальше появились бандлы: бинарь вместе с его конфигом и другими данными, нужными для запуска. Всё это было удобно упаковать в архив, например, в tar.gz. Такой архив приносили на сервер, распаковывали в какую-то директорию, для которой делался symlink. А из неё уже запускался бинарь.
Так возникла первая атомарная система деплоя. Откат выглядел так: бинарь останавливался, symlink переключался на другую директорию, бинарь снова запускался.
Позже начали управлять зависимостями. Статически собранный бинарь просто работает, а вот разделяемыми библиотеками нужно как-то управлять. Эту проблему пытались решить при помощи пакетов. Пакет — это тоже архив, бандл, но в нём описано, какие библиотеки нужны для запуска, как запустить бинарь и что произойдёт после установки.
Однако здесь есть нюанс. Если вы хотите откатить пакет, вы его откатите. Если же он содержит ещё 100 пакетов зависимостей, вам придётся откатить ещё и их (dependency hell). Если вам повезло, то у вас всё это закешировано на хосте. А если нет — нужно будет все 100 пакетов перекачать снова, и время отката будет чуть-чуть больше.
Автоматизация управленияКогда появился зоопарк серверов и операционных систем, этим нужно было как-то управлять. Тогда возникли системы управления конфигурацией.

Автоматизация породила подход, который мы используем по сей день, — инфраструктуру как код. Позже он эволюционировал в GitOps: выкатка и откат стали коммитом в репозиторий.
Рецепты и манифесты в каждой системе устроены по-своему, но обычно это файл со списком серверов: какие компоненты на них поставить и в каком состоянии они должны оказаться.
Например, чтобы запустить прокси-сервер Nginx, его нужно настроить. Допустим, в группе 4 сервера, и вы хотите связать их с бэкендом. Для этого вы создаёте новый файл конфигурации, описываете в нём все серверы бэкенда и то, что нужно подключить. Разумеется, вам может потребоваться и база данных, которую вы также можете настроить и интегрировать. После этого всё будет работать.
Ключевые преимущества этого подхода — декларативность и повторяемость. Дальше можно тестировать. Например, взять файл, запустить в виртуалке и проверить, что все пакеты и файлы приедут, а архивы распакуются. Есть и ещё одно преимущество — быстрое масштабирование: достаточно расширить группу серверов.
Тем временем серверы стали мощнее, и понадобилось запускать на одном физическом сервере, например, десять Nginx. Для этого инженеры стали использовать виртуализацию.
Linux ContainersLXC-контейнеры — это первый успешный встроенный в mainstream ядро Linux легковесный подход. Это ОС-контейнеры: внутри есть свой супервизор, например systemd. Администрировать его нужно точно так же, как физический хост: приносить пакеты, конфиги, секреты. Для этого подойдёт любая система управления конфигурациями, например CFEngine.
Любой контейнер, в том числе LXC, — это не что иное, как cgroups и namespaces.

Это два механизма ядра Linux, которые позволяют делать контейнеры. Вместе они задают видимость и геометрию контейнера. Через namespaces мы говорим процессу, что его PID — 1 и других процессов в системе нет. Можем сделать изоляцию по диску (mnt), тогда процесс видит только свою файловую систему. По сути мы говорим процессу: «Ты совсем один в системе».
Cgroups позволяет задавать геометрию контейнера, например, ограничения. У контейнера может быть 2 ядра и 2 ГБ памяти. Таким образом, контейнер — это набор ограничений, которые работают за счёт механизмов ядра Linux. При помощи cgroups и namespaces мы можем делать любые абстракции, вложенность и дальше с этим работать.
ОС-контейнеры со своим супервизором нужно настраивать и наполнять пакетами, а это сложно, поэтому инженеры начали упрощать. В итоге стали использовать по дефолту application-контейнеры. Самый известный пример — знакомый всем Docker: достаточно указать базовый образ и команду, которую надо запустить. В отличие от ОС-контейнеров, в Docker-контейнере по дефолту запускается один процесс, что упрощает управление и приближает нас к модели «приложение как контейнер».
Однако всё это по-прежнему нужно оркестрировать. Тут возникает Kubernetes — система оркестрации контейнеров, которая позволяет их управляемо выкладывать на кластеры. Там можно делать собственные контроллеры и писать любую логику.
В Kubernetes есть киллер-фича, которая мне очень нравится, — readiness probe. Это механизм, с помощью которого Kubernetes проверяет, готов ли под принимать трафик. Если проба не проходит, Kubernetes убирает под из списка эндпоинтов сервиса: балансировщик нагрузки перестаёт слать туда запросы. Если проходит — под снова начинает получать трафик. Правда, readiness probes добавляет задержку в выкладку и съедает часть скорости.
В итоге системы деплоя усложнились, а мы разучились откатываться со скоростью symlink: изоляция, воспроизводимость, декларативность есть, а вот скорости отката нет. И мы поставили себе цель вернуть скорость symlink в мир контейнеров.
Схема системы деплояДля ясности давайте разберём принцип работы нашей системы деплоя. Это собственная разработка: она похожа на Kubernetes, но не копирует его. В этом объяснении я буду использовать терминологию, характерную для Kubernetes, чтобы упростить восприятие.

Самая важная часть в системе деплоя — конечно, человек: именно он пишет спецификацию. Спека отвечает на вопросы: что катить, как катить и куда катить. Ну, например, в спеке мы можем написать: «Я хочу выкатить Nginx, 10 подов в 3 дата-центра и проверить доступность 80-го порта». Эта спека попадает в базу данных Yandex Planner поверх YTsaurus (получается аналог etcd).
Дальше спеку читают и обогащают контроллеры: включают логирование из коробки, чтобы все Nginx показывали логи в интерфейсе. Добавляют мониторинг с графиками потребления CPU и памяти, чтобы пользователь видел, какая у него геометрия контейнеров и сколько ресурсов они съедают. Настраивают алерты, чтобы пользователь узнал, когда заканчивается место на диске или память.
Обогащённая спека снова попадает в базу данных, а дальше её подхватывает хостовый агент, который написан на основе поведенческих деревьев. Он читает спеку и собирает под прямо на хосте. Агент идёт в container runtime и сообщает: «У меня есть геометрия пода — нужны контейнеры с такими-то ресурсами, ограничениями и видимостью». Контейнерный runtime позволяет не ходить с системными вызовами в ядро Linux, а собирать контейнеры при помощи API контейнерного runtime.
При помощи cgroups и namespaces мы можем создавать любые контейнеры любой формы и вложенности. Именно этим мы и воспользовались. Мы поняли, что можно ускорить выкладку, если разбить её на два этапа — prepare и switch.

Первый этап, подготовка, — самый длительный. Здесь мы скачиваем ресурсы, готовим сеть, запускаем хуки, приносим секреты, проверяем целостность файлов. Всё сложное и дорогое делаем в фазе подготовки. На этапе активации стартует workload — при необходимости проверяем, что он работает.
Эти фазы независимы, поэтому мы разнесли их и сделали систему подконтейнеров.

Внутри подконтейнеров живут Docker-контейнеры. На картинке видно, что один активный, а два скачались, разархивировались, подкачали слои, разложили секреты, проверили файлики, но команду запуска мы не выполняли.
Работает это так. Пользователь говорит: «Хочу хранить 10 релизов». Допустим, их Docker-образ весит 1 ГБ. Тогда резервируем место под 10 релизов — 10 ГБ на диске. При этом не просим дополнительно ни памяти, ни CPU, потому что эти контейнеры ничего не делают: они только лежат на диске и ждут, когда мы их запустим.
Подготовка не привязана к активации: её можно сделать хоть за сутки, хоть за месяц до релиза, а затем активировать в нужный момент. Мы буквально гасим один контейнер и поднимаем другой. Это и есть symlink в мире контейнеров.
Сравниваем системы откатов Дальше расскажу, как мы пришли к этому решению, какие были альтернативы и почему они нам не подошли.
Blue-Green: нужно вдвое больше ресурсовВозьмём, например, Blue-Green. Он работает так: есть два кластера — кластер A и кластер B. Вы выкатываете релиз на кластер B и, если там всё хорошо, переводите туда трафик. Нюанс в том, что Blue-Green требует дополнительных ресурсов: памяти и процессорного времени. А у нас есть, например, Яндекс Поиск. Blue-Green требует ×2 ресурсов, а Поиск не влезает уже в ×1.
Получается, что Blue-Green — классная штука, если у вас мало подов и есть запасные ресурсы. К сожалению, в масштабах Яндекса это не работает.
Canary: ловит не те проблемыКанарейка — это «мини-кластер», куда идёт часть нагрузки. Если что-то пошло не так, вы снимаете нагрузку, откатываете канарейку и возвращаете нагрузку. Канарейка тоже требует дополнительных ресурсов. И тут есть один подвох. Дело в том, что у нас есть тестовые кластеры и LLM, которая пишет тесты и сама их поднимает, так что к моменту выкатки всё уже протестировано — канарейке нечего ловить.
В таком случае мы выкатимся на весь кластер и только тогда поймём, что есть какая-то проблема. Откуда она берётся, если всё протестировано? Например, вот так: мы полностью протестировали свой релиз, смежная команда — свой. Но совместимость двух релизов не тестировал никто. Мы входим в клинч, и кто-то из нас должен откатиться. Такой откат будет долгим, потому что мы уже выкатились на весь кластер. Поэтому канарейка нам тоже не подходит.
Подконтейнеры: платим только дискомЕщё способ накатывать и откатывать. Допустим, у нас кластер из четырёх нод с подконтейнерами. Мы говорим системе: «Хотим Docker-образ с новым релизом. Подготовь». И это может происходить с бюджетом 100%. Это неинвазивная и безопасная операция — мы просто перетаскиваем файлы. Когда они скачались, мы готовы с ними работать.
Дальше второй этап — активация, сам релиз. Он двигается окном: последовательно гасим и запускаем подконтейнеры. Получается, буквально перещёлкиваем symlink. Откат происходит в обратном порядке. Это гораздо быстрее, чем полностью пересобрать контейнер и заново качать файлы.
И главное — мы не платим за это дополнительные CPU/RAM, используется только дополнительное место на диске.
Откат накатом: 10 минут на откатСекретный способ, который все используют, — откат накатом. Представим, у вас есть кластер и там произошла какая-то ошибка. Вы просто собираете новый релиз и заново катите его целиком. Это долго, дорого и, к сожалению, тоже нам не подходит.
На примере 1000 подов можно увидеть, как это работает. Эмулируем выкладку и говорим, что у нас есть оценка: в среднем одна минута на один под. Катим бюджетом по 10%, то есть активируем батчами по 100 подов, и так десять раз. В таком случае 100 подов параллельно поднимаются примерно 1 минуту, а 1000 подов — примерно 10 минут.

С Blue-Green мы очень быстро можем выкатиться, и скорость переключения зависит только от скорости переключения трафика. Тут откатиться получится быстро.
С канарейкой как повезёт. Если мы поймали проблему на канарейке, то можем откатиться за одну минуту. Ну, потому что там 100 подов — это условно кластер. Если не повезло, то мы будем откатываться целых 10 минут.
С нашими подконтейнерами весь кластер мы можем откатить меньше чем за 2 минуты, потому что активация — это 10% от всего пайплайна. То есть примерно 10 секунд мы будем активировать workload и переключать symlink. Соответственно, откат накатом — это 10 минут на откат.
Классная система, но она не решает всех проблем, а закрывает одну конкретную задачу — быстрый откат. Она не поможет, если у вас, например, база данных и есть какая-то миграция. Вы быстро откатитесь, но ничего не будет работать, так как откатится только код.
Например, если у вас долго поднимаются бинари и долго проходят readiness probe, быстро откатиться тоже не выйдет: время съедает подъём процесса, а не переключение. Ключевой момент: решение дорогое. Чтобы его воплотить, нужна собственная инфраструктура или свой способ работать с cgroups и namespaces.
Мы в Яндексе всегда стремились быстро откатывать, и для нас это был де-факто стандарт. Поэтому интеграция была дешёвой, а разработка — достаточно дорогой.
Что с этим всем делатьПовторить подконтейнеры без своей инфраструктуры сложно. А вот саму идею можно использовать как угодно: скачивание, распаковку, прогрев, секреты, проверки достаточно сделать заранее и держать на диске рядом с работающим релизом. Тогда и на релизе, и на откате остаётся только переключение.
Отсюда проверка для любой системы деплоя: сколько реального времени уходит на возвращение старого кода? Если ответ измеряется десятками минут, посмотрите, сколько из них уходит на подготовку, а сколько — на сам старт. Подготовку почти всегда можно вынести вперёд, а вот старт не всегда.
Вся эта работа понадобилась ради умения, которое было у нас ещё во времена tar-архивов. Зато теперь у нас есть изоляция, воспроизводимость, декларативность — и скорость отката, близкая к symlink.
Кстати, насчёт работы. Узнать, как мы делаем внутреннюю инфраструктуру Яндекса, можно в нашем канале. А вопросы про деплой давайте обсудим в комментариях.
| # | Наименование новости | Тональность | Информативность | Дата публикации |
|---|---|---|---|---|
| 1 | Миграция без права на ошибку: как перенести 70 кластеров MongoDB в 7 и не сломать продакшен | 0 | 10.65 | 02-09-2026 |
| 2 | RuntimeNodes: как мы, ML‑щики, стали писать рантайм | 0 | 7.74 | 24-09-2026 |
| 3 | 350 коммитов в неделю: как контрибьютить в проект, который меняется быстрее, чем ты пишешь | 0 | 7.44 | 10-09-2026 |
| 4 | Как на ровном месте сэкономить 1000+ ядер, или Куда на самом деле уходили 80% CPU | 0 | 9.12 | 08-09-2026 |
| 5 | За пределами EXPLAIN: как увидеть выполнение запроса вживую в распределённой СУБД | 0 | 6.98 | 01-10-2026 |
| 6 | Двухпанельный файловый менеджер на Delphi. Из NDN с любовью… | 0 | 6.96 | 27-09-2026 |
| 7 | Как Omit {T, K} растворил типы, или что такое дистрибутивность типов в TypeScript | 0 | 6.9 | 19-08-2026 |
| 8 | Путь к ИИ‑сингулярности | 0 | 8.46 | 28-09-2026 |
| 9 | Забытые технологии недалекого прошлого. Почему стандартизация уничтожила эти амбициозные проекты | 0 | 9.51 | 27-09-2026 |