カテゴリー: BackEnd

DeployerでLaravelをデプロイ! 初期設定〜レシピのカスタマイズまで

はじめに

DeployerはPHP製のデプロイツールです。汎用性のあるツールですが、Laravelはかなり手早くデプロイできるので、実際にデプロイし、レシピをカスタマイズする方法を紹介します。

Deployerの導入

前提条件

サーバ側

  • Gitインストール済み
  • nginx, php-fpmインストール済み
  • mysqlセットアップ済み

ローカル側

  • Laravelのプロジェクトを作成済み
  • Gitリポジトリにプッシュ可能

インストール

composerでDeployerをインストールします。

$ composer require deployer/deployer --dev

インストールが完了したら、Deployerの初期設定を行います。

$ php vendor/bin/dep init

まず、プロジェクトの種類を選択します。今回はLaravelなので 1とします。

 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

次に、Gitリポジトリのリモートを入力します。デプロイしたいプロジェクトのリモートのURLを指定してください。

 Repository []:
 > git@github.com:your/repository.git

最後に、Deployer開発チームにデータを送信するかどうか聞かれます。これはどちらでも構いません。今回は yesにします。

 Contribute to the Deployer Development

 In order to help development and improve the features in Deployer,
 Deployer has a setting for usage data collection. 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.

・・・

 Do you confirm? (yes/no) [yes]:
 > yes

これで完了です。

デプロイの設定

デプロイスクリプト

デプロイスクリプトはプロジェクトのルートにある deploy.phpで行います。Laravelテンプレートを選択したため、 基本的なことをほぼテンプレートのままできてしまいます。これに少し手を加えたので、その辺りも含めて説明します。

<?php
namespace Deployer;

require 'recipe/laravel.php';

// プロジェクト名
set('application', 'laravel-deployer');

// Gitリモートリポジトリ
set('repository', 'git@github.com:your/repository.git');

// [任意] git cloneコマンドにttyを割り当てる。(デフォルトはfalse)
set('git_tty', true);

// デプロイ間で共有されるファイルとディレクトリ
add('shared_files', []);
add('shared_dirs', ['vendor']);

// ウェブサーバから書き込み可能なディレクトリ
add('writable_dirs', ['bootstrap/cache', 'storage']);


// Hosts

host('project.com')
    ->stage('production')
    ->identityFile('~/.ssh/your-key.pem')
    ->user('admin')
    ->set('deploy_path', '/var/www/{{application}}');

// Tasks (テンプレートに含まれていますが、サンプルなので不要)

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

// [任意] デプロイ失敗時に自動的にアンロックする
after('deploy:failed', 'deploy:unlock');

// Migrate database before symlink new release.

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

// 本番環境用のenvファイルをコピーする
before('deploy:shared','deploy:copy:env');

desc('各環境用のenvファイルをコピー');
task('deploy:copy:env', function () {
    run("cp {{release_path}}/.env.{{stage}} {{deploy_path}}/shared/.env");
});

いくつかポイントを紹介します。

  • require 'receipe/laravel.php
    Laravelテンプレートを選択した時に使用されるLaravelレシピです。Laravelの様々なartisanコマンドが使えるレシピです。詳細は後述します。
  • add('shared_dirs', ['vendor']);
    デプロイ時に共有するディレクトリを指定します。ここの指定したディレクトリは、実際のデプロイパス上ではシンボリックリンクに置き換えられ、実態は sharedディレクトリの中に存在することになります。そのため、 vendorディレクトリが毎回のデプロイで削除されずに使いまわされる、 composer installが早くなります。
    また、Laravelレシピ側でデフォルトで storageが指定されています。これにより、デプロイ毎にstorage配下のファイルが消えずに、使用し続けることができます。
  • .envファイルのコピー
    Laravelレシピでは、.envファイルは shared/.envへのシンボリックリンクが作成されるため、このパスにenvファイルをコピーします。Deployer内で使用できる変数を使用してパスとステージを特定して、適切な.envファイルを配置するようにしています。
    例えばステージ名がproductionであれば、 .env.productionをコピーするように実装しました。
  • run()では、コマンド内で {{変数名}}とすることで、変数を使用することができます。例えば、次のような変数があります。
    • deploy_path: host()->set()で指定したデプロイディレクトリを取得できます。
    • release_path: デプロイ中のソースが入っているディレクトリを取得することができます。今回の例では /var/www/laravel-deployer/releases/1が取得できます。 1はリリース毎にインクリメントされます
    • stage: 今デプロイしているステージ名を取得できます。ステージ名は host()->stage()で設定し、デプロイコマンドで指定できます。

