Коротко: деплой — это четыре стадии: загрузить код, поставить зависимости, прогнать миграции и очистить кэш; разбираем простой рабочий вариант для Symfony.
Задеплоить приложение — это, по сути, четыре стадии: загрузить код на сервер, поставить зависимости, прогнать миграции и очистить кэш. Ниже — один из самых простых рабочих вариантов деплоя на примере Symfony-приложения: что держать в репозитории, как собрать релизный тег по git-flow и что выполнить на сервере.
! ВНИМАНИЕ ! Эта статья не истина в последней инстанции и рассчитана в основном на новичков. Я не утверждаю, что это единственно правильный способ деплоя и что остальные способы не имеют права на жизнь. Здесь описан ОДИН ИЗ МНОГИХ вариантов деплоя в продакшен в одной из самых простых вариаций.
Что должно быть в репозитории?
Общий принцип: в репозитории должна быть только кодовая база приложения. Файлов, которые меняются в зависимости от окружения, там быть не должно. К ним относятся:
.env-файлы и их аналоги — конфигурация (но неyaml, не путать);- папки фронтенда вроде
node_modulesи их аналоги; - картинки, не относящиеся к вёрстке сайта: логотип нужен в репозитории, а картинки статей — нет;
- папки и файлы кэшей и логов — для Symfony это
var/logиvar/cache; - любые папки с настройками IDE вроде
.ideaили.vscode.
Всё перечисленное не должно храниться в git.
Какие основные стадии у деплоя?
- загрузка кода на сервер;
- установка зависимостей (composer / сборка фронта) — опционально;
- запуск миграций;
- очистка кэша.
Для всех фреймворков подход примерно одинаков. Единственное исключение — пункт 2: тут есть тонкость, и мнения расходятся надвое. Есть два лагеря: те, кто держит vendor в репозитории, и те, кто нет.
Я отношусь к первому лагерю и считаю, что vendor должен лежать в репозитории. Такой подход гарантирует, что на проде окажется именно тот код, который писал программист и тестировал тестировщик, а не подтянется случайно другая версия с какого-то зеркала. На моей практике был случай: версия строго указана в composer.json, у всех всё работало — выкатили на прод, и всё сломалось. Оказалось, на проде установилась другая версия пакета с зеркала, а не с основного репозитория. Поэтому я предпочитаю катить vendor в прод вместе с кодовой базой.
Что часто требуется сделать перед деплоем?
Есть понятие Deploy Notes — заметки, которые стоит выполнять до или после деплоя. В крупных компаниях код часто пишет один человек, ревьюит другой, тестирует третий, а доставляет в прод четвёртый — причём без ведома первых трёх. Чтобы админ или девопс знал, какие дополнительные действия нужны, и существуют Deploy Notes. В них можно указать (например):
- запустить
composer installилиcomposer update; - запустить
npm install && npm build && rm -rf node_modules; - добавить или удалить переменные окружения в
.env; - перезапустить воркеры;
- создать новый CRON;
- и так далее.
Живой пример
Пример актуален для хостинга, VPS и выделенного сервера. Понадобятся базовые навыки работы в консоли Linux (да, «виндоводам» тут будет больно, если раньше с Linux не работали), доступ к консоли сервера по ssh и настроенный git.
Приложение
Допустим, у нас приложение со стеком:
- PHP 8.1;
- nginx;
- MySQL;
- RabbitMQ;
- memcached;
- Symfony 6.2;
- Vue.js;
- Composer.
git-flow
Определяем git-flow и предполагаем, что мы работаем с git аккуратно, а не пушим всё в master. Есть ветка master — продовая, и ветка разработки develop. Есть рабочая задача TNT-1156.
Находясь на develop, создаём рабочую ветку: git checkout -b TNT-1156. Активно кодим. Допустим, сделали 3 коммита.
Теперь хотим слить 3 коммита в один, чтобы выполнить правило «1 задача = 1 ветка = 1 коммит». Используем интерактивный ребейз:
git rebase -i HEAD~3
Все коммиты, кроме первого, отмечаем буквой s (squash) — там есть подсказки. Сохраняем коммит с внятным сообщением вида 'TNT-1156 added some awesome feature'. На выходе — готовая ветка с одним коммитом.
Предположим, за спринт (неделю) мы сделали 10 задач и хотим всё задеплоить. Нужно собрать билд и тег, в который войдут 10 веток. Подтягиваем все ветки локально:
git fetch --all
Затем для каждой ветки поочерёдно выполняем:
git checkout TNT-1156— переключаемся на ветку;git rebase develop— освежаем ветку отdevelopи при необходимости решаем конфликты;git checkout develop— переключаемся наdevelop;git merge --no-ff TNT-1156— вмерживаем ветку задачи вdevelop.
Так — для всех 10 веток. После того как всё слили в develop локально (напомню — ничего никуда ещё не пушим, всё локально), проверяем, что с прошлого деплоя в develop попало только нужное. Проверить можно через git log или удобный алиас git lg:
git config --global alias.lg "log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit --"
Алиас git lg делает красивую подсветку и отображение для git log в консоли.
Убедившись, что вмержили только нужные ветки, в обязательном порядке запускаем автотесты приложения (надеюсь, они у вас есть). Только при 100% прохождении пушим develop и создаём тег:
git push # пушим develop
git tag testing_101.0 # тег для регрессионного тестирования
git push --tags # пушим тег
Так мы запушили ветку и создали тег для регрессионного тестирования всех 10 задач на предпродовом стенде. Как только тестировщики говорят, что всё ОК, собираем релизный тег:
git checkout master # переключаемся на master
git merge develop --ff-only # мержим develop в master (можно мержить тег)
git push # пушим master
git tag release_101.0 # релизный тег
git push --tags # пушим тег
Непосредственно деплой
Когда релизный тег собран, выкатываем его на прод и выполняем подготовительные работы — тут вспоминаем про Deploy Notes и консоль сервера. Подключаемся по ssh и выполняем:
cd /home/symfony/application/some_route/www— переходим в директорию приложения;- открываем Deploy Notes всех задач и выполняем те, что нужно ДО деплоя;
git stash— убираем все видимые git-изменения (на случай, если кто-то правил прод в обход флоу);git fetch --all— получаем новые ветки и теги;git checkout release_101.0— переключаемся на новый тег;npm install— ставим фронтовые зависимости;npm run build— собираем фронтовый билд;rm -rf /home/symfony/application/some_route/www/node_modules— чистим за сборщиком;su - some_user_who_runs_app -c '/home/symfony/application/some_route/www/bin/console doctrine:migrations:migrate -n'— запускаем миграции;su - some_user_who_runs_app -c '/home/symfony/application/some_route/www/bin/console cache:clear'— чистим кэш;- открываем Deploy Notes и выполняем то, что нужно ПОСЛЕ деплоя;
- желательно в соседнем окне держать продовые логи:
tail -f /home/symfony/application/some_route/www/var/log/prod.log— и смотреть фон ошибок. При неожиданностях сразу откатываемся на предыдущий рабочий тег (фактически это деплой предыдущего тега); - зрительно проверяем основной функционал, что ничего глобально не отвалилось;
- наслаждаемся результатом.
На этом деплой можно считать завершённым.
На что обратить внимание
- старайтесь следовать правилу «1 задача = 1 миграция» — так намного проще откатывать деплой, если что-то пойдёт не так;
- заранее продумывайте схему отката: «деплой оторвало на середине — твои действия?»;
- если есть возможность использовать дополнительные инструменты контроля — используйте (о них ниже);
- не теряйте бдительность после деплоя — поломки проявляются не сразу. Не стоит задеплоиться и сразу уйти на обед на полтора часа, выключив телефон.
Дополнительные инструменты для деплоев
Существует множество инструментов и подходов для безопасных деплоев и заветных 99,99% аптайма. Среди них:
Автоматизация деплоев
- Jenkins;
- TeamCity;
- TravisCI;
- Deployer;
- Ansistrano;
- и др.
Системы мониторинга
- Kibana;
- Grafana;
- Zabbix;
- Prometheus;
- NetData;
- и др.
Не забывайте и про простые, но эффективные вещи из Linux — например htop, чтобы проверить потребление памяти и нагрузку на процессоры. С дополнительными инструментами не переусердствуйте и подбирайте их внимательно: их много и они очень разные.
А что дальше?
Здесь я максимально просто описал деплой простого приложения на одну ноду под небольшим трафиком. А что делать, если приложение работает, например, на 6 нодах? Тогда стоит почитать про стратегии деплоя — их много, и нужно оценивать, что подойдёт в вашем случае. Что можно погуглить:
- Rolling — постепенный «накатываемый» деплой;
- Recreate — повторное создание;
- Blue/Green deployment;
- Canary — канареечные развёртывания;
- Dark — скрытые или A/B деплои.
Если нужно выстроить надёжный процесс деплоя или перейти к многонодовым стратегиям — помогу на консультации.