Deployerを使ってみた

DeployerはPHP製のデプロイツールです。

ダウンロード

http://deployer.org/download から最新のdeployer.pharをダウンロードし、実行可能にします。

ドキュメント上、deployer.phar/usr/local/binにインストールとなっていますが、プロジェクト内にデプロイ用のディレクトリを作りインストールすることにします。

% cd /path/to/PROJECT/ROOT
% mkdir deploy
% cd deploy
% wget http://deployer.org/deployer.phar
% chmod +x deployer.phar
% ./deployer.phar --version
Deployer version 3.0.10  

タスクを定義する

同ディレクトリにdeploy.phpを作り、個々にデプロイ用のタスクを定義していきます。

<?php

// タスク定義
task('my_task', function() {  
    write('<comment>executing my task!</comment>');
})->desc('タスクの説明');

write()はコンソールに出力するためのメソッドです。渡す文字列には<comment>, <info>, <error>のマークアップもできます。desc->('foo')でヘルプを表示した時に出力する説明文を入れておきます。

また、あるタスクの前後に実行するタスクを定義することもできます。

task('my_task:before', function() {  
    write('<comment>my_taskが実行される前に実行するタスク</comment>');
});

task('my_task:done', function() {  
    write('<comment>my_taskが完了後に実行するタスク</comment>');
});

before('my_task', 'my_task:before');  
after('my_task', 'my_task:done');  

これでタスクを実行してみると以下の様に出力されるようになります。

% ./deployer.phar my_task

➤ Executing task my_task
executing my task!✔ Ok  
➤ Executing task my_task:done
my_taskが完了後に実行するタスク✔ Ok  
➜  deploy git:(feature/prototype) ✗ ./deployer.phar my_task
➤ Executing task my_task:before
my_taskが実行される前に実行するタスク✔ Ok  
➤ Executing task my_task
executing my task!✔ Ok  
➤ Executing task my_task:done
my_taskが完了後に実行するタスク✔ Ok  

タスクはグルーピングもできるのでdeployタスクを一連の作業のグループとすることもできます。

task('deploy', [  
    'deploy:prepare',
    'deploy:update',
    // ...
]);

次にデプロイファイルの実行時に受け付ける引数を定義します。

<?php  
use \Symfony\Component\Console\Input\InputArgument;

// ...

// argument(name, mode, description, default)
argument('stage', InputArgument::OPTIONAL, 'タスクを実行するサーバ名を指定する');  

同様にオプションも定義できます。

use \Symfony\Component\Console\Input\InputOption;

// ...

// option(name, shortcut, mode, description, default)
option('tag', null, InputOption::VALUE_OPTIONAL, 'デプロイするタグ名');  

引数とオプションはタスク内で以下のように取得できます。

$arg = input()->getArgument('stage');
$tag = input()->getOption('tag');

この時点でdeploy.phpに記述している内容が、list--helpで出力されるようになっています。

% ./deployer.phar list
Deployer version 3.0.10

Usage:  
  command [options] [arguments]

Options:  
  -h, --help            Display this help message
  -q, --quiet           Do not output any message
  -V, --version         Display this application version
      --ansi            Force ANSI output
      --no-ansi         Disable ANSI output
  -n, --no-interaction  Do not ask any interactive question
  -f, --file[=FILE]     Specify Deployer file.
      --tag[=TAG]       デプロイするタグ名
  -v|vv|vvv, --verbose  Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug

Available commands:  
  hello        foo.htmlを作成する
  help         Displays help for a command
  list         Lists commands
  self-update  Updates deployer.phar to the latest version
  worker       Deployer uses workers for parallel deployment.

サーバを定義する

デプロイ先のサーバーを定義するにはserver('name', 'host', port)で指定します。

server('staging', 'your-hostname', 22)  
    ->user(USERNAME)
    ->identityFile()
    //->password(PASSWORD)
    ->env('deploy_path', '/var/www/html')
    ->stage('stage');

先ほどのtaskargumentoptionと組み合わせ

% ./deployer.phar [task] [stage]

を実行することで、特定のサーバへデプロイすることになります。

なお、サーバはYAML形式で別ファイルに定義しておいて、deploy.phpから読みこむだけにすることもできます。

staging:  
  host: staging.example.com
  user: www
  identity_file: ~
  stage: stage
  deploy_path: /var/www/html

production:  
  host: example.com
  user: www
  identity_file: ~
  stage: production
  deploy_path: /var/www/html
serverList('servers.yml');  

サーバ上でタスクを実行する

これまでで、サーバーを定義し実行させたいタスクの定義方法が分かったので、実際に簡単なタスクをサーバ上で実行してみます。

<?php

use \Symfony\Component\Console\Input\InputArgument;  
use \Symfony\Component\Console\Input\InputOption;