サーバサイドの設定

今回は割愛しますが、設定に合わせて次の対応を行います。

  • /var/www/laravel-deployerディレクトリの作成
  • gitのインストール
  • ssh-keygenでキーを生成し、GithubにDeploy Keyとして登録する。
  • nginx.confで、rootを /var/www/laravel-deployer/current/publicにしておく。

デプロイコマンド

それでは、実際にデプロイしてみます。

$ vendor/bin/dep deploy production

deployは、Laravelレシピで定義されているタスク名で、ここにデプロイに最低限必要な一通りのタスクがまとまっています。productionは、ステージ名です。
実際に実行すると、次のようになります。

$ vendor/bin/dep deploy production
✈︎ Deploying master project.com
✔ Executing task deploy:prepare
✔ Executing task deploy:lock
✔ Executing task deploy:release
➤ Executing task deploy:update_code
Cloning into '/var/www/laravel-deployer/releases/2'...
remote: Enumerating objects: 11, done.
remote: Counting objects: 100% (11/11), done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 6 (delta 3), reused 6 (delta 3), pack-reused 0
Receiving objects: 100% (6/6), done.
Resolving deltas: 100% (3/3), completed with 3 local objects.
Enumerating objects: 187, done.
Counting objects: 100% (187/187), done.
Compressing objects: 100% (135/135), done.
Writing objects: 100% (187/187), done.
Total 187 (delta 34), reused 187 (delta 34)
Connection to project.com closed.
✔ Ok
✔ Executing task deploy:copy:env
✔ Executing task deploy:shared
✔ Executing task deploy:vendors
✔ Executing task deploy:writable
✔ Executing task artisan:storage:link
✔ Executing task artisan:view:cache
✔ Executing task artisan:config:cache
✔ Executing task artisan:optimize
✔ Executing task artisan:migrate
✔ Executing task deploy:symlink
✔ Executing task deploy:unlock
✔ Executing task cleanup
Successfully deployed!

Deployerのディレクトリ構成

初めてデプロイが完了すると、指定したパスにディレクトリが生成されます。

  • current: releases/nへのシンボリックリンク。(nは最新のデプロイ番号)
  • releases: デプロイしたソースの実態
  • shared: deploy.phpで指定した共有ディレクトリの格納場所 (今回の例では、vendor, storageが入っています。)

そのため、Webサーバは current配下を見れば最新のソースが格納されていることになります。

レシピ

requireされている receipe/laravel.phpには他にも様々なレシピがあります。デプロイタスクに組み込むこともできますし、直接タスクを実行することもできます。
例えば、マイグレーションはデプロイタスクに組み込まず、任意のタイミングで実行したい場合は $ vendor/bin/dep artisan:migrate productionで実行できます。

レシピをカスタマイズする方法

レシピをカスタマイズするには、deploy.phpの実装でいくつかやり方があるので、紹介します。

コンフィグの追加/上書き

レシピで set()で定義されているコンフィグは、追加したり上書きしたりできます。追加するには add(キー, 値)、上書きするには set(キー、値)とします。

任意のタスクを、定義済みタスクの前後に追加

deployタスクのように、一連のタスクを実行するタスクの中に、任意のタスクを追加したい場合があります。先ほどの例では次のように使用しています。

// 本番環境用のenvファイルをコピーする
before('deploy:shared','deploy:copy:env');

