GitOps и blue-green / развёртывание без простоя
Симптом
Цель: развёртывать изменения схемы и приложения без простоя; приблизиться к нулевому простою с помощью миграций expand-contract, окружений blue-green, feature flags и автоматического отката.
1. GitOps: сделайте Git единым источником истины
Описывайте инфраструктуру, миграции схемы и версию приложения в Git; развёртывание запускается слиянием в репозиторий (GitOps). Reconciler (Argo CD/Flux или CI/CD) применяет желаемое состояние к реальному окружению. Тогда каждое изменение проверяемо, воспроизводимо и при необходимости может быть откачено к предыдущему коммиту.
PostgreSQL
# repo/
# migrations/ (Vxxx__*.sql)
# k8s/ (Deployment, Service)
# flags/ (feature flag config)2. Меняйте схему обратно совместимо (expand-contract)
Для нулевого простоя не меняйте схему одним ломающим шагом; разделите изменение на две фазы. EXPAND: добавьте новую структуру (новый столбец/таблицу) так, чтобы старый и новый код могли работать одновременно (nullable-столбец, со значением по умолчанию, двойная запись). ПОСЛЕ перехода приложения на новую структуру — CONTRACT: удалите больше не используемый старый столбец/таблицу отдельной миграцией. Тогда старая и новая версии приложения во время развёртывания без проблем работают параллельно.
PostgreSQL
-- EXPAND (V5): backward-compatible addition
ALTER TABLE customer ADD COLUMN email text;
-- for a while the code writes/reads both columns
-- CONTRACT (V9): drop the old column
ALTER TABLE customer DROP COLUMN mail_old;3. Спрячьте рискованное поведение за feature flag
Запускайте новый путь выполнения кода (например, чтение нового столбца) условно за feature flag; включайте флаг, когда схема готова, и выключайте без изменения развёртывания, если что-то пошло не так. Флаг отделяет развёртывание кода (deploy) от активации функции (release). Фаза expand выкатывается, но изменение поведения включается контролируемо и может быть быстро отменено.
PostgreSQL
-- application layer (pseudocode)
-- if flag('new_email_field'):
-- SELECT email FROM customer ...
-- else:
-- SELECT mail_old FROM customer ...4. Настройте окружение blue-green
Держите два идентичных окружения: 'blue' (рабочее) и 'green' (новая версия). Разверните новую версию приложения в green и проверьте её до получения реального трафика; когда всё готово, переключите маршрутизатор/трафик на green. Важно: база данных обычно ОБЩАЯ, поэтому схема должна быть совместима с обеими версиями (это обеспечивает expand-contract). Хотя цель подключения при переключении меняется, совместимость схемы означает отсутствие простоя.
PostgreSQL
# traffic is switched to green (new version)
# shared DB: schema must be compatible with both blue and green5. Проверяйте развёртывание мониторингом (canary/наблюдение)
Прежде чем переключать весь трафик, отслеживайте такие метрики, как доля ошибок, задержка, число ошибок/блокировок в базе данных и отставание репликации; при желании направьте небольшой процент трафика (canary) на green и понаблюдайте. Если показатели работоспособности хорошие, увеличивайте трафик постепенно; если плохие — не продолжайте. Мониторинг служит входными данными для решения об автоматическом откате.
PostgreSQL
-- live health queries
SELECT count(*) FROM pg_stat_activity WHERE wait_event_type='Lock';
SELECT * FROM pg_stat_replication;6. Подготовьте автоматический откат
Если метрики превышают порог, развёртывание должно откатываться автоматически: в blue-green быстро переключите трафик обратно на blue (старую версию). Откат приложения мгновенный, потому что blue всё ещё работает. Со стороны схемы откат обычно НЕ НУЖЕН: благодаря expand-contract новая схема совместима и со старым кодом, поэтому достаточно откатить приложение. Именно поэтому изменения схемы должны оставаться независимыми от развёртывания приложения и всегда быть обратно совместимыми.
PostgreSQL
# traffic instantly returns to blue (application rollback)
# schema is not rolled back: expand-contract is compatible with old code
# if truly needed: write a new corrective migration