DeployerでLaravelをデプロイする

GithubやBitbucketなどGitリポジトリで管理されているLaravelのプロジェクトをDeployerを使ってデプロイしてみます。

# Deployerとは

Deployerの[公式サイト](https://deployer.org/)には以下のように書かれています。
> A deployment tool written in PHP with support for popular frameworks out of the box.

様々なフレームワークをサポートしているデプロイツールです。
もちろんLaravelもサポートされています。

# デプロイ手順

## 1. Deployerのインストール

composerを使ってインストールします。

```
$ composer require deployer/deployer --dev
```

これで `vendor/bin/dep` でDeployerを実行できるようになります。

# 2. 設定ファイル作成

今回はDeployerが提供するLaravel用の設定ファイルの雛形を編集していきます。
以下を実行すると `deploy.php` が作成されます。

```
$ vendor/bin/dep init -t Laravel // Laravel用の設定ファイルの雛形を作成
```

次にデプロイ先のHost情報を記載する `hosts.yml` を作成します。

`<hostname>` と `<username>` には適切な値を設定してください。

```hosts.yml
staging:
  hostname: <hostname> # デプロイ先のホスト名
  stage: staging
  roles:
    - app
  deploy_path: /var/www/html
  branch: master
  become: <username> # デプロイを行うユーザ
production:
  hostname: <hostname> # デプロイ先のホスト名
  stage: production
  roles:
    - app
  deploy_path: /var/www/html
  branch: master
  become: <username> # デプロイを行うユーザ
```

先ほど作成した `deploy.php` を編集します。

```deploy.php
<?php
namespace Deployer;

require 'recipe/laravel.php';

// Project name
set('application', 'アプリケーション名');

// Project repository
set('repository', 'Gitリポジトリ');

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

// Shared files/dirs between deploys
add('shared_files', []);
add('shared_dirs', []);

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

// 統計情報を送信しない
set('allow_anonymous_stats', false);

// Hosts
inventory('hosts.yml');

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

// shared/.envを.env.{stage}で上書きする
after('deploy:shared', 'overwrite-env');

// Migrate database before symlink new release.
before('deploy:symlink', 'artisan:migrate');


// Tasks

task('deploy', function () {
    // 本番への反映は確認を挟む
    if (input()->hasArgument('stage') && (input()->getArgument('stage') === 'production')) {
        if (!askConfirmation('productionに反映して問題ありませんか？', true)) {
            writeln('deploy was stopped');
            return;
        }
    }
    invoke('deploy:laravel');
});

desc('shared/.envを.env.{stage}で上書き');
task('overwrite-env', function () {
    $stage = get('stage');
    $src = ".env.${stage}";
    $deployPath = get('deploy_path');
    $sharedPath = "${deployPath}/shared";
    run("cp -f {{release_path}}/${src} ${sharedPath}/.env");
});

/**
 * Main task
 */
desc('Deploy your project');
task('deploy:laravel', [
    'deploy:info',
    'deploy:prepare',
    'deploy:lock',
    'deploy:release',
    'deploy:update_code',
    'deploy:shared',
    'deploy:vendors',
    'deploy:writable',
    'artisan:storage:link',
    'artisan:view:clear',
    'artisan:cache:clear',
    'artisan:config:cache',
    'artisan:optimize',
    'deploy:symlink',
    'deploy:unlock',
    'cleanup',
]);
```

### .envの扱い
デフォルトの挙動では `deploy:shared` タスクで `{deploy_path}/shared` に `.env` がなければ、空の `.env` を作成して `{deploy_path}/shared/.env` から `{release_path}/.env` へのシンボリックリンクを貼ります。
今回は各環境の `.env` を `.env.{stage}` のようにしてプロジェクト内に含める運用をしたかったので、 `deploy:shared` タスクの直後に `{deploy_path}/shared/.env` を `.env.{stage}` で上書きするようにしました。

## 3. デプロイ先サーバの設定

### 1. SSH接続するユーザがsudoできるようにする

`hosts.yml` の `become` で指定したユーザになるためにsudoできる必要があるため、SSH接続するユーザがsudoできるようにする。

### 2. デプロイ先のディレクトリをデプロイユーザが作成できるように権限を確認

Deployerは `hosts.yml` の `deploy_path` で指定したディレクトリを作成してくれるのでデプロイするユーザに書き込み権限があるか確認しておく。

### 3. 初回Gitクローン時にSSH接続の確認をしないようにする

```~/.ssh/config
Host github.com
  StrictHostKeyChecking no

Host bitbucket.org
  StrictHostKeyChecking no
```

### 4. Webサーバの設定

Webサーバのドキュメントルートを `{deploy_path}/current/public` に設定する。

また、Nginx+PHP-FPM環境でOPcacheを利用する場合は以下のように `SCRIPT_FILENAME` と `DOCUMENT_ROOT` を設定すると良いです。
詳しくは[こちら](https://qiita.com/kotarella1110/items/726004497d3605441fb6)を参照。

```
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
fastcgi_param DOCUMENT_ROOT $realpath_root;
```

## 4. デプロイ

```
$ vendor/bin/dep deploy staging // ステージング
$ vendor/bin/dep deploy production // 本番
```

## 5. おまけ

デプロイ開始時と結果をSlack通知します。

composerでレシピを追加。

```
$ composer require deployer/recipes --dev 
```

```deploy.php
・・・略・・・

require 'recipe/slack.php';

・・・略・・・

// deployタスクを上書きした場合必要。
after('deploy', 'success');

// Slack Notification
set('slack_webhook', '<Webhook URL>');
set('slack_text', '_{{user}}_ が `{{branch}}` を *{{target}}* へデプロイしています...🚀');
set('slack_success_text', '*{{target}}* へのデプロイが成功しました。🎉');
set('slack_failure_text', '*{{target}}* へのデプロイが失敗しました。😂');
before('deploy', 'slack:notify');
after('success', 'slack:notify:success');
after('deploy:failed', 'slack:notify:failure');
```

# 参照
- [Deployer](https://deployer.org/)
- [DeployerでLaravelをデプロイする](https://qiita.com/shalman/items/c09eb23137a9d5269f10)
- [SSHのknown_hosts](https://qiita.com/unosk/items/81e81a8a10c9ceb8c36e)
- [OPcache が有効で且つ PHP-FPM を使用した ウェブサーバで、Symlink ベースのデプロイするとコードが反映されない](https://qiita.com/kotarella1110/items/726004497d3605441fb6)