Zero-Downtime Deployment в Kubernetes: настройка и миграции

Обновление боевого сервиса не должно останавливать продажи, заявки и платежи. Мы настраиваем выкатку без простоя под ключ: готовим конфигурации, проверяем миграции данных и передаём вашей команде схемы и регламенты выпуска. Окно недоступности сокращается с часов до нуля, а релизы идут по расписанию, а не по ночам.
Часто задаваемые вопросы

Zero-downtime deployment: что ломается при выкатке в Kubernetes

Zero-downtime deployment ломается не на этапе сборки образа, а в момент, когда под снимают с балансировки. Типовой сценарий: RollingUpdate с maxUnavailable больше нуля отправляет поды на удаление сразу после сигнала, приложение не успевает доработать активные запросы, а балансировщик ещё держит их в своих эндпоинтах — соединения рвутся на середине ответа, и часть клиентов получает 502 вместо данных.

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

Третий сбойный сценарий — откат под нагрузкой. Старая и новая версии какое-то время работают с одной схемой: миграция сделана вперёд, приложение предыдущего релиза не знает новых колонок и сыпет ошибками при обновлении версии. Откат, который должен занимать секунды, превращается в разбор инцидента и восстановление данных.

Ниже разбираем, как это закрывается: корректное завершение с дренажем соединений, честные пробы с прогревом, порядок миграций и правила выкатки, при которых отсутствие простоя — свойство конфигурации, а не везение дежурного.

Непрерывный релиз без простоя: что меняется в работе

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

Проба готовности, хук перед остановкой и запас в одну копию — штатные механизмы Kubernetes, которые и убирают окно: сервис отвечает на всём протяжении выката. Настройка вида maxUnavailable: 0 при maxSurge: 1 перестаёт быть ручной договорённостью и становится частью описания развёртывания.

Готовность к нагрузке закрывается бюджетом прерываний: он не даёт обновлению или вытеснению узла погасить слишком много копий сразу, а прогревающий контейнер наполняет кэш до попадания экземпляра в ротацию.

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

Автоматизация выпуска версий доводит это до конвейера: версия описывается декларативно, откат — возврат предыдущего описания, а не ручная пересборка. На практике окно недоступности уходит в ноль, выпуск версии занимает один проход вместо серии согласований, а доля откатов падает в разы — вместе с классами «потеря трафика на старте» и «миграция применена раньше, чем код к ней готов».

Zero-downtime deployment: сравнение стратегий выкатки

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

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

Стратегия выкатки Когда подходит Ограничения
Постепенное обновление Сервисы без собственного состояния, регулярные релизы Старая и новая версии живут параллельно: схема данных, форматы сообщений и ответов должны читаться обеими
Сине-зелёный контур Критичные интерфейсы, где нужен мгновенный откат переключением трафика Двойной расход ресурсов на время выкатки, поддержка двух наборов конфигурации и доступов
Канареечный выпуск версии Изменения с неочевидным влиянием, нужен замер на доле аудитории Требуются метрики по версиям и маршрутизация по доле трафика, наблюдение удлиняет релиз
Полное пересоздание Внутренние и пакетные сервисы, фоновые задачи Есть окно недоступности, для внешних интерфейсов не годится

Отдельно стоит миграция схемы: её проводят в три шага — расширение структуры, перенос данных, снятие старого поля — и только после того, как обе версии приложения перестали обращаться к прежнему формату.

Этапы внедрения: от аудита кластера до передачи схем

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

  1. Аудит кластера. Разбираем, как развёрнуты сервисы: число реплик, пробы готовности, корректность завершения подов и судьба открытых соединений при остановке. Узкое место обычно одно — общий внешний балансировщик без управления долей трафика.
  2. План внедрения. Схема окружений, разграничение доступов и секретов, очередь изменений по сервисам — так, чтобы релизы не мешали друг другу, а откат был возможен на любом шаге.
  3. Настройка выкатки. Поэтапное обновление с лимитом на число одновременно недоступных реплик, пробы готовности с рабочими порогами, пауза на закрытие соединений перед остановкой контейнера.
  4. Миграции схемы данных. Разбиваем на совместимые шаги: расширение, перенос, удаление старого поля. Старая и новая версии сервиса работают одновременно, откат не теряет данные.
  5. Проверка на учебном контуре. Прогон релиза под нагрузкой и с имитацией отказов: отключение узла, обрыв соединений, ошибка в новой версии. Считаем потери трафика и время восстановления.
  6. Передача регламентов. Схемы окружений, конфигурации, порядок релиза и отката, перечень метрик и условия, при которых выкатка останавливается автоматически.

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

Что остаётся у заказчика: конфигурации, схемы, регламенты

