«А вы не сломаете прод?»: тестовое окружение, канарейки и выкладка без простоя
Страх сломать хрупкий продакшн: главный тормоз автоматизации. Разбираем технически: тестовое окружение как точная копия, канареечные релизы и выкладка без простоя в согласованном окне с планом отката.
Страх сломать хрупкий легаси: главный тормоз автоматизации; разбираем, как его снимают технически
Этот вопрос задают почти на каждом первом разговоре. Иногда его произносят прямо, иногда он звучит иначе: «у нас сложная инфраструктура», «наш прод очень специфичный», «последний раз выкладка встала на три часа». За всеми формулировками - одна и та же тревога: что если автоматизация сломает то, что сейчас хотя бы как-то работает.
Тревога обоснована. Именно поэтому первое правило нашей работы с продакшном: не трогать его напрямую, пока изменение не прошло проверку в безопасной среде.
Почему выкатывают ночью, и что с этим не так
Типичная картина у клиентов, которые приходят к нам с болью по CI/CD: выкладка делается вручную по инструкции из Confluence, а выкатывают всегда ночью или в воскресенье. Логика понятна: меньше пользователей, если что-то пойдёт не так, удар будет тише.
Но ночная выкладка - это симптом проблемы, а не решение. Инженеры устали, внимание рассеяно, круг нужных людей для разбора инцидента недоступен. Именно ночью «пропускают» то, что заметили бы днём. В нашем посте про GitLab CI 2017 года мы описывали схожую ситуацию: команда переходила с ручных выкладок на пайплайн не потому что стало веселее, а потому что ручная выкатка на пятнадцати сервисах в 02:00 - это слишком высокий риск ошибки на усталого инженера.
Цель: сделать выкладку настолько предсказуемой, чтобы её время перестало иметь значение. Для этого нужны три вещи: среда для проверки изменений, механизм переноса без простоя и заранее согласованный план отката.
Тестовое окружение: точная копия, а не «похожая среда»
Тестовое окружение - это среда, которая максимально воспроизводит продакшн. Не «похожая», не «достаточно близкая», а воспроизводящая: та же конфигурация сервисов, те же переменные окружения, та же схема сети, те же версии зависимостей.
В тестовом окружении мы прогоняем всё, что планируем выкатить в прод: обновления кода, изменения конфигурации, миграции схемы базы данных. Если что-то ломается: ломается здесь, а не в продакшне. Это и есть ценность среды.
Когда в 2014 году мы добавляли Serverspec-тесты в Jenkins-пайплайн для проверки состояния инфраструктуры после выкладки Ansible, в том посте честно написали: тестовое окружение тогда ещё не было автоматизировано, и выкладка шла прямо в продакшн. Это была известная точка риска. Тестовое окружение как обязательный шаг пайплайна появилось позже: именно потому, что тестирование непосредственно перед продом недостаточно без предварительной проверки в изолированной среде.
Сейчас в нашем стандартном пайплайне на GitLab это выглядит так: ветка develop автоматически выкладывается в тестовое окружение, прогоняются автотесты, Serverspec-тесты инфраструктуры. Только при успехе этой цепочки открывается выкладка в прод: и она уже не ручная, а тоже автоматизированная.
Честный компромисс: тестовое окружение не бесплатно и не ловит всё
Здесь важно не лукавить.
Тестовое окружение стоит денег. Полная копия продакшна - это двойная инфраструктура. Для крупных контуров это существенная статья. Оптимизация: тестовые среды не должны работать круглосуточно. В нашем материале по FinOps мы показывали, как ночное гашение сред разработки и тестовых сред дало −12% счёта при том, что функциональность сред не пострадала: разработчики приходили к рабочим средам к 8:00, а платили только за рабочие часы.
Тестовое окружение не воспроизводит прод на 100%. Данные другие. Трафик другой. Иногда есть внешние интеграции, которые в тестовой среде либо заглушены, либо ведут себя иначе. Это означает, что тестовое окружение снижает риск, но не устраняет его полностью. Остаточный риск существует, и с ним нужно работать отдельным инструментом.
Этим инструментом служит канареечная выкладка.
Канарейки: сначала 5% трафика
Канареечный релиз - это выкатка новой версии на небольшую долю продакшн-трафика до полного переноса. Классический вариант: 5% пользователей получают новую версию, остальные 95% работают со старой. Мы наблюдаем метрики: ошибки, задержки, бизнес-события, и только при нормальных показателях расширяем аудиторию.
В Kubernetes это реализуется через управление весами трафика в Ingress или service mesh. В простых случаях: через параллельные развёртывания с ручным управлением весами. Логика одна: новый код встречается с реальными пользователями под контролем, а не сразу полностью.
Если что-то пошло не так на 5%: инцидент затронул малую долю, откат занимает секунды, большинство пользователей ничего не заметили. Это принципиально отличается от ситуации, когда изменение выкатывается сразу на всех и обнаруживается постфактум.
Выкладка без простоя: перенос без остановки
Выкладка без простоя - это выкатка, при которой сервис остаётся доступным на всём протяжении переноса. Пользователи не видят страницы ошибки, запросы не теряются, балансировщик переключается между старой и новой версией без разрыва.
В Kubernetes это достигается через плавающее обновление: новые поды поднимаются рядом со старыми, трафик переключается по мере готовности, старые поды выводятся из ротации только после того, как новые прошли проверку работоспособности. PodDisruptionBudget ограничивает количество одновременно недоступных реплик: это тот самый параметр, который в нашем проекте 2017 года потребовал нескольких итераций настройки.
Для выкладки без простоя нужна готовность приложения: корректное завершение (сервис принимает SIGTERM и аккуратно доводит до конца активные запросы), точка проверки работоспособности, правильно настроенные пробы liveness и readiness. Это не всегда есть в легаси-приложениях, и тогда выкладка без простоя требует предварительной работы с кодом: мы честно говорим об этом до старта.
Согласованное окно и план отката
Даже при наличии тестового окружения и выкладки без простоя мы согласовываем окно для плановых выкладок: не потому что боимся, а потому что это правильная практика. Окно - это не запрет, а договорённость: в это время команда готова к реакции, мониторинг настроен на повышенное внимание, дежурный инженер на связи.
Что ещё важнее окна - заранее определённый план отката. До начала деплоя мы фиксируем:
- как откатываемся: предыдущий образ выложен и готов к
kubectl rollout undoили аналогу; миграции схемы: только обратно совместимые, либо откат делается отдельно; - критерий откатиться: конкретный порог, например уровень ошибок выше базового на канареечной группе в течение пяти минут;
- кто принимает решение: не обсуждается в момент инцидента, а определено заранее.
Откат - это не признание поражения. Это заранее спроектированный выход, который делает выкладку безопасной. Когда откат есть и он работает, страх сломать прод становится управляемым.
Что это даёт на практике
Когда пайплайн выстроен: тестовое окружение, автотесты, канарейка, выкладка без простоя, согласованное окно, план отката: выкладка перестаёт быть событием. Это типовая операция, которую инженер запускает в рабочее время, не в 02:00 в воскресенье.
Именно к этому состоянию мы приводим инфраструктуру клиентов: выкладка релиза в один клик, реакция на инцидент от 15 минут силами дежурной смены, а не ваша команда с телефоном под подушкой. Окна работ и RTO/RPO фиксируем в договоре под конкретный контур.
Если ваша команда сейчас выкладывает руками и ночью, или если вопрос «а вы не сломаете прод?» возникает каждый раз перед выкаткой: это точка входа для разговора. Подробно о том, что входит в сопровождение: страница DevOps-аутсорсинга. Если контур на отечественном стеке или нужен переход на реестровое ПО: практика импортозамещения.