Несомненно, тема, думаю, многими заезжена до дыр — всё-таки, деплой надо делать для каждого проекта — но я всё же подниму её и расскажу об одном замечательном инструменте, о котором, по какой-то странной причине, до сих пор ничего не написали на Хабре, да и вообще в русскоязычном сегменте как-то о нём мало что написано. Исправим это недоразумение.
Deployer хорош во многих отношениях. Код скрипта для деплоя получается коротким. Написан на старом добром Пыхчанском — то бишь, скорее всего, ставить отдельно какие-то другие инструменты на сервер вам не придётся. Если же и придётся — то PHP обычно устанавливается одной командой на любом сервере. Почему-бы и не заюзать его в своих проектах?
Написал утилиту некий Антон Медведев, у него кстати довольно приятный блог есть. Спасибо тебе, Антон :)
Первый коммит был сделан аж в 2013-м году, и до сих пор инструмент потихоньку развивается. У него также есть приятный сайт, на котором можно найти о нём всю документацию.
Что лично мне нравится больше всего из того, что даёт данный инструмент — это возможность быстро откатиться на прошлый "рабочий" релиз, если новый релиз оказался неудачным. Также довольно удобно то, что если при попытке "выкатить" новый релиз что-то пойдёт не так (миграции не применились, фронтенд файлы не скомпилировались, тесты не прогнались..) — то ваше текущее работающее приложение это никак не затронет — оно будет работать как ни в чём ни бывало. Дело в том, что Deployer не изменит ссылку у директории, обозначающей текущий активный релиз, до того момента, пока ваш новый "релиз" не будет полностью установлен и готов к работе.
Единственное, чего Deployer не решает — это возможные проблемы с применением миграций к вашей базе данных. Но это вообще тема сложная, не знаю, существуют ли вообще элегантные решения в данном случае. Если существует — буду рад узнать, какое.
Структура папок релизов
Весь проект разделяется на три папки: current
, releases
и shared
. В общем, это довольно распространённый вид для подобных инструментов, и он действительно удобен. Скажем, в одном из моих проектов на Laravel эта структура выглядит вот так:
current
— ссылка на папку последнего успешно собранного релиза, т.е. текущее приложение.
releases
— все релизы, которые были собраны. По умолчанию сохраняется только последние три релиза, и это значение можно легко поменять.
shared
— папка, в которой находятся все "общие" файлы, которые относятся ко всем релизам одновременно и не должны создаваться каждый раз заново. К примеру — файл .env
, загруженые пользователями файлы, и так далее.
Пример скрипта деплоя Laravel приложения
Я люблю лично зайти на свой сервер, запустить скрипт деплоя и наблюдать за процессом его работы. Просто, мне так намного спокойнее жить, так как я всегда могу предпринять какие-то срочные меры, если при деплое что-то пойдёт не так. А так, как я знаю, люди обычно запускают подобный скрипт со своей локальной машины, которая подключается по SSH к серверу и производит деплой. Если это надо сделать сразу на нескольких машинах — то такой подход конечно будет удобнее. К слову, Deployer позволяет выполнять деплой сразу на нескольких машинах в том числе.
Естественно, перед тем как получить возможность выполнить данный скрипт, вам необходимо сначала установить Deployer на свою систему.
В одном из моих проектов на Laravel 5 скрипт деплоя deploy.php
выглядит следующим образом:
<?php
// Подключим основные рецепты из Deployer'а
require 'recipe/common.php';
require 'recipe/laravel.php';
// Укажем основные параметры деплоя
localServer('local', 'localhost')
->user('{ваш-пользователь}')
->env('deploy_path', '/path/to/project/dir')
->stage('local')
;
set('repository', '{ваш-git-репозиторий.git}');
env('branch', '{ветка-для-релизов}');
env('git_cache', true);
// Общие папки для вашего проекта, которые будут прозрачно доступны всем релизам
// Они не будут создаваться заново при новом релизе, вместо них будут созданы
// ссылки на их одноимённые папки в папке shared
set('shared_dirs', [
'storage/app',
'storage/framework/cache',
'storage/framework/sessions',
'storage/framework/views',
'storage/logs',
'public/uploads',
'node_modules',
]);
// Общие файлы. Принцип точно такой же, как с папками
// В случае с Laravel нам необходимо сделать "общим" лишь один
// файл - .env
set('shared_files', ['.env']);
// Папки, в которые приложение должно иметь возможность
// писать данные. В нашем случае - это три директории
set('writable_dirs', ['storage', 'vendor', 'public/uploads' ]);
set('http_user', '{ваш-пользователь}');
set('composer_command', '/usr/local/bin/composer'); // Путь к расположение Composer'а
// Задача для деплоя. Установить NPM компоненты
task('deploy:install-npm', function() {
run('cd {{release_path}} && npm i');
});
// Ещё одна задача: скомпилировать все фронтенд файлы, в моём случае
// это делается через Grunt.js
task('deploy:compile-assets', function() {
run('cd {{release_path}} && grunt deploy-production');
});
// Выполнить миграции
task('deploy:migrations', function() {
run('cd {{release_path}} && php artisan migrate --force');
});
// Создать кеш для правил роутинга
task('deploy:create-route-cache', function() {
run('cd {{release_path}} && php artisan route:cache');
});
// Создать кеш для файлов конфигураций
task('deploy:create-config-cache', function() {
run('cd {{release_path}} && php artisan config:cache');
});
// Очистить все закешированные данные
task('deploy:clean-cached-data', function() {
run('cd {{release_path}} && rm bootstrap/cache/*');
});
// Перезапустить PHP после успешного деплоя
task('reload:php-fpm', function() {
run('sudo /usr/sbin/service php7.0-fpm restart');
});
task('deploy', [
'deploy:prepare',
'deploy:release',
'deploy:update_code', // Скачать последний код с гитхаба
'deploy:shared', // Создать ссылки на общие данные
'deploy:vendors', // Обновить компоненты композера
'deploy:clean-cached-data', // Очистить все закешированные данные
'deploy:create-route-cache', // Создать кеш для правил роутинга
'deploy:create-config-cache', // Создать кеш для файлов конфигураций
'deploy:install-npm', // Обновить NPM компоненты
'deploy:compile-assets', // Скомпилировать фронтенд файлы
'deploy:migrations', // Применить миграции
'deploy:symlink', // создать ссылку текущего релиза на этот
'cleanup', // Удалить старые релизы
])->desc('Deploy your project');
after('deploy', 'success');
// После деплоя перезапустим php
after('deploy', 'reload:php-fpm');
// После отката на прошлый релиз - тоже перезапустим его
after('rollback', 'reload:php-fpm');
Также у меня есть пара маленьких файлов, лежащих рядом с вышеуказанным файлом: start-deploy.sh
и rollback-deploy.sh
. Для того чтобы быстро запустить деплой или, соответственно, откатить его.
Файл start-deploy.sh
:
dep deploy local
Файл rollback-deploy.sh
:
dep rollback local
Следовательно, чтобы запустить процесс деплоя, нам остаётся набрать лишь одну команду в Bash'е:
./start-deploy.sh
Таким образом, как мы видим, введя всего одну команду, мы заставим сервер выполнить все необходимые шаги для разворачивания нашего проекта. И только если всё прошло хорошо, папка current
сменит ссылку на новый релиз и перезапустит PHP после всего этого.
В общем-то, это всё, что я хотел рассказать и показать. Надеюсь, это будет кому-то полезным. Ну и конечно интересно будет узнать мнение других людей о том, как они занимаются деплоем своих приложений.
Комментарии (27)
cawakharkov
02.06.2016 12:24Когда последний раз пользовался, то столкнулся с тем, что например для dev'а нельзя выбрать другую ветку, или не внимательно читал доки, или нет такой возможности.
johnluxor
02.06.2016 13:51+1Есть параметр --tag=branch|tag
И тогда будет брать из нужно ветки. По умолчанию master.
Но опять же, значение по умолчанию можно задать в конфиге
misterio
02.06.2016 12:50Рекомендую еще попробовать Rocketeer
SiDz
04.06.2016 21:56Лично, мне не нравится в rocketeer, то, что он тянет вендоры laravel, для laravel проекта — никаких проблем.
Но, например, я работаю с symfony и мне не очень нравится, что тянуться ненужные зависимости.
deployer в этом плане мне понрвавился больше. хоть мы и пользовались capifony до этого.
+ в том, что не нужно ставить руби, чтобы деплоить.
akubintsev
02.06.2016 16:36Да, это гораздо лаконичнее того, что я когда-то писал xml-ем под phing :) Правда под ним был еще dbdeploy, позволявший управлять миграциями в БД.
Надо попробовать.
muxx
02.06.2016 23:33В более менее крупном проекте билдить проект на боевых серверах – не очень хорошая затея. Допустим, у нас 10 серверов. Во-первых, билдиться будет 10 раз на каждом из серверов. Во-вторых при билде проекта выполняются ресурсоемкие операции. Например, разогрев кеша крупного symfony-проекта нормально так догружает сервер. Т.е. у нас все сервера, которые под боевой нагрузкой, одновременно начинают параллельно нагружаются деплой-процессом, а это может привести к печальному результату, например к просадке времени отдачи страниц у пользователей.
Мы билдим готовый собранный проект с разогретым кешем и статикой и просто раскатываем его на сервера, где остается только выполнить миграции и переключить симлинки.saggid
02.06.2016 23:56Здравствуйте) Очень интересно. А каким образом вы еще и кеш разогретый умудряетесь положить прямо в билд?
yurykabanov
03.06.2016 07:27Давать пользователю выполнять /usr/bin/service это очень плохая идея.
task('reload:php-fpm', function() {
run('sudo /usr/sbin/service php7.0-fpm restart');
});
Потому что помимо upstart/systemd сервисов, /usr/bin/service может стартовать и обычные SysV инит скрипты:
# Otherwise, use the traditional sysvinit
if [ -x "${SERVICEDIR}/${SERVICE}" ]; then
exec env -i LANG="$LANG" PATH="$PATH" TERM="$TERM" "$SERVICEDIR/$SERVICE" ${ACTION} ${OPTIONS}
else
echo "${SERVICE}: unrecognized service" >&2
exit 1
fi
и единственная проверка для таких файлов — это проверка на существование!
Как следствие можно легко и непринужденно можно поднять права до рута, имея ограниченный доступ к серверу:
$ cd /tmp; echo «id» > awesome_service.sh; chmod +x awesome_service.sh
$ sudo /usr/bin/service ../../tmp/awesome_service.sh
uid=0(root) gid=0(root) группы=0(root)saggid
03.06.2016 07:27Здравствуйте) Но ведь всё это делается только через судо, т.е. нужно пароль ввести для выполнения подобной операции? И как ещё такие вещи давать ему выполнять? Или как вы это делаете у себя, чтобы было реально безопасно?
yurykabanov
03.06.2016 08:03То есть при деплое на десяток серверов вы будете каждый раз вводить пароль? А если серверов больше десяти и пароли разные? Я предполагал что у вас в /etc/sudoers разрешен запуск service без пароля (что как раз таки не безопасно).
Для себя я пока не нашел красивого решения, завел отдельного пользователя (от которого не запускаются приложения), разрешил ему в sudoers запускать service. Тогда конфиг deployer'а можно исправить на
runLocally("ssh specialuser@server service php5-fpm restart");
P.S. не разобрался как тег source использовать, в превью все хорошо, а в самом сообщении — нет. А нет, это видимо из-за предмодерации было.saggid
03.06.2016 09:09У нас пока нет десятка серверов :) Поэтому это пока не стоит проблемой. Спасибо за разъяснения.
VolCh
07.06.2016 08:26Для себя я пока не нашел красивого решения
Запускать php-fpm не от рута не вариант?
DoctorX
12.06.2016 00:51в /etc/sudoers можно разрешить запуск service только с конкретным аргументом (php7.0-fpm restart')
ufadiz
Если развертывать локально на серваке, то деплойер так то особо и не нужен. Его идеология предполагает, что он используется для удаленного деплоя на различных сервера, включая staging и production серверы.
Хотя, я считаю, что вместо deployer вполне можно использовать capistrano, так как у него сообщество все равно больше развито, чем у deployer и капистрано есть плагины для разворачивания проектов как на руби, так и на пхп и так на ноде.
github.com/capistrano/laravel
Вот плагин для ларавел
saggid
Как бы сказать, «особо и не нужен». Одно дело вбить одну команду; другое дело — ворох этих команд, и в правильном порядке, и ничего не забыв.
Да и об идеологии такой я не слышал. Есть инструмент, есть его варианты использования. Какая разница, как ты его будешь использовать, если от него в любом случае есть польза, и в том, и в другом случае?
И чем конкретно капистрано лучше деплоера? Вот сижу я сейчас на деплоере, вроде счастливый и довольный. Зачем мне капистрано?
garex
Тем что написан раньше, отлажен лучше, больше готовых плагинов. Основан на rake (ruby's make) и в-принципе делает всё, что надо.
Для капистраны не надо ставить на удалённой ноде ничего — только нужен ssh-доступ.
saggid
Написан всего на пол года раньше. Да и вообще — не очень это сильный довод, смотреть, кто когда был написан.
Насчёт готовых плагинов — возможно, хотя хотелось бы увидеть конкретные юз-кейсы с этими плагинами, показывающие превосходство капистрано над другими подобными системами деплоя.
Основан на rake — ну это вообще не довод и не повод. Каждый инструмент на чём-то там основан.
Для деплоера тоже, при желании, не надо ставить на удалённой ноде ничего — он точно также может работать по ssh, это просто я лично люблю сам заходить на сервер и делать деплой руками, мне так спокойнее.
По суди, разница по функциям между инструментами совсем небольшая. Деплоер ещё попроще будет: для его работы достаточно всего одного файла-конфига. Но, при желании, можно и "расширить" конфигурацию, разбив её несколько файлов.
maximkou
Использовал и тот, и другой. Деплоер неплохо решает свои задачи на большинстве проектов, ничем не хуже капистрано. Но, на мой взгляд, если проект написан на PHP — лучше использовать деплоер. Чем меньше разных технологий — тем проще поддерживать проект.
saggid
И Деплоер точно также подходит и для любых других проектов. Причем, что удобно — можно его бинарник прямо в корень проекта положить и запускать без проблем на любой машине: он ведь ничего не требует для своей работы, даже пхп.
Лаконично и очень даже удобно, как по мне.
Что насчет капистрано — то это обязательная установка руби, бандлера, и так далее. А разницы как минимум никакой, как я вижу на данный момент.
vtvz_ru
А вот по поводу этого, сэр, хочу поспорить. Deployer написан на PHP и успакован в исполняемый Phar. А чтобы запустить Phar обязательно нужен PHP. Даже на официальном сайте написано:
«Deployer is a deployment tool written in PHP»
saggid
Я полагал, что phar архив становится исполняемым приложением :) Да, я ошибся. PHP для работы Deployer'а нужен.
ElectricHeart
Разве phar архивы не требуют php?
ElectricHeart
Как долго проходил коммент модерацию) Уже успели даже ответить