
2024-05-10
Как деплоить приложение в прод
2023-10-18
Давайте рассмаотрим ситуацию, что разрабатывать приложения вы уже научились, но что делать дальше? Как это всё залить в интернет и как автоматизировать этот процесс? Расскажу в этой статье на примере Symfony приложения!
! ВНИМАНИЕ ! Данная статья не является истиной в последней инстанции. Статья расчитана в основном на новичков. Я не утверждаю, что это единственно правильный способ деплоя и все остальные способы не имеют права на жизнь. Эта статья описывает ОДИН ИЗ МНОГИХ вариантов реализации деплоя приложения в продакшен в одной из самых простых вариаций.
Общий принцип таков, что в репозитории должна быть только кодовая база приложения. В репозитории не должно быть файлов, которые могут меняться в зависимости от окружения.
Что подходит под файлы, зависящие от окружения:
1) .env
файлы и их аналоги - конфигурационные файлы ( но не yaml
, не путать )
2) папки фронтэнда типа node_modules
и их аналоги
3) картинки, которые не относятся к верстке сайта - например логотип - нужен в репозитории, а картинки статей - не нужны
4) папки и файлы кэшей и логов - для Symfony - это папки var/log
и var/cache
5) любые папки с настройками IDE типа .idea
или .vsc
Все вышеуказанные папки и файлы не должны храниться в репозитории git.
1) загрузка кода на сервер
2) установка зависимостей ( композер / билд фронта ) ( опционально )
3) запуск миграций
4) очистка кэша
Для всех фреймворков подход будет примерно один и тот же, как описан выше. Единственным исключением является пункт 2 - здесь есть одна тонкость и мнения расходятся надвое. Есть два лагеря - те, кто держат vendor
в репозитории и те, кто этого не делают.
Я отношусь к первому лагерю и считаю, что vendor
должен лежать в репозитории. Такой подход гарантирует, что на проде будет именно тот код, который использовал программист и который тестировал тестировщик, а не влезет случайно из какого-то соседнего зеркала какая-то другая версия. На моей практике был случай, когда казалось бы и версия строго указана в composer.json
и казалось бы и у программиста и у тестировщика всё работало - выкатили на прод и всё сломалось. Оказалось, что на проде установилась другая версия пакета не с основного репозитория, а с зеркала. Поэтому я предпочитаю, чтобы vendor
хранился в репозитории и раскатывался в прод, как и кодовая база приложения, написанная в разрезе задачи.
Есть такое понятие как Deploy Notes - это заметки, которые стоит оставлять и выполнять перед или после деплоя. В крупных компаниях часто бывает так, что код пишет один человек, ревьюит код другой человек, тестирует третий, а доставляет в прод четвертый, причем без ведома первых трёх. Так вот, чтобы админ или девопс, который доставляет код знал какие дополнительные действия ему нужно сделать - существуют Deploy Notes в которых можно указать (например):
1) запустить composer install
или composer update
2) запустить npm install && npm build && rm -rf node_modules
3) добавить или удалить переменные окружения в .env
4) перезапустить воркеры
5) создать новый CRON
6) и так далее и тому подобное
Данный пример актуален и для хостинга и для VPS и для выделенного сервера. Вам потребуется немного умений работы в консоли linux ( да, виндоводы, вам тут будет больно, если до этого с линуксом не работали ), доступ в консоль сервера / VPS / хостинга по ssh
и настроенный git
.
Допустим у нас есть приложение со следующим стеком:
- php 8.1
- nginx
- mysql
- rabbitmq
- memcached
- Symfony 6.2
- vue.js
- composer
Далее мы определяем git-flow
и предполагаем, что мы не совсем дикари и качественно работаем с git
, а не пушим всё в мастер, следующим образом:
Есть ветка 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 веток по 10 задачам.
Нам нужно подтянуть себе локально все ветки - сделать это можно командой git fetch --all
После этого для каждой ветки мы выполняем поочередно следующие команды:
1) git checkout TNT-1156
- переключаемся на ветку
2) git rebase develop
- освежаем ветку от develop
, чтобы подлить все изменения и в случае необходимости решаем конфликты
3) git checkout develop
- переключаемся на develop
4) git merge --no-ff TNT-1156
- подмерживаем ветку задачи в develop
ветку
Так мы делаем для всех 10 веток по 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% проходимости в success мы теперь можем запушить develop
ветку и создать тег следующим образом (находясь на develop
ветке):
git push
- пушим develop ветку
git tag testing_101.0
- создаем тег для регрессионного тестирования
git push --tags
- пушим новый тег
Таким образом мы запушили ветку и создали тег для тестирования на предпродовом стенде ( оно же регрессионное тестирование ) всех 10 задач одновременно.
Как только тестировщики нам говорят, что с их стороны всё ОК и регрессионное тестирование проведено - мы можем собрать тег для релиза.
Теперь мы находясь на ветке develop
выполняем следующие действия:
git checkout master
- переключаемся на ветку master
git merge develop --ff-only
- мержим в ветку master ветку develop
( или можно мержить тег )
git push
- пушим ветку master
git tag release_101.0
- создаем релизный тег
git push --tags
- пушим тег в удаленный репозиторий
Теперь, когда мы собрали тег для деплоя - нам нужно его как-то выкатить на продовый сервер и осуществить все подготовительные работы. Тут мы вспоминаем про Deploy Notes и консоль хостинга / VPS / сервера.
Подключаемся по консоли к серверу и осуществляем следующие действия для нашего приложения:
1) cd /home/symfony/application/some_route/www
- переходим в директорию приложения
2) открываем Deploy Notes всех задач и выполняем те, что нужно выполнить ДО деплоя
3) git stash
- убираем все изменения видимые по гиту ( на случай если кто-то ковырял прод в обход флоу )
4) git fetch --all
- получаем все новые ветки и теги
5) git checkout release_101.0
- переключаемся на новый тег
6) npm install
- устанавливаем фронтовые зависимости
7) npm run build
- собираем фронтовый билд
8) rm -rf /home/symfony/application/some_route/www/node_modules
- удаляем и чистим за фронтовым сборщиком
9) su - some_user_who_runs_app -c '/home/symfony/application/some_route/www/bin/console doctrine:migrations:migrate -n'
- запускаем миграции
10) su - some_user_who_runs_app -c '/home/symfony/application/some_route/www/bin/console cache:clear'
- чистим кэш
11) открываем Deploy Notes всех задач и выполняем те, что нужно выполнить ПОСЛЕ деплоя
12) параллельно желательно, чтобы в соседнем окне у вас были открыты продовые логи, например так: tail -f /home/symfony/application/some_route/www/var/log/prod.log
- и смотрим фон ошибок. Если есть какие-то неожиданности и противоречивое поведение - то сразу откатываемся на предыдущий работающий тег ( фактически это деплой предыдущего тега, если совсем на пальцах )
13) проверяем зрительно основной функционал, что ничего глобально не оторвало
14) наслаждаемся результатом наших трудов
На этом деплой можно считать завершённым.
Есть вещи на которых стоит обязательно заострить внимание.
1) всегда старайтесь следовать правилу, что 1 задача содержит только 1 миграцию. В этом случае вам будет намного проще производить откат деплоя в случае, если что-то вдруг пойдет не так
2) всегда продумывайте заранее схему отката деплоя по следующему вопросу: "вот у вас оторвало деплой в середине процесса - твои действия?"
3) если существует возможность использовать дополнительные инструменты контроля - используйте их ( о конкретике чуть ниже )
4) не теряйте бдительности после деплоя - поломки могут проявиться не сразу. Не стоит делать так, что задеплоились и сразу ушли на обед на полтора часа выключив телефон.
Существует множество дополнительных инструментов и подходов для проведения безопасных деплоев и достижения заветных 99,99% аптайма приложения. Средни них есть:
- Jenkins
- TeamCity
- TravisCI
- Deployer
- Ansistrano
- etc.
- Kibana
- Grafana
- Zabbix
- Prometheus
- NetData
- etc.
Также не стоит забывать про довольно простые, но эффективные вещи, которые есть в linux - например htop
, за счет которого можно проверить потребляемую память и нагрузку на процессоры и сделать какие-то выводы.
С дополнительными инструментами не нужно переусердствовать и подбирать их нужно также очень внимательно, так как их много и они очень разные.
В данной статье я постарался обрисовать для новичка максимально просто и плоско процесс деплоя простого приложения на одну ноду (один сервер) под небольшим трафиком.
Но что делать, если вы работаете с приложением, которое работает например на 6 нодах? В этом случае вам стоит почитать про стратегии деплоя - их много разных и нужно оценивать что подойдет конкретно в вашем случае. Вот примеры того, что можно погуглить:
- Rolling постепенный "накатываемый" деплой
- Recreate повторное создание
- Blue/Green deployment
- Canary канареечные развертывания
- Dark скрытые или A/B деплой
Ура! Я наконец-то дописал статью как собирать собственные бандлы на Symfony 6!!!
Статья про EasyAdmin всё ещё в процессе )))
Не, ну мне же надо на чем-то тестировать твиттер локальный...
Я тут еще много полезного буду выкладывать, так что заходите обязательно почитать.
Сайтик пока что в разработке - это далеко не окончательная версия - по сути это то что удалось слепить за 8 часов.
Комментарии