• «Сине-зеленые» развертывания. Разверните несколько новых копий приложения, подождите, пока они запустятся и пройдут проверку работоспособности, переключите на них промышленный трафик и затем удалите старые копии. «Сине-зеленые» развертывания работают только в том случае, если ресурсы можно инициализировать динамически (например, когда ваше приложение находится в облаке, в котором в любой момент можно запустить новые виртуальные серверы) и ваше приложение допускает выполнение более пяти копий одновременно. Преимущество этого подхода в том, что пользователям всегда доступна только одна версия вашего приложения и у вас всегда запущено не менее пяти копий, поэтому в ходе развертывания емкость вашей системы не понижается.
• Канареечные развертывания. Разверните одну новую копию приложения, подождите, пока она не пройдет проверку работоспособности, направьте к ней реальный трафик и затем приостановите развертывание. Во время этой паузы сравните новую (канареечную) копию приложения с одной из старых (контрольных) копий. Это сравнение можно проводить на разных уровнях: загрузка процессора, использование памяти, латентность, пропускная способность, частота возникновения ошибок в журнальных записях, HTTP-коды ответов и т. д. В идеале оба сервера будут выглядеть идентично. Это даст вам уверенность в том, что новый код будет работать без каких-либо проблем. В этом случае вы возобновляете развертывание и завершаете его с помощью одной из скользящих стратегий. Но, если вы обнаружили какие-то различия в поведении, это может быть признаком проблем в новом коде, поэтому развертывание следует отменить, а канареечную версию удалить, пока ситуация не ухудшилась.
Название этой стратегии пришло из угольной промышленности. Шахтеры брали с собой в шахту канареек: если тоннель был наполнен опасными газами (например, окисью углерода), канарейка умирала до того, как эти газы причиняли вред самим шахтерам. Это служило системой раннего предупреждения об опасности, благодаря которому шахтеры знали, что им следует немедленно покинуть тоннель, пока не случилась беда. Канареечные развертывания работают похожим образом. Они позволяют методично проверять новый код в промышленных условиях, и если что-то пойдет не так, вы узнаете об этом на ранних этапах, когда проблема затронула лишь небольшую часть ваших пользователей. Таким образом, у вас будет достаточно времени, чтобы отреагировать и предотвратить дальнейший ущерб.
Канареечные развертывания часто используют в сочетании с ротацией функций — это когда все новые возможности заворачиваются в условное выражение. По умолчанию условное выражение ложное, поэтому при начальном развертывании кода новая возможность выключена. Благодаря этому канареечный сервер, который вы развернули, должен вести себя точно так же, как контрольный, а любые расхождения можно автоматически считать проблемными и инициировать откат назад. Позже, если проблем не обнаружилось, вы можете включить новую функцию для части ваших пользователей с помощью внутреннего веб-интерфейса. Например, вы можете начать с работников компании: если все работает, функцию можно сделать доступной для 1 % пользователей; если и после этого все идет хорошо, число пользователей можно расширить до 10 % и т. д. Если в какой-то момент возникнет проблема, эту функцию можно будет опять свернуть. Этот процесс позволяет разделить развертывание нового кода и выпуск новых возможностей.
Сервер развертывания
Развертывание нужно запускать на CI-сервере, а не на компьютере для разработки. Это даст вам несколько преимуществ.
•Полная автоматизация. Для запуска развертываний на CI-сервере придется полностью автоматизировать все этапы этого процесса. Таким образом, развертывание будет описано в коде и ни один из этапов не будет пропущен по чьей-то ошибке. Это сделает его быстрым и воспроизводимым.
• Однородное окружение. Если запускать развертывания на компьютерах разработчиков, разница в их конфигурации неминуемо приведет к ошибкам. Это, скажем, касается разных операционных систем, версий зависимостей (разные версии Terraform), конфигурационных файлов и того, что на самом деле развертывается (например, разработчик может случайно развернуть изменение, которое не было зафиксировано в системе управления версиями). Вы можете избежать всех этих проблем, если будете развертывать все с одного и того же CI-сервера.
• Лучшее управление доступом. Вместо того чтобы раздавать права доступа каждому разработчику, вы можете сделать так, чтобы только CI-сервер мог выполнять развертывание (особенно в промышленной среде). Соблюдать правила безопасности для одного сервера намного легче, чем для десятков и сотен людей с доступом к промышленной системе.
Продвижение артефактов по разным окружениям
Если вы поддерживаете свою инфраструктуру неизменяемой, для выкатывания обновлений одну и ту же версию артефакта необходимо распространить по разным окружениям. Например, если у вас есть окружения для разработки (Dev), финального тестирования (Staging) и промышленного использования (Production), для выкатывания версии v0.0.4 вашего приложения нужно сделать следующее.
1. Развернуть версию v0.0.4 в Dev.
2. Провести в Dev ручное и автоматическое тестирование.
3. Если версия v0.0.4 хорошо работает в Dev, повторить шаги 1 и 2 для Staging (это называют продвижением артефакта).
4. Если версия v0.0.4 хорошо работает в Staging, повторить шаги 1 и 2 для Production.
Мы везде используем один и тот же артефакт, поэтому, если он работает в одном окружении, существует высокая вероятность того, что он заработает и в другом. Но если вы столкнетесь с какими-либо проблемами, то всегда сможете откатиться к более старой версии.
Процесс развертывания инфраструктурного кода
Мы закончили с процессом развертывания кода приложений. Пришло время поговорить о том, как развертывается инфраструктурный код. В этом разделе под инфраструктурным я понимаю код, который написан с использованием любого средства IaC (конечно, включая Terraform) и с помощью которого можно развертывать произвольные изменения инфраструктуры, не ограничиваясь одним приложением.
Рассмотрим рабочий процесс для инфраструктурного кода.
1. Используем систему управления версиями.
2. Выполняем код локально.
3. Вносим изменения в код.
4. Подаем изменения на рассмотрение.
5. Выполняем автоматические тесты.
6. Проводим слияние и выпускаем новую версию.
7. Развертываем.
Внешне это идентично аналогичному процессу для прикладного кода, но все важные отличия — внутри. Развертывание изменений в инфраструктурном коде более сложное, и методики, которые для этого используются, известны не так хорошо. Поэтому, чтобы вам было легче, каждый этап будет привязан к аналогичному этапу из процесса для кода приложений.
Использование системы управления версиями
Весь ваш инфраструктурный код, как и прикладной, должен находиться в системе управления версиями. Значит, как и прежде, для загрузки кода используется команда gitclone. Однако в контексте инфраструктуры существует несколько дополнительных требований:
• отдельные репозитории для текущей инфраструктуры и модулей;
• золотое правило Terraform;
• проблема с ветками.
Отдельные репозитории для текущей инфраструктуры и модулей
Как уже обсуждалось в главе 4, для кода Terraform обычно нужно как минимум два репозитория: один для модулей, а другой — для текущей инфраструктуры. В первом вы создаете свои универсальные модули с версионированием — как те, которые вы создали в предыдущих главах этой книги (cluster/asg-rolling-deploy, data-stores/mysql, networking/alb и services/hello-world-app). Второй репозиторий определяет вашу текущую инфраструктуру, которую вы развернули в том или ином окружении (Dev, Stage, Prod и т. д.).
Есть один хороший рабочий подход: нужно организовать единую инфраструктурную команду, которая специализируется на создании многоразовых надежных модулей промышленного уровня. Создавая библиотеку модулей, реализующих идеи из главы 6, эта команда может сделать для вашей компании отличное конкурентное преимущество. У каждого модуля будет компонуемый API, основательная документация (в том числе и исполняемая документация в папке examples), комплексный набор автоматических тестов, поддержка версионирования и совместимость со всеми требованиями, которые компания предъявляет к инфраструктуре промышленного уровня (то есть безопасность, соблюдение стандартов и правовых норм, масштабируемость, высокая доступность, мониторинг и т. д.).
Если вы собираете такую библиотеку (или покупаете уже готовую60), все остальные команды в вашей компании смогут пользоваться ею для развертывания и администрирования своей инфраструктуры (чем-то напоминает каталог услуг). При этом: а) никому больше не придется собирать эту инфраструктуру с нуля; б) системные администраторы больше не будут задерживать рабочий процесс, так как они теперь не отвечают за развертывание и администрирование инфраструктуры для каждой команды. Вместо этого сисадмины смогут уделять большую часть своего времени написанию инфраструктурного кода, а остальные команды получат возможность работать автономно, применяя эти модули для подготовки необходимых ресурсов. И, поскольку внутри все используют одни и те же канонические модули, с ростом компании и изменением требований системные администраторы смогут выпускать новые версии этих модулей для всех команд, обеспечивая тем самым согласованность и удобство в обслуживании.
Если быть точным, удобство в обслуживании будет сохраняться до тех пор, пока соблюдается золотое правило Terraform.
Золотое правило Terraform
Так можно быстро проверить работоспособность вашего кода Terraform: откройте репозиторий текущей инфраструктуры, выберите наугад несколько папок и выполните команду terraformplan для каждой. Если в каждом случае вывод указывает на отсутствие изменений, все прекрасно: значит, ваш инфраструктурный код совпадает с тем, что у вас на самом деле развернуто. Если вам иногда попадаются небольшие расхождения и вы время от времени слышите от своих коллег оправдания («О, точно, я немного подкрутил вручную эту небольшую штучку и забыл обновить код»), ваш код не соответствует реальности и вскоре это может вызвать проблемы. Если команда terraformplan завершается полной неудачей и возвращает ошибки или каждый раз получается огромное расхождение, ваш код не имеет никакого отношения к реальной жизни и, скорее всего, бесполезный.