deploy:copy:envは自前のタスクですが、これを deploy:sharedの前に実行させています。
deploy:sharedタスクは、実際のソースをシンボリックリンクに置き換えますが、その際に shared内に対象ディレクトリもしくはファイルが無ければ、それぞれからの状態で生成します。
これが.envファイルで行われてしまうと、後からコピーする時に、空ファイルをいちいち削除しなければならなくなってしまうため、事前にenvファイルを shared/.envにコピーさせたかったからです。
参考までに実際の deploy:sharedタスクで、shared_filesの処理は次のようになっています。

<?php
/* (c) Anton Medvedev <anton@medv.io>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
namespace Deployer;

use Deployer\Exception\Exception;

desc('Creating symlinks for shared files and dirs');
task('deploy:shared', function () {
    $sharedPath = "{{deploy_path}}/shared";

    // ・・・(shared_dir部分は省略)・・・

    foreach (get('shared_files') as $file) {
        $dirname = dirname(parse($file));

        // shared_filesへのパスが、sharedディレクトリに存在しない場合は、まずディレクトリを作成する。
        if (!test("[ -d {$sharedPath}/{$dirname} ]")) {
            run("mkdir -p {$sharedPath}/{$dirname}");
        }

        // shared_filesがsharedディレクトリに存在せず、releaseディレクトリ内に元ファイルがある場合は、
        // releaseディレクトリ内の元ファイルをそのままコピーする
        if (!test("[ -f $sharedPath/$file ]") && test("[ -f {{release_path}}/$file ]")) {
            // Copy file in shared dir if not present
            run("cp -rv {{release_path}}/$file $sharedPath/$file");
        }

        // releaseディレクトリ内に元ファイルがある場合は、のファイルは削除する。
        run("if [ -f $(echo {{release_path}}/$file) ]; then rm -rf {{release_path}}/$file; fi");

        // releaseディレクトリ内に元ファイルまでのディレクトリが存在しなければ作成する
        run("if [ ! -d $(echo {{release_path}}/$dirname) ]; then mkdir -p {{release_path}}/$dirname;fi");

        // sharedにファイルを新規作成(touchなので、既存ファイルの中身は置き換わらない。)
        run("touch $sharedPath/$file");

        // releaseディレクトリの対象パスに、sharedディレクトリ内ファイルへのシンボリックリンクを貼る。
        run("{{bin/symlink}} $sharedPath/$file {{release_path}}/$file");
    }
});

 

おまけ

Github Actionsでもデプロイできるようにしてみたので、簡単に紹介します。作成したワークフローはこちらです。

name: Laravel

on:
  push:
    branches: [ master ]

jobs:
  laravel-tests:

    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v2
    - name: Copy .env
      run: php -r "file_exists('.env') || copy('.env.production', '.env');"
    - name: Install Dependencies
      run: composer install -q --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist
    - name: Generate key
      run: php artisan key:generate
    - name: deploy
      run: |
        mkdir ~/.ssh
        echo "${{ secrets.SECRET_KEY }}" > ~/.ssh/your-project.pem
        ssh-keyscan -H "project.com" >> ~/.ssh/known_hosts
        chmod 600  ~/.ssh/your-project.pem
        vendor/bin/dep deploy production

3つポイントがあります。

  1. SSHキーは、GithubのSecretsに登録しておきます。
  2. deploy.phpの git_ttyはGithub Actionsでは動作しなかったので、falseにします。
  3. あらかじめknown_hostsに登録しておきます。

個人用のプロジェクト程度であれば、これで十分便利に使用できると思いました。

さいごに

いかがでしたか。Deployerはテンプレートやレシピが充実していて、主要なフレームワークやCMSで簡単にデプロイできることが分かりました。Laravelの公式パッケージで envoyと言うツールもありますが、Deployerの方がレシピが豊富なので便利だと思いました。

おすすめ書籍

カイザー

シェア
執筆者:
カイザー
タグ: phplaravel

最近の投稿

フロントエンドで動画デコレーション&レンダリング

はじめに 今回は、以下のように…

3週間 前

Goのクエリビルダー goqu を使ってみる

はじめに 最近携わっているとあ…

1か月 前

【Xcode15】プライバシーマニフェスト対応に備えて

はじめに こんにちは、suzu…

2か月 前

FSMを使った状態管理をGoで実装する

はじめに 一般的なアプリケーシ…

3か月 前