В процессе работы вы должны регулярно фиксировать свой код, указывая четкие сообщения с описанием ваших изменений:
$ git commit -m "Updated Hello, World text"
Подача изменений на рассмотрение
В конечном счете код и тесты заработают так, как вам того хочется. Это будет означать, что их пора подать на рассмотрение. Это можно сделать либо с помощью отдельного инструмента для разбора кода (такого как Phabricator или ReviewBoard), либо посредством запроса на включение внесенных изменений (pull request), если вы используете GitHub. Такие запросы можно создавать несколькими способами. Один из самых простых — публикация ветки example-feature обратно в origin (то есть обратно в GitHub). В этом случае GitHub автоматически выведет в терминал URL-адрес запроса на включение изменений:
$ git push origin example-feature
(...)
remote: Resolving deltas: 100% (1/1), completed with 1 local object.
remote:
remote: Create a pull request for 'example-feature' on GitHub by visiting:
remote: https://github.com/
remote:
Откройте этот URL-адрес в своем браузере, введите название и описание запроса и нажмите кнопку Create (Создать). После этого члены вашей команды смогут сделать разбор изменений, как показано на рис. 8.1.
Рис. 8.1. Запрос на включение внесенных изменений в GitHub
Выполнение автоматических тестов
Вы должны настроить хуки фиксации, чтобы автоматические тесты запускались каждый раз, когда вы сохраняете или загружаете свои изменения в систему управления версиями. Чаще всего для этого используют сервер непрерывной интеграции (continuous integration, или CI), такой как Jenkins, CircleCI или TravisCI. Самые популярные CI-серверы имеют встроенную интеграцию с GitHub, которая, помимо автоматического запуска тестов при фиксации кода, умеет показывать эти тесты в самом запросе на включение внесенных изменений (рис. 8.2).
На рис. 8.2 можно видеть, что сервер CircleCi выполнил для кода в заданной ветке модульные, интеграционные и сквозные тесты, а также некоторые проверки со статическим анализом (в виде сканирования на предмет уязвимостей с помощью инструмента под названием snyk) и все они прошли успешно.
Рис. 8.2. В запросе на включение внесенных изменений в GitHub показываются результаты автоматических тестов из CircleCi
Слияние и выпуск новой версии
Члены вашей команды должны просмотреть ваши изменения в поиске возможных ошибок, заодно исправляя их в соответствии с рекомендациями по оформлению кода (подробнее об этом — чуть позже) и проверяя, пройдены ли имеющиеся тесты. Они также должны следить за тем, предусмотрены ли новые тесты для любой добавленной вами логики. Если все выглядит хорошо, ваш код можно объединить с веткой master.
Следующим шагом будет выпуск кода. Если вы используете подход с неизменяемой инфраструктурой (как обсуждалось в подразделе «Средства шаблонизации серверов» на с. 31), для выпуска кода приложения его нужно упаковать в новый, неизменяемый артефакт с поддержкой версионирования. В зависимости от того, как именно вы хотите упаковывать и развертывать свое приложение, это может быть новый образ Docker, новый образ виртуальной машины (например, новый экземпляр AMI), новый JAR-файл, новый TAR-файл и т. д. Какой бы формат вы ни выбрали, убедитесь, что ваш артефакт неизменяемый (то есть вы его никогда не модифицируете) и ему присвоен уникальный номер версии (чтобы вы могли отличить его от других).
Например, если вы упаковываете свое приложение с помощью Docker, номер версии можно хранить в теге Docker. В качестве тега подойдет идентификатор фиксации кода (хеш SHA1). Это позволит привязать развертываемый вами образ к коду, который он содержит:
$ commit_id=$(git rev-parse HEAD)
$ docker build -t brikis98/ruby-web-server:$commit_id .
Эти команды соберут новый образ Docker под названием brikis98/ruby-webserver и назначат ему тег с идентификатором самой последней фиксации кода, который будет иметь примерно такой вид: 92e3c6380ba6d1e8c9134452ab6e26154e6ad849. Если позже придется заниматься отладкой этого образа, вы сможете узнать, какой именно код он содержит, проверив его тег с ID-фиксации:
$ git checkout 92e3c6380ba6d1e8c9134452ab6e26154e6ad849
HEAD is now at 92e3c63 Updated Hello, World text
У идентификаторов фиксаций есть один недостаток: их сложно читать и запоминать. В качестве альтернативы можно создать тег Git:
$ git tag -a "v0.0.4" -m "Update Hello, World text"
$ git push --follow-tags
Этот тег тоже ссылается на определенную фиксацию в Git, но с помощью более понятного имени. Вы можете использовать его для своих образов Docker:
$ git_tag=$(git describe --tags)
$ docker build -t brikis98/ruby-web-server:$git_tag .
Таким образом, при отладке можно загрузить код с определенным тегом:
$ git checkout v0.0.4
Note: checking out 'v0.0.4'.
(...)
HEAD is now at 92e3c63 Updated Hello, World text
Развертывание
Теперь, когда у вас есть артефакт с поддержкой версионирования, вы можете его развернуть. Развертывание прикладного кода можно выполнить множеством разных способов: в зависимости от типа вашего приложения, от того, как вы его упаковали, как вы хотите его запускать, какая у вас архитектура, какие инструменты вы используете и т. д. Вот несколько ключевых моментов, которые стоит учесть:
• инструментарий для развертывания;
• стратегии развертывания;
• сервер для развертывания;
• продвижение артефакта по разным окружениям.
Инструментарий для развертывания
Существует множество разных инструментов для развертывания приложений. Выбор зависит от того, как вы упаковали свой код и как хотите его запускать. Вот несколько примеров.
•Terraform. Как вы уже видели в этой книге, Terraform можно использовать для развертывания разных типов приложений. Например, в начальных главах вы создали модуль под названием asg-rolling-deploy, который умел выполнять скользящее развертывание в ASG. Если бы вы упаковали свое приложение в виде AMI (например, с помощью Packer), его новые версии можно было бы развертывать с применением того же модуля asg-rolling-deploy; для этого вам нужно было бы обновить параметр ami в своем коде Terraform и выполнить terraformapply.
• Средства оркестрации Docker. Существует ряд средств оркестрации для развертывания и администрирования приложений в виде контейнеров Docker, включая Kubernetes (наверное, самое популярное из них), Apache Mesos, HashiCorp Nomad и Amazon ECS. Если ваше приложение упаковано с помощью Docker, вы можете развертывать его новые версии, используя команду kubectlapply из состава Kubernetes (kubectl — это утилита командной строки для взаимодействия с Kubernetes) и передавая ей файл YAML, который определяет имя и тег образа для развертывания.
• Скрипты. Terraform и большинство средств оркестрации поддерживают лишь ограниченный набор стратегий развертывания (об этом поговорим далее). Если ваши требования выходят за эти рамки, вам, скорее всего, придется писать собственные скрипты на языке программирования общего назначения (например, Python или Ruby) и использовать средства управления конфигурацией (как Ansible или Chef) или другие инструменты для автоматизации серверов (скажем, Capistrano).
Самое сложное в написании такого рода скриптов — это обработка сбоев. Например, что произойдет, если компьютер, выполняющий ваш скрипт развертывания, потеряет интернет-соединение или сломается посреди этого процесса? Не так-то просто написать скрипт, который был бы идемпотентным и мог бы восстановиться после сбоя и корректно завершить развертывание. Вам, возможно, придется записывать куда-то состояние скрипта (хотя иногда состояние можно вывести, обращаясь к инфраструктуре) и создать конечный автомат, способный справиться с любым возможным состоянием — начальным и переходным.
Стратегии развертывания
Существует целый ряд разных стратегий, которые можно использовать для развертывания приложений в зависимости от конкретных требований. Представьте, что у вас есть пять запущенных копий старой версии вашего приложения и вы хотите выкатить новую версию. Вот несколько самых распространенных стратегий, которые могут вам в этом помочь.
•Скользящие развертывания с заменой. Удалите старую копию приложения, разверните вместо нее новую, подождите, пока она не пройдет проверку работоспособности, направьте к ней реальный трафик и повторяйте этот процесс, пока не будут заменены все старые копии. Эта стратегия развертывания гарантирует, что у вас никогда не будет запущено больше пяти копий вашего приложения, что может быть полезным, если ваши ресурсы ограничены (например, если каждая копия приложения выполняется на отдельном физическом сервере) или если вы имеете дело с системой, которая хранит свое состояние и идентифицирует каждую копию уникальным образом (это часто случается в консенсусных системах, таких как Apache ZooKeeper). Стоит отметить, что эта стратегия может заменять сразу несколько копий приложения (при условии, что вы сможете выдержать нагрузку и не потеряете данные при работе меньшего числа копий) и в ходе развертывания у вас будут одновременно запущены как старые, так и новые версии.
• Скользящие развертывания без замены. Разверните одну новую копию приложения, подождите, пока она не пройдет проверку работоспособности, направьте к ней реальный трафик, удалите старую копию и затем повторяйте этот процесс, пока не будут заменены все старые копии. Эта стратегия развертывания годится только в том случае, если ресурсы можно инициализировать динамически (например, когда ваше приложение находится в облаке, в котором в любой момент можно запустить новые виртуальные серверы) и если ваше приложение допускает выполнение более пяти копий одновременно. Преимущество этого подхода в том, что у вас всегда запущено не менее пяти копий приложения и во время развертывания емкость вашей системы не понижается. Стоит отметить, что эта стратегия может заменять сразу несколько копий приложения (как это делал ваш модуль asg-rolling-deploy) и в ходе развертывания у вас будут одновременно запущены как старые, так и новые версии.