Мы не оставляем заказчика с набором устных договорённостей: всё, что определяет поведение системы при выкатке без простоя, живёт в репозитории вместе с историей изменений. Инженер, который придёт в команду позже, прочитает не «так исторически сложилось», а причину каждого выставленного параметра.

  • Конфигурации выкатки — декларативные описания с параметрами готовности, паузой между волнами и условиями автоматического отката.
  • Схемы окружений — карта стендов, их отличий по данным и нагрузке, порядка продвижения сборки между ними.
  • Политики доступа — права выданы ролям, а не людям: границы «кто запускает выкатку, кто читает журналы» переживают смену состава команды.
  • Документация — путь запроса через обновляемую версию, обязательные проверки и порядок действий при расхождении ожидаемого и фактического состояния.
  • Регламент дежурства — какие сигналы переводят дежурного в режим отката, кому уходит эскалация и что разбирается после инцидента.
  • Сценарии миграций — порядок изменений схемы данных, совместимый со старой и новой версией приложения на время переключения трафика.
  • Проверки перед выпуском — набор автоматических прогонов, без которых сборка не проходит дальше тестового контура.

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

Кейс: релиз платёжного сервиса без остановки трафика

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

Что поменяли

В Kubernetes перешли на стратегию RollingUpdate с параметрами maxUnavailable: 0 и maxSurge: 1: новая реплика попадает в балансировку только после успешной readiness-пробы, а старая получает сигнал на остановку и дорабатывает уже принятые запросы. Миграции схемы вынесли из старта контейнера в отдельный шаг с блокировкой — параллельные реплики больше не накатывают одну и ту же схему и не валят выпуск. Поля в базе добавляли по схеме expand-contract: новый код читает и старые, и новые данные, а несовместимые удаления идут следующим релизом.

Метрики до и после

Время выпуска версии сократилось с 40–60 минут ночного окна до 6–8 минут в рабочее время. Доля откатов упала с примерно каждой пятой выкатки до одной из двадцати, и почти все они автоматические — по метрикам ошибок, без ручного разбора. Обращений пользователей к странице ошибки при релизе — ноль: соединения дожидаются ответа от живой реплики вместо обрыва.

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

Чем выкатка новой версии отличается от перезапуска сервиса?

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

Разница видна в том, как считается готовность к трафику. При перезапуске сервис возвращается в балансировку, как только процесс отдал порт. При обновлении контейнера нужна проба, проверяющая не «жив ли процесс», а «готов ли он обслуживать запрос»: соединение с базой, прогретый кеш, доступность зависимостей. Без неё новая версия получает обращения на этапе инициализации — отсюда ошибки на первом же запросе.

Вторая половина задачи — корректное завершение. Старый экземпляр не удаляют сразу: сначала выводят из балансировки, затем отправляют сигнал остановки и только после заданного окна закрывают соединения. Нарушите порядок — часть клиентов оборвётся ровно в момент смены версии, хотя обновление формально прошло успешно.

В Kubernetes эти шаги описываются декларативно: сколько копий поднимать параллельно, какую долю можно вывести из строя, сколько ждать завершения. Это параметры манифеста, а не ручные действия инженера в момент релиза.

Именно эта разница отделяет перезапуск от zero-downtime deployment: выкатка идёт дольше, но пользователь не видит простоя. Для сервисов с базой добавляется порядок миграций — расширение схемы до выкатки, удаление устаревших полей после.

Технические сомнения: миграции схем и обратная совместимость

Изменение структуры данных — то, что по-настоящему ломает zero-downtime deployment. Пока идёт выкатка, в кластере одновременно живут поды старого и нового поколения, и оба работают с одной базой. Схема обязана оставаться совместимой с обеими версиями кода: иначе новая падает на старых данных, а старая — на новых.

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

Что будет, если релиз придётся откатить?

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

Как убедиться, что старая версия не сломается?

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

Как версионируется схема?

Таблицей применённых миграций и сверкой в конвейере: сборка не выкатывается, если версия схемы не совпадает с ожидаемой. Сами миграции идемпотентны и применяются до старта подов, поэтому повторный запуск ничего не ломает.

Обсудим задачу: вернёмся с оценкой и планом внедрения

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

Что стоит подготовить к этому разговору:

  • Как релиз выкатывается сейчас: последовательное обновление реплик, параллельный контур или доля трафика на новую версию.
  • Настроены ли проверки готовности и как сервис дорабатывает запросы, когда под останавливается.
  • Как применяются миграции схемы: отдельно от кода, с обратной совместимостью или одним шагом.
  • Есть ли фоновые задачи и очереди, которые нельзя запускать в двух версиях одновременно.
  • Какие проверки проходят до выката и по каким метрикам вы решаете, что релиз удался.
  • Допустимое окно обслуживания и допустимая доля потерянных запросов при переключении.

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

Наши компетенции:

Последние работы

  • Разработка сайта компании B2B ADVANCE
    Разработка сайта компании B2B ADVANCE
    1542
  • Разработка веб-приложения для компании FEEDME
    Разработка веб-приложения для компании FEEDME
    1394
  • Разработка веб-сайта для компании БЕЛФИНГРУПП
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    1081
  • Разработка интернет магазина для компании FURNORO
    Разработка интернет магазина для компании FURNORO
    1369
  • Разработка логотипа компании B2B Advance
    Разработка логотипа компании B2B Advance
    816
  • Разработка веб-приложения для компании Enviok
    Разработка веб-приложения для компании Enviok
    1142