Deployer — удобный и гибкий деплой приложений

Несомненно, тема, думаю, многими заезжена до дыр — всё-таки, деплой надо делать для каждого проекта —, но я всё же подниму её и расскажу об одном замечательном инструменте, о котором, по какой-то странной причине, до сих пор ничего не написали на Хабре, да и вообще в русскоязычном сегменте как-то о нём мало что написано. Исправим это недоразумение.

Deployer хорош во многих отношениях. Код скрипта для деплоя получается коротким. Написан на старом добром Пыхчанском, запакован в самоисполняемый бинарник — то бишь, ставить отдельно какие-то другие инструменты на сервер вам не придётся. Почему-бы и не заюзать его в своих проектах?

Написал утилиту некий Антон Медведев, у него кстати довольно приятный блог есть. Спасибо тебе, Антон :)

Первый коммит был сделан аж в 2013-м году, и до сих пор инструмент потихоньку развивается. У него также есть приятный сайт, на котором можно найти о нём всю документацию.

Что лично мне нравится больше всего из того, что даёт данный инструмент — это возможность быстро откатиться на прошлый «рабочий» релиз, если новый релиз оказался неудачным. Также довольно удобно то, что если при попытке «выкатить» новый релиз что-то пойдёт не так (миграции не применились, фронтенд файлы не скомпилировались, тесты не прогнались…) — то ваше текущее работающее приложение это никак не затронет — оно будет работать как ни в чём ни бывало. Дело в том, что Deployer не изменит ссылку у директории, обозначающей текущий активный релиз, до того момента, пока ваш новый «релиз» не будет полностью установлен и готов к работе.

Единственное, чего Deployer не решает — это возможные проблемы с применением миграций к вашей базе данных. Но это вообще тема сложная, не знаю, существуют ли вообще элегантные решения в данном случае. Если существует — буду рад узнать, какое.

Весь проект разделяется на три папки: current, releases и shared. В общем, это довольно распространённый вид для подобных инструментов, и он действительно удобен. Скажем, в одном из моих проектов на Laravel эта структура выглядит вот так:

ad35ace374a644e6b5cf8e9f149d095b.png

current — ссылка на папку последнего успешно собранного релиза, т.е. текущее приложение.
releases — все релизы, которые были собраны. По умолчанию сохраняется только последние три релиза, и это значение можно легко поменять.
shared — папка, в которой находятся все «общие» файлы, которые относятся ко всем релизам одновременно и не должны создаваться каждый раз заново. К примеру — файл .env, загруженые пользователями файлы, и так далее.

Я люблю лично зайти на свой сервер, запустить скрипт деплоя и наблюдать за процессом его работы. Просто, мне так намного спокойнее жить, так как я всегда могу предпринять какие-то срочные меры, если при деплое что-то пойдёт не так. А так, как я знаю, люди обычно запускают подобный скрипт со своей локальной машины, которая подключается по SSH к серверу и производит деплой. Если это надо сделать сразу на нескольких машинах — то такой подход конечно будет удобнее. К слову, Deployer позволяет выполнять деплой сразу на нескольких машинах в том числе.

Естественно, перед тем как получить возможность выполнить данный скрипт, вам необходимо сначала установить Deployer на свою систему.

В одном из моих проектов на Laravel 5 скрипт деплоя deploy.php выглядит следующим образом:

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 после всего этого.

В общем-то, это всё, что я хотел рассказать и показать. Надеюсь, это будет кому-то полезным. Ну и конечно интересно будет узнать мнение других людей о том, как они занимаются деплоем своих приложений.

© Habrahabr.ru