yujiro's blog

「インターネット上で正しい答えを得る最善の方法は、質問することではない。間違った答えを投稿することだ」by ウォード・カニンガム

Deployer を使ってLaravel5 をDeploy してみる

Deployer はPHP 製のデプロイツールです。サクッと設定が書けて楽にデプロイ環境が構築できる印象でした。capistrano に似てるなって思いました。

今回はLaravel5.3 をデプロイする前提で書いていきます。

参考にしたのは

https://deployer.org/docs/

です。

環境

Laravel 5.3

Deployer 6.0.3

インストール

deployer.phar のダウンロード

$ curl -LO https://deployer.org/deployer.phar

パス通して dep コマンドで使えるようにする

$ sudo mv deployer.phar /usr/local/bin/dep

実行権限付与

$ sudo chmod +x /usr/local/bin/dep

composer で project に Deployerのソースコードをinstall

$ composer require deployer/deployer

使い方

deploy.php の作成

$ dep init

                                           
  Welcome to the Deployer config generator  
                                            


 This utility will walk you through creating a deploy.php file.
 It only covers the most common items, and tries to guess sensible defaults.
 
 Press ^C at any time to quit.

 Please select your project type [Common]:
  [0 ] Common
  [1 ] Laravel
  [2 ] Symfony
  [3 ] Yii
  [4 ] Yii2 Basic App
  [5 ] Yii2 Advanced App
  [6 ] Zend Framework
  [7 ] CakePHP
  [8 ] CodeIgniter
  [9 ] Drupal
  [10] TYPO3

と聞かれるので

> 1

と入力。次に

Repository []:

と聞かれるのでgitのリポジトリを入力。ちなみにあとから設定できますので空でEnterでも全然構いません。

Contribute to the Deployer Development
 
 In order to help development and improve Deployer features in,
 Deployer has a setting for collection of usage data. This function
 collects anonymous usage data and sends it to Deployer. The data is
 used in Deployer development to get reliable statistics on which
 features are used (or not used). The information is not traceable
 to any individual or organization. Participation is voluntary,
 and you can change your mind at any time.
 
 Anonymous usage data contains Deployer version, php version, os type,
 name of the command being executed and whether it was successful or not,
 exception class name, count of hosts and anonymized project hash.
 
 If you would like to allow us to gather this information and help
 us develop a better tool, please add the code below.
 
     set('allow_anonymous_stats', true);
 
 This function will not affect the performance of Deployer as
 the data is insignificant and transmitted in separate process.
 
  Do you confirm? (yes/no) [yes]:
> yes

とすると

Successfully created: /path/to/proj/deploy.php

となります。

deploy.php ですが、私は以下のようにしました。

<?php
namespace Deployer;

require 'recipe/laravel.php';

// Project name
set('application', 'my_project');

// Project repository
set('repository', 'git@github.com:hoge/hoge.git');

// [Optional] Allocate tty for git clone. Default value is false.
set('git_tty', true);

// Shared files/dirs between deploys
add('shared_files', ['.env']);
set('shared_dirs', [
    'storage/app',
    'storage/framework/cache',
    'storage/framework/sessions',
    'storage/framework/views',
    'storage/logs'
]);

// Writable dirs by web server
add('writable_dirs', []);


set('bin/php', function () {
    return '/home/ubuntu/.phpenv/shims/php';
});
// Hosts

host('hogehoge.com') // ← ホスト名 もしくは Global IP 
    ->stage('production')
    ->user('ubuntu')
    ->port(22)
    ->identityFile('~/.ssh/id_rsa')
    ->set('deploy_path', '/path/to/project/');

// Tasks

task('build', function () {
    run('cd {{release_path}} && build');
});

after('deploy', 'deploy:db_seed');

desc('Execute artisan db:seed');
task('deploy:db_seed', function () {
    $output = run('{{bin/php}} {{release_path}}/artisan db:seed --class=UsersTableSeeder');
    writeln('<info>' . $output . '</info>');
    $output = run('{{bin/php}} {{release_path}}/artisan db:seed --class=SomethingTableSeeder');
    writeln('<info>' . $output . '</info>');
});

// [Optional] if deploy fails automatically unlock.
after('deploy:failed', 'deploy:unlock');

// Migrate database before symlink new release.

before('deploy:symlink', 'artisan:migrate');

解説していきます。

set('repository', 'git@github.com:hoge/hoge.git');

git のリポジトリのurlです。

set('git_tty', true);

