Почему zero-downtime деплой сложнее, чем кажется
Rolling update в Kubernetes – три строчки конфигурации. Кажется, что zero-downtime решён. На самом деле это только начало. Настоящие сложности – в слоях ниже контейнера.
Проблема №1: Database migrations
Самый частый источник downtime при "zero-downtime" деплоях. При rolling update одновременно работают старая и новая версия кода. Если миграция добавляет NOT NULL колонку без дефолта – старая версия не может писать в таблицу. Всё упало.
Правило: миграция всегда должна быть совместима со старой версией кода. Это означает многоэтапный подход:
-- Этап 1: добавить колонку nullable (обе версии работают) ALTER TABLE users ADD COLUMN phone VARCHAR(20); -- Деплой новой версии кода (заполняет phone при создании) -- Этап 2: backfill существующих записей UPDATE users SET phone = '' WHERE phone IS NULL; -- Этап 3: добавить NOT NULL после полного деплоя ALTER TABLE users ALTER COLUMN phone SET NOT NULL; Проблема №2: Переименование колонок
Кажется простым: ALTER TABLE ... RENAME COLUMN. Но при rolling update старая версия кода всё ещё ищет старое имя. Порядок правильного переименования:
- Добавить новую колонку, скопировать данные
- Добавить триггер: изменения в старой → новую, и наоборот
- Деплоить новый код, который пишет в новую колонку
- Убедиться, что старая версия кода нигде не осталась
- Удалить триггер, удалить старую колонку
Итого: 4 деплоя вместо одного. Это нормально.
Проблема №3: Backward-incompatible API
Если вы переименовали поле в JSON-ответе API – старые клиенты сломаются сразу после деплоя. Даже если клиент – это ваш же фронтенд в другом сервисе.
// правило двойного деплоя
Никогда не удаляйте поле и не меняйте его тип в одном деплое. Сначала добавьте новое поле (сохраните старое). Убедитесь, что все клиенты перешли на новое. Только потом удалите старое.
Проблема №4: Session affinity
При blue/green деплое пользователи с активными сессиями в "синем" окружении внезапно оказываются в "зелёном". Если сессии хранятся в памяти (не в Redis) – они потеряны. Пользователь разлогинен в середине транзакции.
Решение стандартное: externalized session (Redis, БД), а не in-memory. Но часто это добавляется не с самого начала.
Feature Flags как инструмент деплоя
Feature flags позволяют деплоить код без активации функциональности. Новая логика скрыта за флагом, включается отдельно от деплоя. Это развязывает deploy и release – можно деплоить непрерывно, а включать фичи по расписанию или для отдельных пользователей.
Деплой – технический процесс. Release – бизнес-решение. Смешивать их – источник стресса и ошибок.
Для большинства продуктов достаточно простого решения: флаги в конфиге или базе данных, без сложных платформ. LaunchDarkly и аналоги оправданы при десятках команд и тысячах флагов.