Представь: у тебя интернет-магазин, пятница вечер, трафик на пике. Надо выкатить обновление — новый алгоритм расчёта скидок. Обычный деплой означает несколько минут даунтайма. Несколько минут в пятницу вечером — это потерянные заказы, злые пользователи и нервный менеджер. Сине-зелёный деплой решает именно эту проблему.
Как это работает
Идея проще, чем кажется. Ты держишь две идентичные production-среды: одну называешь «синей», другую — «зелёной». В любой момент времени только одна из них принимает реальный трафик, вторая стоит в резерве.
Последовательность такая:
- Сейчас работает синяя среда — на ней живёт версия 1.0.
- Ты разворачиваешь версию 1.1 на зелёной среде.
- Прогоняешь тесты на зелёной — убеждаешься, что всё работает.
- Переключаешь балансировщик нагрузки с синей на зелёную. Это занимает секунды.
- Зелёная теперь принимает трафик. Синяя стоит живой, на ней 1.0.
- Если что-то пошло не так — переключаешь обратно за секунды.
- Когда убедился, что 1.1 работает штатно — синюю обновляешь до 1.1 и она становится следующим резервом.
Переключение трафика — это буквально изменение одного параметра в конфиге балансировщика или смена DNS-записи. Даунтайм при этом нулевой.
Минимальная реализация через Nginx
Не нужен Kubernetes, чтобы попробовать. Вот как это делается на двух обычных серверах с Nginx в качестве балансировщика.
Структура такая:
lb.example.com — балансировщик (Nginx)
blue.internal — синяя среда, порт 8080
green.internal — зелёная среда, порт 8080
Конфиг балансировщика:
upstream active_backend {
server blue.internal:8080;
# server green.internal:8080; # закомментировано — не активно
}
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://active_backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
Чтобы переключиться на зелёную среду, раскомментируешь строку с green, комментируешь строку с blue и делаешь nginx -s reload. Reload в Nginx — это graceful: текущие соединения доживают свой век, новые идут уже на новый upstream. Даунтайма нет.
Можно автоматизировать это через простой скрипт:
#!/bin/bash
CURRENT=$(grep -v '#' /etc/nginx/conf.d/backend.conf | grep 'server' | awk '{print $2}')
if [[ $CURRENT == *"blue"* ]]; then
sed -i 's/server blue.internal/# server blue.internal/' /etc/nginx/conf.d/backend.conf
sed -i 's/# server green.internal/server green.internal/' /etc/nginx/conf.d/backend.conf
echo "Switched to GREEN"
else
sed -i 's/server green.internal/# server green.internal/' /etc/nginx/conf.d/backend.conf
sed -i 's/# server blue.internal/server blue.internal/' /etc/nginx/conf.d/backend.conf
echo "Switched to BLUE"
fi
nginx -s reload
Просто, предсказуемо, легко дебажить.
Реализация в Kubernetes
В Kubernetes сине-зелёный деплой делается через манипуляцию с labels у Service.
Есть два Deployment:
# blue deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: app-blue
spec:
replicas: 3
selector:
matchLabels:
app: myapp
version: blue
template:
metadata:
labels:
app: myapp
version: blue
spec:
containers:
- name: app
image: myapp:1.0
# green deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: app-green
spec:
replicas: 3
selector:
matchLabels:
app: myapp
version: green
template:
metadata:
labels:
app: myapp
version: green
spec:
containers:
- name: app
image: myapp:1.1
Service при этом смотрит на конкретную версию:
apiVersion: v1
kind: Service
metadata:
name: myapp-service
spec:
selector:
app: myapp
version: blue # ← вот здесь переключаем
ports:
- port: 80
targetPort: 8080
Чтобы переключиться на зелёную:
kubectl patch service myapp-service -p '{"spec":{"selector":{"version":"green"}}}'
Эта команда выполняется мгновенно. Kubernetes перестаёт направлять трафик на синие поды и начинает на зелёные. Существующие соединения завершаются корректно благодаря graceful termination.
Что делать с базой данных
База данных — самое больное место в сине-зелёном деплое. Если новая версия приложения меняет схему БД, надо думать аккуратно.
Плохой сценарий: версия 1.1 добавляет колонку discount_type в таблицу orders с NOT NULL. Ты переключаешься на зелёную среду, но потом что-то идёт не так и надо откатиться на синюю. Версия 1.0 не умеет работать с колонкой discount_type и падает.
Правильный подход — backward-compatible миграции. Делается в три этапа:
Деплой 1.1: добавляем колонку discount_type как nullable, без дефолта. Обе версии — старая и новая — могут работать с такой схемой.
Деплой 1.2: версия приложения начинает заполнять discount_type. Старая версия её игнорирует — ничего не ломается.
Деплой 1.3: когда убедились, что откат больше не нужен — делаем колонку NOT NULL, добавляем дефолт, убираем обратную совместимость.
Да, это дольше и сложнее, чем просто «добавил колонку и забыл». Но это цена нулевого даунтайма.
Альтернатива — использовать отдельную базу для каждой среды и синхронизировать их. Это работает для read-heavy приложений, но для write-heavy становится кошмаром. Лучше не надо.
Мониторинг после переключения
Переключил трафик — не расслабляйся. Первые 10-15 минут критичны. Что смотришь:
Error rate: если на синей среде было 0.1% ошибок, а на зелёной стало 2% — что-то не так. Порог ставь исходя из baseline, не из головы.
Latency: медианное время ответа и p99. Если медиана не изменилась, а p99 вырос с 200мс до 2 секунд — у части пользователей проблемы.
Бизнес-метрики: конверсия, количество оформленных заказов, количество регистраций. Технические метрики могут быть в норме, а пользователи при этом не могут пройти онбординг из-за бага в новой форме.
Хорошая практика — автоматический откат. Если error rate превышает порог в течение 5 минут после переключения — скрипт автоматически откатывается на предыдущую среду и пишет в Slack. Без участия человека.
#!/bin/bash
ERROR_RATE=$(curl -s prometheus:9090/api/v1/query \
--data-urlencode 'query=rate(http_requests_total{status=~"5.."}[5m]) / rate(http_requests_total[5m])' \
| jq '.data.result[0].value[1]' | tr -d '"')
if (( $(echo "$ERROR_RATE > 0.05" | bc -l) )); then
echo "Error rate $ERROR_RATE exceeds threshold, rolling back"
./switch-to-blue.sh
curl -X POST $SLACK_WEBHOOK -d '{"text":"Auto-rollback triggered: error rate exceeded 5%"}'
fi
Когда это реально нужно
Сине-зелёный деплой — не серебряная пуля. Есть смысл использовать, если:
- Приложение обрабатывает деньги, заказы, медицинские данные — то есть даунтайм стоит реальных денег или репутации.
- Деплои происходят часто: несколько раз в неделю или чаще.
- Есть возможность держать две среды — это дополнительные расходы на инфраструктуру.
- Команда готова поддерживать усложнённый пайплайн деплоя.
Когда это излишество:
- Сайт-визитка или лендинг с ночным трафиком в 10 человек. Даунтайм в 2 минуты раз в месяц — не проблема.
- Стартап на ранней стадии, где скорость итераций важнее стабильности.
- Бюджет не позволяет держать двойную инфраструктуру.
В этих случаях хватает rolling deployment или просто деплоя в нерабочее время.
Сравнение с канареечным деплоем
Часто путают blue-green с canary deployment. Разница принципиальная.
При blue-green переключение бинарное: либо 100% трафика на синей, либо 100% на зелёной. Промежуточного состояния нет.
При canary сначала на новую версию идёт 1-5% трафика, потом 10%, потом 50%, потом 100%. Это позволяет поймать баги на малом проценте пользователей, но усложняет инфраструктуру и требует умного роутинга.
Blue-green проще настроить и понять. Canary лучше для критически важных изменений, где нужно валидировать на реальных пользователях постепенно.
Можно комбинировать: делать canary внутри зелёной среды — сначала 10% трафика на зелёную, потом 100%.
Инструменты, которые упрощают жизнь
Argo Rollouts — расширение для Kubernetes, которое добавляет blue-green и canary из коробки. Не надо писать скрипты руками, всё через CRD-ресурсы.
Spinnaker — тяжёлая платформа для CI/CD от Netflix. Умеет blue-green, canary, автоматический rollback по метрикам. Избыточно для маленьких команд, незаменимо для крупных.
AWS CodeDeploy — если ты на AWS, то blue-green деплой для ECS и Lambda настраивается через пару кликов в консоли. С Lambda вообще переключение происходит на уровне aliases — это элегантно.
GitHub Actions + Terraform — для тех, кто не хочет отдельный инструмент. Пишешь workflow, который поднимает новую среду через Terraform, прогоняет тесты и переключает DNS или балансировщик.
Чек-лист перед первым blue-green деплоем
Последний пункт особенно важен. Первый раз переключать среды лучше не в пятницу вечером на production, а в спокойной обстановке на staging, когда никто не смотрит и можно спокойно разобраться с нюансами.
Если ты запускаешь новый проект и думаешь о том, как выстроить деплой с самого начала — это вопрос, который стоит поднять ещё на этапе проектирования инфраструктуры. Студия REEXY, например, при разработке корпоративных сайтов и интернет-магазинов закладывает архитектуру под такие сценарии изначально, чтобы потом не переделывать.
Blue-green деплой — это про уверенность. Когда знаешь, что в любой момент можешь откатиться за 30 секунд, деплоить становится менее страшно. А когда деплоить не страшно — делаешь это чаще. А когда делаешь чаще — изменения меньше, риски ниже. Вот такая петля обратной связи.