Несомненно, тема, думаю, многими заезжена до дыр — всё-таки, деплой надо делать для каждого проекта — но я всё же подниму её и расскажу об одном замечательном инструменте, о котором, по какой-то странной причине, до сих пор ничего не написали на Хабре, да и вообще в русскоязычном сегменте как-то о нём мало что написано. Исправим это недоразумение.
Deployer хорош во многих отношениях. Код скрипта для деплоя получается коротким. Написан на старом добром Пыхчанском, запакован в самоисполняемый бинарник — то бишь, ставить отдельно какие-то другие инструменты на сервер вам не придётся. Почему-бы и не заюзать его в своих проектах?
Написал утилиту некий Антон Медведев, у него кстати довольно приятный блог есть. Спасибо тебе, Антон :)
Первый коммит был сделан аж в 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');
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 после всего этого.
В общем-то, это всё, что я хотел рассказать и показать. Надеюсь, это будет кому-то полезным. Ну и конечно интересно будет узнать мнение других людей о том, как они занимаются деплоем своих приложений.
Автор: saggid