DeployerはPHP製のデプロイツールです。汎用性のあるツールですが、Laravelはかなり手早くデプロイできるので、実際にデプロイし、レシピをカスタマイズする方法を紹介します。
サーバ側
ローカル側
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
add('shared_dirs', ['vendor']);
shared
ディレクトリの中に存在することになります。そのため、 vendor
ディレクトリが毎回のデプロイで削除されずに使いまわされる、 composer install
が早くなります。storage
が指定されています。これにより、デプロイ毎にstorage配下のファイルが消えずに、使用し続けることができます。shared/.env
へのシンボリックリンクが作成されるため、このパスにenvファイルをコピーします。Deployer内で使用できる変数を使用してパスとステージを特定して、適切な.envファイルを配置するようにしています。.env.production
をコピーするように実装しました。run()
では、コマンド内で {{変数名}}
とすることで、変数を使用することができます。例えば、次のような変数があります。 deploy_path
: host()->set()
で指定したデプロイディレクトリを取得できます。release_path
: デプロイ中のソースが入っているディレクトリを取得することができます。今回の例では /var/www/laravel-deployer/releases/1
が取得できます。 1
はリリース毎にインクリメントされますstage
: 今デプロイしているステージ名を取得できます。ステージ名は host()->stage()
で設定し、デプロイコマンドで指定できます。今回は割愛しますが、設定に合わせて次の対応を行います。
ssh-keygen
でキーを生成し、GithubにDeploy Keyとして登録する。/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!
初めてデプロイが完了すると、指定したパスにディレクトリが生成されます。
そのため、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つポイントがあります。
git_tty
はGithub Actionsでは動作しなかったので、falseにします。個人用のプロジェクト程度であれば、これで十分便利に使用できると思いました。
いかがでしたか。Deployerはテンプレートやレシピが充実していて、主要なフレームワークやCMSで簡単にデプロイできることが分かりました。Laravelの公式パッケージで envoy
と言うツールもありますが、Deployerの方がレシピが豊富なので便利だと思いました。