serverList('servers.yml');

task('hello', function() {  
    write('<info>create foo.html</info>');

    $arg = input()->getArgument('stage');
    $tag = input()->getOption('tag');

    $output = "Deploying $tag on $arg server!";

    run("touch foo.html && echo \"$output\" > foo.html");

})->desc('foo.htmlを作成する');

argument('stage', InputArgument::OPTIONAL, 'タスクを実行するサーバ名を指定する');  
option('tag', null, InputOption::VALUE_OPTIONAL, 'デプロイするタグ名');  

run()はサーバー上で実行するコマンドを文字列で渡します。コマンドはサーバで定義したdeploy_pathのパス上で実行されます。ローカルで実行させたいタスクの場合はrunLocally()を使います。

Recipes

自力で簡単なタスクを定義して実行してみましたが、Deployerでは一般的なタスクがRecipeという形で用意されています。

https://github.com/deployphp/deployer/tree/master/recipe

common.phpをベースとしたLaravelやWordpress用のrecipeが既に用意されています。これらのrecipeをdeploy.phpでオーバライドしたり、新しいタスクを追加したりでオリジナルのデプロイスクリプトを作成できるようになります。

Laravelをデプロイしてみる

Laravelで作ったアプリケーションをデプロイしてみることにします。ここでは、とりあえずプロジェクトの骨子だけ作って、それをそのままデプロイします。

% laravel new DeployMe
% cd DeployMe
% mkdir deploy
% touch deploy/deploy.php
% touch deploy/server.yml
% git init
% git remote add origin git@....

サーバの情報はdeploy/server.ymlに記述します。

staging:  
  host: your-hostname
  user: your-username
  password: your-password
  stage: stage
  deploy_path: ~/www

production:  
  host: your-hostname
  port: 22
  user: your-username
  identity_file: ~
  stage: production
  deploy_path: /var/www

deploy.phpではLaravelのrecipeとserver.ymlを読み込みます。

<?php

require 'recipe/laravel.php';

serverList('server.yml');  

recipe/laravel.phpではdeployというタスクグループが以下のように定義されています。

task('deploy', [  
    'deploy:prepare',
    'deploy:release',
    'deploy:update_code',
    'deploy:vendors',
    'deploy:shared',
    'deploy:symlink',
    'cleanup',
])->desc('Deploy your project');
after('deploy', 'success');  

それぞれのタスクはrecipe/common.phpで定義されているので順を追っていくと、大体以下の様なことが行われます。

  • deploy:prepare -> サーバに接続、ソースコードを配置するディレクトリを作る
  • deploy:release -> デプロイするソースコードを配置するためのディレクトリを準備
  • deploy:update_code -> git cloneでソースを取得する
  • deploy:vendors -> composerで依存ライブラリをインストールする
  • deploy:shared -> 環境用の設定ファイルなどを配置
  • deploy:symlink -> 最新リリース版へのシンボリックリンクを貼る
  • cleanup -> 古いリリース版をアーカイブする

deploy.phpではdeploy:update_codeで必要となるGitレポジトリを定義しておきます。

set('repository', 'git@...');  

deploy:update_codeは、repositoryで指定したレポジトリをcloneしているので、サーバー上でアクセス権を設定しておく必要もあります。Github/Bitbucketを使っているなら、SSHキーの追加画面でサーバの公開鍵を登録しておきます。

準備ができたらデプロイをしてみます。

% ./deployer.phar deploy staging
✔ Executing task deploy:prepare
✔ Executing task deploy:release
✔ Executing task deploy:update_code
✔ Executing task deploy:vendors
✔ Executing task deploy:shared
✔ Executing task deploy:symlink
✔ Executing task cleanup
➤ Executing task success
Successfully deployed!  
✔ Ok

サーバ上のデプロイ先を確認すると、以下の様なディレクトリが作られています。

current`release`内の最新版ソースへのシンボリックリンク
releases3世代分のリリースを保管しておく。デプロイする度に入れ替わる
shared各リリースで共通のファイル(`storage`や`.env`)

release内で保管しておくリリースの数はrecipe/common.phpで設定されているものなので、必要に応じて設定します。

set('keep_releases', 5);  

サーバ上ではApacheなりNginxのドキュメントルートをcurrentのソースに指定しておきます。(Laravelならcurrent/public)

何かやらかした場合

% ./deployer.phar rollback staging

で、release内の最新のアーカイブが消え、currentが一つ前のリリースにリンクしてくれます。

まとめ

小規模なPHPプロジェクトで、チーム的にRuby/Pythonなど他の言語のツールを使う余裕がない場合は、便利な気がしています。

長々と書いてきましたが、実際のところrecipeを使えばGitレポジトリを指定するだけでデプロイできるのでお手軽です。