Гибкий деплой приложений с Deployer

Feb 13, 2017 11:14 · 567 words · 3 minute read deployer

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

Для этого можно использовать множество разных инструментов — Phing, Envoyer, Capistrano, Ansistrano и конечно же Deployer. Давайте разберемся!

Deployer мне пришелся по душе по многим причинам — написан на php, устанавливается и настраивается очень просто, код скрипта получается коротким и понятным, хорошо документирован и есть много рабочих примеров.

Установка не должна вызвать никаких трудностей:

curl -LO https://deployer.org/deployer.phar
mv deployer.phar /usr/local/bin/dep
chmod +x /usr/local/bin/dep

Далее в каталоге с исходным кодом проекта необходимо создать файл eploy.php, в котором и будем описывать процесс развертывания приложения:

touch deploy.php

Во-первых, необходимо настроить путь к репозиторию приложения, например:

...
set('repository', '{ваш-git-репозиторий.git}');
...

Во-вторых, необходимо описать хотя бы один сервер, на который будет производиться деплой:

...
server('production', 'example.com')
    ->user('{пользователь-для-деплоя}')
    ->identityFile()
    ->set('deploy_path', '/var/www/example');
...

Самый простой способ подключения к серверу для деплоя — авторизация по ssh-ключу. По умолчанию используются ключи ~/.ssh/id_rsa и ~/.ssh/id_rsa.pub, но если вы хотите использовать другие ssh-ключи или ключи, созданные с паролем, то нужно использовать параметр identityFile, например:

...
    ->identityFile('~/Code/.ssh/id_rsa.pub', '~/Code/.ssh/id_rsa', 'password_for_keys')
...

Добавим также в скрипт деплоя так называемые «шары» — общие файлы и папки для всех релизов (например, это могут быть файлы настроек и каталог с логами/сессиями/кешем и т.д.):

...
add('shared_files', [
    '.env'
]);
add('shared_dirs', [
    'storage'
]);
...

С такой минимальной конфигурацией уже можно выполнять развертывание приложения:

dep deploy

Данная команда при выполнении создаст на сервере «example.com» в каталоге /var/www/example следующую структуру проекта:

├── .dep
├── current -> releases/1
├── releases
│   └── 1
└── shared
    ├── .env
    └── storage

В каталоге releases будут находиться релизы (по умолчанию сохраняется 5 последних релизов, это значение можно изменить добавив в скрипт деплоя строку set('keep_releases', 3);).

В каталоге shared находятся общие файлы/папки для всех релизов, которые будут подключаться с помощью симлинков.

Несложно догадаться, что current — это симлинк на последний успешно развернутый релиз (ссылка будет указывать на предыдущий релиз пока не выполнятся успешно все задачи из скрипта деплоя).

В каталоге .dep хранятся метаданные для deployer‘a — логи скрипта, логи релизов, файл .lock (создается в момент деплоя) и т. д.

Как правило, минимальной конфигурации скрипта деплоя недостаточно для «реальных» проектов, поэтому некоторые задачи приходится дописывать самостоятельно. Например, если нам нужно после обновления программного кода перезапустить php-fpm на сервере, то скрипт деплоя нужно дополнить:

...
desc('Restart PHP-FPM service');
task('php-fpm:restart', function () {
    run('sudo systemctl restart php7.0-fpm.service');
});
 
after('deploy:symlink', 'php-fpm:restart');
...

Примечание. Пользователь, от которого выполняется деплой, должен иметь права на перезапуск сервиса php-fpm на сервере.

После проделанных действий также нужно изменить путь к каталогу с кодом в настройках web-сервера, для Nginx это может выглядеть так:

server {
  listen 80;
  server_name example.com www.example.com;
 
  root /var/www/example/current/public;
 
  location / {
    try_files $uri /index.php$is_args$args;
  }
}

Существуют также готовые рецепты для многих популярных фреймворков, таких как Laravel, Symfony, Yii, Zend и т.д., которые еще больше упрощают настройку деплоя, подробнее о них можно узнать здесь.

Мой скрипт деплоя полностью выглядит так:

<?php
namespace Deployer;
require 'recipe/laravel.php';
 
// Configuration
set('repository', '{git-репозиторий.git}');
set('keep_releases', 3);
 
add('shared_files', [
    '.env'
]);
add('shared_dirs', [
    'storage'
]);
add('writable_dirs', [
    'bootstrap/cache',
    'storage',
    'node_modules',
    'vendor'
]);
 
// Servers
set('default_stage', 'dev');
 
server('dev', '{dev-server}')
    ->user('deployer')
    ->identityFile('~/Code/.ssh/id_rsa.pub', '~/Code/.ssh/id_rsa')
    ->stage('dev')
    ->set('deploy_path', '/var/www/html');
 
server('prod', '{prod-server}')
    ->user('deployer')
    ->identityFile('~/Code/.ssh/id_rsa.pub', '~/Code/.ssh/id_rsa')
    ->stage('prod')
    ->set('deploy_path', '/var/www/html');
 
 
// Tasks
desc('Run NPM install');
task('deploy:install-npm', function () {
    cd('{{release_path}}');
    run('npm install');
});
 
desc('Run GULP');
task('deploy:compile-assets', function () {
    cd('{{release_path}}');
    run('gulp --production');
});
 
desc('Restart PHP-FPM service');
task('php-fpm:restart', function () {
    run('sudo systemctl restart php7.0-fpm.service');
});
 
after('artisan:config:cache', 'deploy:install-npm');
after('deploy:install-npm', 'deploy:compile-assets');
after('deploy:writable', 'artisan:migrate');
after('deploy:symlink', 'php-fpm:restart');
tweet Share