#git #развертывание
Первый раз работаю в небольшой команде на внутрикорпоративном проекте. Изучить гит
более-менее осилил. Теперь мучаюсь с деплоем. Суть такова:
Используем стандартную схему веток (master, develop, topic). Программируем и верстаем
у себя на локалке. Есть боевой сервер. Он же и git-сервер. Он же и тестовый сервер
:) Сервер содержит 2 веб-приложения. Первое мы пуллим из master-ветки — релиз готов,
второе — из develop — тестируем, прежде чем мастер пулить.
Так вот, чтобы банально выложить малюсенькое изменение feature ветки в продакшн (или
хотя бы в тест) мне приходится превозмогать рутину:
Переключиться с feature на develop, обновить ветку.
Смержить feature, запушить develop
Переключиться на master, обновить её
Смержить develop, запушить master
Подключиться к боевому серверу сделать пулл на develop-приложении
Подключиться к боевому серверу сделать пулл на master-приложении
???
"А тут ещё букву поправьте, пожалуйста"
goto 1
Это можно ли как-то всё упросить? Я уверен, что-то делаю неправильно.
Ответы
Ответ 1
Пункты 1-2 - это очень упрощенный (где убрано к примеру тестирование) процесс, который
делает каждый разработчик. Это его обычный цикл.
Пункт 3-4 - это релиз. Он не должен делать обычным разработчиком. Он делается релиз-менеджером
(или ответственным человеком, назначенным на этом). Он не делается на каждый чих, а
по процессу - раз в две недели или два раза в день.
Пункты 5-8 делаются нажатием одной (максимум двух кнопок) или запуском одного скрипта.
Он запускается на специальном билд сервере (для маленькой такой компании это может
быть одна машина и для сборки, и для тестов и для репозитория). Сам скрипт сборки обычно
делает следующее
git clone / git archive
прогон скрипта, который фиксит конфиги, шаблоны (вы же не хотите показывать всем
программистам пароли к базе на проде?)
запуск скрипта, который процессит css, минимизирует js и тому подобное.
прогон юнит тестов (они здесь, так как после минимизации могут быть проблемы:) )
архивирование и занесение архива в надежное место
подключаемся к прод серверу(серверам) и заливаем туда архив.
архив распаковывается на сервере в отдельную папку, правится конфиг nginx/apache
или симлинка и сервер рестартует. (можно конечно остановить сервер, перетереть файлы
и запустить сервер, но если что то пошло не так...).
на проде проганяются минимальные тесты (к примеру, что главная страница возвращает
200 код, а не 500).
и в git добавляется тег с ссылкой на релиз.
Этот скрипт пишеться один раз и потом подганяется по мере надобности.
также этот скрипт следует дополнить, что бы он мог работать и с develop веткой.
Теперь упрощаем жизнь. В хуки на гит сервере добавляем этот скрпит, что бы при пуше
в девелом/мастер автоматом запускалось и, как результат, деплоилось на тестовый сервер/прод.
Мердж в мастер обычным программистам запрещается.
Теперь все выглядит так. Разработчик взял фичу, сделал под нее ветку, сделал. После
мержда в девелом (или через мердж реквест) его код автоматом выливается на тестовое
окружение. Если вдруг так случилось, что он все сломал - он должен фиксить.
Если так не подходит, можно скрипт запускать на CI - один с самых популярных - jenkins.
В результате деплой - это просто нажать одну кнопку.
Q/A
а почему не пулить прямо на боевом сервере? Если кто то уведет сервер, у него будет
доступ к полной истории разработки. Это нужно?
у меня никто не уведет историю гита, поэтому я буду пулить. уже было
зачем сколько сложностей с скриптом? А представьте, что багу нашли среди ночи и нужно
срочно фиксить. Наличие такого скрипта сильно упрощает жизнь.
а можно без тестов? можно. Но лучше сделать какие-то минимальные.
Ответ 2
Давайте для начала разделим сущности. В процессе задействованы:
Боевой сервер.
Тестовый сервер.
Сервер git
Некоторый "агент деплоя". Сейчас вы развёртываете (деплоите) руками, хорошо бы делегировать
эту задачу серверу непрерывной интеграции (continuous integration server, CI server).
В простейшей версии это реализуется хуками на сервере git.
Исходный код приложения
Дистрибутив приложения — в вашем случае это минимизированные с помощью Gulp файлы.
В данный момент задачи всех серверов выполняет один, а дистрибутив хранится вместе
с кодом. Давайте тем не менее рассматривать их как отдельные и стремиться к низкой
связности между ними (как в ООП между классами). Это даст нашей системе гибкость и
гораздо большую масштабируемость. В частности, не будем полагаться на то, что что-то
лежит рядом на одном диске или доступно по локальной сети.
Что нужно сделать:
Отделить дистрибутив от кода. При коммите запускаем gulp, получаем минимизированные
файлы, (опционально) пакуем их в архив, перемещаем на сервер и там разворачиваем. Минимизированный
код хранить не нужно, т.к. мы всегда можем его получить из исходного.
Реализовать в коде сценариий развёртывания. Хотя бы предусмотреть место для действий,
производимых на сервере: заполнение конфигов, перезагрузка сервисов и т.п. Как говорилось
выше, это делается через git-hook и/или сервер CI.
Автоматизировать реакцию на изменения в репозитории: при пуше в определённые ветки
должно происходит развёртывание в определённом окружении. Поскольку сценарий развёртывания
у нас теперь в коде, мы можем его.
И в организации работы:
Пересмотреть порядок релизов. Сейчас вы как будто бы релизите каждую опечатку и релизить
может каждый. Стоит так же подумать о защите ветки master (в том числе от своих неосторожных
действий).
Работающий пример
Предлагаю проводить эксперимент на примере GitLab.com (т.е. будем использовать облачный
сервис). Выбор субъективный, вот аргументы за:
Есть возможность создавать бесплатные приватные репозитории. (Т.е. ваш коммерческий
код в относительной безопасности.)
В комплекте есть GitLab CI, тесно интегрированный с репозиторием.
Доступны (в том числе для приватных репозиториев) бесплатные раннеры — условно, виртуальные
машины, на которых мы будем выполнять наши задачи.
Аргумент против: если проект будет расти, когда-нибудь бесплатной лицензии не будет
хватать для всех задач.
Рабочий пример с конвейерами и логами выложен на GitLab.com.
Основной файл, описывающий выполняемый сценарий, лежит в корневой папке проекта и
называется .gitlab-ci.yml.
image: alpine
stages:
- gulp
- test
- package
- deploy
cache:
paths:
- node_modules/
gulp:
stage: gulp
image: node:latest
before_script:
- npm install
script: node_modules/.bin/gulp compress
artifacts:
paths:
- dist
expire_in: 10 minutes
test:
stage: test
script: ls dist
script: echo 'run tests here'
package:
stage: package
script: tar -czvf packaged.tar.gz dist
artifacts:
paths:
- packaged.tar.gz
deploy_prod:
stage: deploy
script: echo 'deploy on master'
only:
- master
deploy_test:
stage: deploy
script: echo 'deploy on develop'
only:
- develop
Зависимости мы вынесем в конфиг для npm — package.json. В файле gulpfile.js описана
задача compress, обеспечивающая минимизацию.
Скриншот окна конвейеров CI:
Результат работы: архив packaged.tar.gz, в нём dists/hello.js, в нём
!function(t,e,i){t.title="New title"}(window,document);