これを true にすると http で git 接続してる場合、Deployコマンド実行後、username と password の入力プロンプトが表示されます。ssh で git に接続する場合はfalse でOKです。

set('shared_dirs', []);

ここに入力されたディレクトリは currentディレクトリの中に実体が作られず、shared ディレクトリへのシンボリックリンクが貼られます。この辺はcapistrano と同じですね。

deployer の仕組みとして、deploy すると毎回、その時の最新のバージョンのソースコードcurrent ディレクトリに入ります。(これもシンボリックリンクなのですが)

(因みにそこがルートになるので、current の下は app とか .env とかが置かれています。)

つまりデプロイするたびに current ディレクトリが全て上書きされるってことです。

でもそうするとLogやらキャッシュやらはどうなるの??毎回消えちゃうの??って話になりますよね。それを回避するのがこの shared っていう仕組みです。消したくないやつをここに記述します。

因みに set としているのは、 add をつかうと例外で死ぬためです。

add を使うと、

  [Deployer\Exception\Exception]                        
  Can not share same dirs `storage/app` and `storage`.  

上記のエラーがでました。色々調べてコアファイルもみたんですが、このshared_dirs は下記ファイルで同じディレクトリ or 親ディレクトリが存在していないかのチェックをしているのですが、

vendor/deployer/deployer/recipe/deploy/shared.php

その判定ロジックが

<?php //〜

    foreach (get('shared_dirs') as $a) {
        foreach (get('shared_dirs') as $b) {
            if ($a !== $b && strpos(rtrim($a, '/') . '/', rtrim($b, '/') . '/') === 0) {
                throw new Exception("Can not share same dirs `$a` and `$b`.");
            }
        }
    }

となっていました。それで何故か

get('shared_dirs')

の返り値が、自ら設定した

[
    'storage/app',
    'storage/framework/cache',
    'storage/framework/sessions',
    'storage/framework/views',
    'storage/logs'
]

ではなく、

[
    'storage',
    'storage/app',
    'storage/framework/cache',
    'storage/framework/sessions',
    'storage/framework/views',
    'storage/logs'
]

となっていました。 これでは ループが 1つめのforeach の 2つめの値の

'storage/app',

になったとき、

if('storage/app/' !== 'storage/' && strpos('storage/app/', 'storage/') === 0){
    // 試合終了
}

となって試合終了します。

まぁ色々謎なんですが、set つかうと

[
    'storage/app',
    'storage/framework/cache',
    'storage/framework/sessions',
    'storage/framework/views',
    'storage/logs'
]

になるので大丈夫です。

追記

なんか、公式でも

set('shared_dirs', []);

set が使われていました。

なんか僕が勝手に add 使ってハマってたみたいでした。

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

shared_dirs のファイル版です。Laravel5 の場合はこの .env だけでOKでしょう。

add('writable_dirs', []);

List of dirs which must be writable for web server.

とありますので、webサーバーが書き込めるようにPermission をよろしく設定してくれるのでしょう。

set('bin/php', 〜

set('bin/php', function () {
    return '/home/ubuntu/.phpenv/shims/php';
});

僕はphp は phpenv 使っているので(本番環境でもその予定)、そのための設定です。これやらないと /usr/bin/php 使おうとして死にました。

host('hogehoge.com')

host('hogehoge.com') // ← ホスト名 もしくは Global IP 
    ->stage('production')
    ->user('ubuntu')
    ->port(22)
    ->identityFile('~/.ssh/id_rsa')
    ->set('deploy_path', '/path/to/project/');

stage は production とか staging とか設定します。多分なんでもよい。

デプロイするときに

$ dep deploy production

上記のように環境を記述してコマンド実行するので、その時に名前が一致しているstage の host 設定を使うということです。

task('artisan:db:seed',

desc('Execute artisan db:seed');
task('artisan:db:seed', function () {
    $output = run('{{bin/php}} {{release_path}}/artisan db:seed --class=UsersTableSeeder');
    writeln('<info>' . $output . '</info>');
    $output = run('{{bin/php}} {{release_path}}/artisan db:seed --class=SomethingTableSeeder');
    writeln('<info>' . $output . '</info>');
});

自分の好きなタスクを設定できます。 試しにdb:seed コマンドを実行するタスクを書いてみました。

あとは

$ dep deploy production -vvv

(-vvv は詳細表示。)

を実行してログをぼけーっと眺めるだけでよろしくやってくれます。

以上です。