LaravelプロジェクトをレンタルサーバーのXserverへ、安全かつ効率よくデプロイするために、PHP製のデプロイツール「Deployer」を使っています。この記事では、ローカルでビルドした成果物を rsync でサーバーへ転送する構成を、deploy.php の完成例つきで解説します。Git経由デプロイとの違い、shared / writable 設定、.env・storage・bootstrap/cacheの配置、migrationやqueue再起動、rollback、そしてXserver特有のSSH・PHPパス・権限のハマりどころまで、この記事の設定例だけで初回デプロイを再現できることを目指しています。
デプロイ全体の流れ(Deployer導入〜初回デプロイ)
細かい設定に入る前に、Deployerを導入してから初回デプロイが完了するまでの全体像を先に押さえておきます。
- Deployerを導入する:ローカルのLaravelプロジェクトに
composer require --dev deployer/deployerでインストールする - XserverのSSHを有効化する:サーバーパネルでSSH設定をオンにし、公開鍵を登録する(ポートは
10022) deploy.phpを用意する:後述の完成例をベースに、ホスト名・ユーザー・パスを自分の環境へ書き換える- サーバー側に
.envを配置する:shared/.envとして本番用の環境設定を置く - 初回デプロイを実行する:
./vendor/bin/dep deploy stgでステージングへ、問題なければprodへ - 公開ディレクトリをシンボリックリンクする:Xserverの公開ディレクトリから
current/publicへリンクを張る
以下ではこの流れに沿って、実際に使っている設定を分解して説明します。
現在地点
以前に、以下の記事を書きました。
→ レンタルサーバーにLaravelをデプロイするDeployerの使い方
こちらの記事ではDeployerを使用したシンプルなデプロイ方法を紹介しています。
記事内では「レンタルサーバー環境でnodeのビルドを行いましょう」と紹介していますが、なにぶん古い記事なため、node.jsの必要バージョンがv20系と新しい物にしないとビルドが出来ないのですが、v20系のnodeが動かないという問題が発生していました。
→ node.jsの実行エラー解消「version `GLIBC_2.27′ not found」
今まではv16系で対処していたのですが、重い腰をあげて対応したのが今回の記事につながりました。レンタルサーバー内でnodeのビルドはせずに、ビルド結果のjsファイルをまとめてrsyncでデプロイする事にしました。
Deployerとは?
Deployer はPHPアプリケーション向けのデプロイツールで、SSH経由でサーバーへのコード転送や、キャッシュクリア、ロールバックなどを自動化できます。
Laravelとの親和性も高く、recipe/laravel.phpを利用することで composer install・migration・各種キャッシュ生成・storage:link といったLaravel特有の処理を最初から組み込んだ状態でデプロイできます。導入はローカルのLaravelプロジェクトで次のコマンドを実行するだけです。
composer require --dev deployer/deployer
今回の構成
- デプロイ先のサーバー:Xserver(SSH接続可)
- PHPバージョン:8.2
- Laravelアプリケーション
- デプロイ方式:ローカルでnpmビルド → rsync転送
- 環境:本番(prod)とステージング(stg)の2つ
- バージョン:”deployer/deployer”: “^7.5”,
rsyncを使う理由とGit経由デプロイとの違い
Deployerの標準的なデプロイ(recipe/laravel.php の deploy:update_code)は、サーバー上で git clone / git pull を実行してソースを取得します。これに対して今回はサーバー側のgit取得を無効化し、ローカルでビルドした成果物を rsync で転送する方式に切り替えています。
rsyncでデプロイする利点は次のとおりです。
- GitHubなどからソースを取得するためのデプロイキー(秘密鍵)をレンタルサーバー内に置かないで済む
- nodeのソースインストールなど、レンタルサーバーでの事前準備が不要になる(ビルドはローカルで完結)
- 差分のみ転送されるため、2回目以降の転送が速い
Git経由デプロイとの違いを整理すると次の表のようになります。
| 観点 | Git経由デプロイ | rsyncデプロイ(本記事) |
|---|---|---|
| ソース取得 | サーバー上で git clone/pull | ローカルから rsync で転送 |
| ビルド(npm等) | サーバー上で実行 | ローカルで実行し成果物だけ転送 |
| サーバーの事前準備 | git・node・npmが必要 | SSHとPHPがあればよい |
| 秘密情報 | デプロイキーをサーバーに配置 | サーバーに鍵を置かない |
| 向いている環境 | 自由に環境構築できるVPS等 | Xserver等の制約が多いレンタルサーバー |
Xserverのように、GLIBCのバージョンが低くて新しいNode.jsが動かない環境では、rsync方式のほうが圧倒的に扱いやすくなります。
deploy.php の完成例
まずは全体像として deploy.php の完成例を掲載します。この1ファイルで初回デプロイまで再現できます。各設定項目はこのあとのセクションで分解して説明します(ホスト名・ユーザー名・パスはご自身の環境に置き換えてください)。
<?php
namespace Deployer;
require 'recipe/laravel.php';
require 'contrib/rsync.php';
// ---- 基本設定 ----
add('shared_files', ['public/.htaccess']);
set('keep_releases', 3);
set('bin/php', '/usr/bin/php8.2');
set('bin/composer', '/usr/bin/php8.2 ~/bin/composer.phar');
// ---- 共有 / 書き込み権限(recipe/laravel.php の既定に追記する場合)----
add('shared_dirs', ['storage']);
add('shared_files', ['.env']);
add('writable_dirs', ['bootstrap/cache', 'storage']);
set('writable_mode', 'chmod'); // Xserverは sudo 不可
set('writable_use_sudo', false);
// ---- rsync 設定 ----
set('rsync_src', __DIR__);
set('rsync_dest', '{{release_path}}');
set('rsync', [
'exclude' => [
'.git',
'deploy.php',
'node_modules',
'tests',
'vendor',
'storage',
'public/storage',
'bootstrap/cache',
'.env*',
'.phpunit.result.cache',
'.styleci.yml',
'.DS_Store',
'herd.yml',
'jsconfig.json',
'phpunit.xml',
'README.md',
],
'flags' => 'rz',
'options' => ['delete'],
'timeout' => 60,
]);
// ---- ホスト定義(本番)----
host('prod')
->set('hostname', '*****.xserver.jp')
->set('remote_user', 'productionuser')
->set('identity_file', '~/.ssh/production_xserver')
->set('deploy_path', '/home/productionuser/production.example.com/script/sample-laravel')
->set('port', 10022)
->set('http_user', 'productionuser')
->set('cache_clear_url', 'https://production.example.com/clear.php');
// ---- ホスト定義(ステージング)----
host('stg')
->set('hostname', '*****.xserver.jp')
->set('remote_user', 'staginguser')
->set('identity_file', '~/.ssh/staging_xserver')
->set('deploy_path', '/home/staginguser/staging.example.com/script/sample-laravel')
->set('port', 10022)
->set('http_user', 'staginguser')
->set('cache_clear_url', 'https://staging.example.com/clear.php');
// ---- カスタムタスク ----
task('npm:build', function () {
runLocally('npm run build');
});
task('local:sync', ['npm:build', 'rsync']);
task('cache:clear', function () {
fetch('{{cache_clear_url}}');
});
task('artisan:queue:restart', function () {
run('{{bin/php}} {{release_or_current_path}}/artisan queue:restart');
});
// ---- フック ----
task('deploy:update_code')->disable();
after('deploy:release', 'local:sync');
after('deploy:vendors', 'artisan:queue:restart');
after('deploy:cleanup', 'cache:clear');
after('deploy:failed', 'deploy:unlock');
基本設定
add('shared_files', ['public/.htaccess']);
set('keep_releases', 3);
set('bin/php', '/usr/bin/php8.2');
set('bin/composer', '/usr/bin/php8.2 ~/bin/composer.phar');
keep_releases:サーバー上に残す過去リリース数。3なら直近3世代を保持し、rollbackで戻せます。bin/php:XserverはPHPがバージョン別のパスになっているため、/usr/bin/php8.2のように明示します。bin/composer:Composerはグローバル導入されていないので、composer.pharをPHPで実行するパスを指定します。
rsync 設定
rsync_src に転送元(ローカルのプロジェクトルート)、rsync_dest に転送先(今回のリリースディレクトリ)を指定します。exclude には .env・node_modules・vendor・storage・bootstrap/cache など、サーバー側で管理・生成すべきものを列挙し、rsyncで上書きしないようにします。options の delete は、転送元に存在しないファイルを転送先からも削除するオプションです。
shared / writable の設定(.env・storage・bootstrap/cache)
リリースごとにディレクトリが切り替わるDeployerでは、「リリース間で共有したいファイル」と「書き込み権限が必要なディレクトリ」を分けて扱います。recipe/laravel.php は次の内容をあらかじめ登録しています。
| 設定 | 既定値(Laravel recipe) | 役割 |
|---|---|---|
shared_files | .env | リリース間で共有するファイル。{{deploy_path}}/shared/ に実体を置き、各リリースからシンボリックリンク |
shared_dirs | storage | リリース間で共有するディレクトリ。ログ・アップロード・キャッシュが世代をまたいで引き継がれる |
writable_dirs | bootstrap/cache, storage | Webサーバーが書き込むディレクトリ。deploy:writable で権限が付与される |
それぞれの配置と権限のポイントは以下です。
- .env:
shared_filesなので実体は{{deploy_path}}/shared/.env。初回デプロイの前に、このパスへ本番用の.envを自分で配置します(SCPやサーバー上のviで作成)。権限はchmod 600が無難です。rsyncのexcludeに.env*を入れているのは、この共有ファイルを転送で上書きしないためです。 - storage:
shared_dirsなので実体は{{deploy_path}}/shared/storage。デプロイのたびに作り直されず、ログやアップロードファイルが保持されます。excludeにstorageを入れているのも同じ理由です。 - bootstrap/cache:
writable_dirs。config:cacheなどが書き込むため、書き込み権限が必要です。rsyncではexcludeし、サーバー側でdeploy:writableにより権限を確保します。
Xserverは sudo が使えないため、writable_use_sudo を false、writable_mode を chmod にしておくと権限付与でつまずきません。
ホスト定義(Xserver)
本番(prod)とステージング(stg)をホストとして分けて定義します(完成例の該当部分を再掲)。
本番(prod)
host('prod')
->set('hostname', '*****.xserver.jp')
->set('remote_user', 'productionuser')
->set('identity_file', '~/.ssh/production_xserver')
->set('deploy_path', '/home/productionuser/production.example.com/script/sample-laravel')
->set('port', 10022)
->set('http_user', 'productionuser')
->set('cache_clear_url', 'https://production.example.com/clear.php');
ステージング(stg)
host('stg')
->set('hostname', '*****.xserver.jp')
->set('remote_user', 'staginguser')
->set('identity_file', '~/.ssh/staging_xserver')
->set('deploy_path', '/home/staginguser/staging.example.com/script/sample-laravel')
->set('port', 10022)
->set('http_user', 'staginguser')
->set('cache_clear_url', 'https://staging.example.com/clear.php');
ホスト定義を分けることで、コマンド引数を指定するだけで本番/ステージングを切り分けられます。port がXserverのSSHポート 10022 である点に注意してください。
# ステージング環境へのデプロイ
./vendor/bin/dep deploy stg
# 本番環境へのデプロイ
./vendor/bin/dep deploy prod
他にも Selector の機能を使う事でラベルを指定、複数のアプリケーションサーバーやバッチサーバーなども切り分けられるので必要に応じて設定してみてください。
カスタムタスク
npm ビルドとrsync実行
task('npm:build', function () {
runLocally('npm run build');
});
task('local:sync', [
'npm:build',
'rsync',
]);
デプロイを実行しているローカル環境で必要な処理として npm run build があります。開発時のjsビルドではなく、本番環境向けのビルドを実行してからrsyncを行う local:sync タスクを定義します。runLocally() はローカルで、run() はサーバー上で実行される点が違いです。
Composer・migration・キャッシュ生成(recipe組み込み)
Composerインストール・migration・各種キャッシュ生成は、recipe/laravel.php の deploy タスクに最初から組み込まれています。あとで示すタスクツリーのとおり、次の順で自動実行されます。
deploy:vendors:composer install --no-dev --optimize-autoloader相当を実行artisan:storage:link:php artisan storage:linkartisan:config:cache/route:cache/view:cache/event:cache:本番用の各種キャッシュを生成artisan:migrate:本番では--force付きでマイグレーションを実行
Composerのメモリ不足でこける場合は set('composer_options', '...') の調整や、bin/composer の指定を見直してください。
queue(キュー)ワーカーの再起動
キューワーカーを常駐させている場合、デプロイ後は古いコードを掴んだままのワーカーを入れ替える必要があります。queue:restart を叩くタスクを追加し、deploy:vendors の後にフックします。
task('artisan:queue:restart', function () {
run('{{bin/php}} {{release_or_current_path}}/artisan queue:restart');
});
after('deploy:vendors', 'artisan:queue:restart');
Xserverでのキューワーカーの常駐・cron設定については、別記事の Laravelのjob/queueをレンタルサーバーで実行するためのコツ もあわせて参考にしてください。
キャッシュクリア用タスク
XserverにはOPcacheが導入されており、これはDeployerのようなシンボリックリンクを貼り替えるデプロイ方式とは相性が悪いのでキャッシュ削除をする必要があります。以下参考情報
そのため、キャッシュ削除する処理を作成し、Apache経由でキャッシュ削除するURLにアクセスすることでキャッシュクリアを行います。
task('cache:clear', function () {
fetch('{{cache_clear_url}}');
});
cache_clear_url にキャッシュクリア用のURLを設定しておきます(ホスト定義内の cache_clear_url)。clear.php は以下の設定で public/ 配下に作成しておきます。
// public/clear.php
<?php
opcache_reset();
OPcacheの削除方法そのものについては エックスサーバーの強力なキャッシュ、OPcacheを削除する で詳しく解説しています。
デプロイフックの設定
task('deploy:update_code')->disable();
after('deploy:release', 'local:sync');
after('deploy:vendors', 'artisan:queue:restart');
after('deploy:cleanup', 'cache:clear');
after('deploy:failed', 'deploy:unlock');
deploy:update_code は本来 git からソースを取得する処理なので、disable() で無効化し、代わりに deploy:release の後に local:sync(npmビルド+rsync)が実行されるようフック登録します。deploy:failed にフックした deploy:unlock は、失敗時にロックを解除して次のデプロイを妨げないための保険です。
デプロイ手順
.envをサーバーの{{deploy_path}}/shared/.envに用意しておきます(もしくは最初の数回は失敗させながら、実ファイルが作成されたら本番用に設定するなど)。./vendor/bin/dep deploy stgでステージングへデプロイを実行します。- デプロイされたら、Xserverの公開ディレクトリ(例:
~/staging.example.com/public_html)から{{deploy_path}}/current/publicへシンボリックリンクを設定します。 - ステージングで問題なければ
./vendor/bin/dep deploy prodで本番へ反映します。
コマンドを実行すると以下のタスクツリーの流れで処理が進みます。deploy:update_code が disabled になり、代わりに local:sync(npm:build → rsync)が走っている点、末尾で cache:clear が実行されている点を確認してください。
% ./vendor/bin/dep tree deploy
The task-tree for deploy:
└── deploy
├── deploy:prepare
│ ├── deploy:info
│ ├── deploy:setup
│ ├── deploy:lock
│ ├── deploy:release
│ ├── local:sync // after deploy:release
│ │ ├── npm:build
│ │ └── rsync
│ ├── deploy:update_code // disabled
│ ├── deploy:env
│ ├── deploy:shared
│ └── deploy:writable
├── deploy:vendors
├── artisan:storage:link
├── artisan:config:cache
├── artisan:route:cache
├── artisan:view:cache
├── artisan:event:cache
├── artisan:migrate
└── deploy:publish
├── deploy:symlink
├── deploy:unlock
├── deploy:cleanup
├── cache:clear // after deploy:cleanup
└── deploy:successcurrent切り替えとrollback(切り戻し)
Deployerはリリースごとに releases/1, releases/2… とディレクトリを作り、デプロイ成功時に deploy:symlink で current シンボリックリンクを最新リリースへアトミックに張り替えます。Xserverの公開ディレクトリを current/public に向けておけば、切り替えの瞬間だけ参照先が変わり、ダウンタイムなく公開できます。
デプロイ後に不具合が見つかった場合は、rollback で直前のリリースへ即座に戻せます。
# 直前のリリースへ切り戻し
./vendor/bin/dep rollback prod
# 保持されているリリース一覧を確認
./vendor/bin/dep releases prod
keep_releases で指定した世代数だけリリースが残るため、その範囲で戻せます。注意点として、rollbackでは cache:clear が自動実行されません。OPcacheが古いコードをキャッシュしたままになることがあるので、切り戻し後は clear.php のURLへアクセスしてOPcacheをリセットしてください。
Xserverでハマりやすいポイント
Xserver特有でつまずきやすいのは、SSH・PHPパス・権限・コマンド実行まわりです。よくある問題と対処をまとめました。
| 項目 | 起きやすい問題 | 対処 |
|---|---|---|
| SSH接続 | ポート22で繋がらない/認証に失敗する | ポートは 10022。サーバーパネルでSSHを有効化し公開鍵を登録。identity_file で鍵を明示 |
| PHPパス | php artisan が古いPHPで動く | which php はCLI既定版。bin/php に /usr/bin/php8.2 のようにバージョン付きパスを指定 |
| Composer | composer コマンドが無い | composer.phar を ~/bin に置き、bin/composer を php8.2 ~/bin/composer.phar に設定 |
| 権限 | deploy:writable でsudoエラー | writable_use_sudo を false、writable_mode を chmod に |
| 公開ディレクトリ | ドキュメントルートが public_html 固定 | public_html から current/public へシンボリックリンク。サブディレクトリ公開は別記事参照 |
| OPcache | デプロイしても古い挙動のまま | clear.php(opcache_reset())へアクセスしてリセット |
| Node.js | サーバー上でnpmビルドが動かない | ビルドはローカルで実行しrsyncで転送(本記事の方式)。GLIBC問題は別記事参照 |
公開ディレクトリの切り替え(サブディレクトリ配下での公開)については Laravel8系でサブディレクトリ配下の公開に対応する が参考になります。
デプロイ後にキャッシュ由来の不具合が残る場合は Laravel キャッシュクリアができない場合のトラブル改善方法、稼働中のLaravelバージョンを確かめたいときは Laravelのバージョン確認方法|artisan・Composer・ファイルで確認 を確認してください。
まとめ
Deployer + rsync によるLaravelアプリの公開は、ビルドをローカルで完結できるため、サーバーにNode.jsやnpmを導入する必要がなく、セキュリティや負荷の観点でも非常にスマートです。deploy.php の完成例・shared/writable 設定・.env/storageの配置・migrationやqueue再起動・rollbackまで押さえておけば、初回デプロイから日々の運用までひととおり回せます。
Xserverのように、GLIBCのバージョンが低いために必要なNode.jsのバージョンを導入する手間がある環境でもこの方法なら対応可能です。これからLaravelを本番公開する方は、ぜひこの方法を参考にしてみてください!

コメントを残す