Laravelのデプロイ
かつてLaravelの開発環境を設定していろいろ学んできましたが、いよいよデプロイです。
Windows10で開発したLaravel9のプロジェクトをDebian11にデプロイしました。Laravelのデプロイは基本的にはプロジェクト単位でコピーをして設定を変更するだけなのでさほど難しくはないものです。
ただLaravel9がPHPのバージョン8.0以上を要求しているのに対し、執筆時点のDebian11においてはAPTで取得できるPHPのバージョンは7.4だったので、apacheとPHPをmakeすることになりましたが、こちらの方法については別記事DebianでApacheとPHPをビルドを参考にしてください。
Laravelプロジェクトのデプロイ
Apache と PHPの環境が整ったら、本番サーバーにLaravelプロジェクトをデプロイします。
サーバーにcomposerがない場合はリンク先を参考にインストールします。
# php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" # php -r "if (hash_file('sha384', 'composer-setup.php') === '55ce33d7678c5a611085589f1f3ddf8b3c52d662cd01d4ba75c0ee0459970c2200a51f492d557530c71c15d8dba01eae') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;" # php composer-setup.php # php -r "unlink('composer-setup.php');"
作業後に作業ディレクトリ「composer.phar」という名前で実行ファイルができます。これは後からオートローダ―を最適化する際に利用します。binディレクトリなどパスの通った場所から、composerというシンボリックリンクを張ります。
同様にNode.js環境もインストールします。Node.jsのインストールはNVMを使って行うのがいいと思います。
LinuxにおけるNVMは実行したユーザーのホームディレクトリに.nvmとして保存されます。実行ファイルへのパスはスクリプトが.bashrcファイルに記述してくれますが、初回時はうまく反映されていないことがあります。which nvm等の応答がなかったら再ログインしてみてください。またroot環境でインストールしてしまうと、ユーザー環境からは利用できない場所にインストールされてしまいますので注意が必要です。
$ wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash $ nvm install --lts $ nvm use インストールされたバージョン
nvmをアンインストールしたい場合は.nvmディレクトリを削除するだけです。npmの実行ファイルは.nvmディレクトリの下流にありますが、エラーログ等はユーザーディレクトリ内の.npmディレクトリに保存されるようですので、nvmアンインストール時にはそれも削除します。
この後の手順は次のようになります。
- ファイルのコピー
基本的にプロジェクトディレクトリをコピーします。「/node_modules,/public/build,/storage/*.key,/vendor,.phpunit.result.cache」など通常.gitignoreに含まれるディレクトリ以外のすべてが対象となります。
後々のことを考えるとgitを利用して開発側でアップロードしたgitレポジトリを本番側でcloneした方が便利だと思いますが、この時デフォルトだと、.envファイルは含まれないので別途生成するかコピーします。また、composerやnodeで設定したパッケージもコピーされませんので、それぞれ「npm update」「composer install」を実行します。
コピーしたファイルとディレクトリの所有権はこのあとcomposerやnpmを実行するユーザーにしておきます。
- APP_KEYの再生成
.envファイルに設定してあるAPP_KEYを生成または再生成します。
$ php artisan key:generate
- 環境をプロダクションモードに
.envファイル中の「APP_ENV」の値を「local」から「production」にします。
- デバッグモードの解除
.envファイル中の「APP_DEBUG」の値をfalseにします。APP_ENVの後に出現する設定なのでここで紹介していますが、この値をfalseに設定すると実行時に詳細なエラーメッセージが出なくなりますので、稼働を確認してからの方がいいかもしれません。
- APP_URLの設定
デフォルトでapp.testとなっているURLを正規のものに変えます。
- データベースの設定
初回のデプロイ時は環境によってデータベースにユーザー登録等が必要になります。またマイグレーションやシードも必要に応じて実行します。
bootstrapに相当する場所(サービスなど)にデータベーステーブルが存在することが前提のコードが書かれていると、例外がスローされてphp artisanコマンドを実行できない場合があります。コードを直すか、それが難しければ開発側のデータベースのテーブルをダンプしたものを本番側でインポートします。MySQL(MariaDB)におけるダンプ・インポートの方法は次の通りです。
$ mysqldump -u ユーザー名 -p データベース名 --default-character-set=binary>dump.sql
$ mysql-u ユーザー名 -p データベース名 --default-character-set=binary<dump.sql
- JavaScriptのビルド
JavaScriptの環境自体は既に設定済みだと思いますので、ビルドのコマンドを実行するだけです。buildしてから本番環境にコピーする方法も試してみたのですが、やり方が悪かったのか@viteディレクティブが本番環境にならずうまくいきませんでした。
$ npm run build
ビルド時に、次のようなエラーが出た場合はstack overflowを参考に、/etc/sysctl.confのファイルウォッチャーの制限を変更します。
System limit for number of file watchers reached/etc/sysctl.conf
... fs.inotify.max_user_watches = 16384 ...
ここで設定した値は適当です。筆者の環境ではデフォルト値が8192だったので倍にしました。変更後、「sysctl -p」で設定を反映させます。
- オートローダの最適化
Composerのクラスオートローダの最適化をします。ここで先ほどインストールしたcomposer.pharを使います。composerは管理者権限で実行すると警告がでます。
$ composer.phar install --optimize-autoloader --no-dev
- configの最適化
構成ファイルをひとつのキャッシュファイルにまとめます。新しい環境でconfigのキャッシュしないと古い環境のものが使われてしまい、思わぬエラーに悩まされることになります。
$ php artisan config:cache
- ルートの最適化
すべてのルート登録をキャッシュ ファイル内の 1 つのメソッド呼び出しに減らします。
$ php artisan route:cache
- ビューの最適化
すべての Blade ビューをプリコンパイルします。
$ php artisan view:cache
すべて終えたら、エントリーポイントであるpublicディレクトリ内のindex.phpにアクセスして意図したように表示できるかチェックします。
開発環境がWindowsでデプロイはLinuxという場合は、ファイルの大文字と小文字の違いが識別されるようになる上に権限の概念も加わりますので注意が必要です。
筆者が実際に遭遇した事例を挙げると、はphp artisanコマンド等でautoload_real.phpのコードでエラーとなって何もできなかったのですが、composer.jsonのautoload上のパスの中のappのaが大文字になっていました。
...
"autoload": {
"files": [
"app/helpers/CustomHelper.php"
]
},
...
ちなみにこれは自作のヘルパー関数を追加する設定だったので、値の修正後は「composer dump-autoload」を実行してautoloaderを更新することも必要です。
細かな設定
稼働が確認できたら細かな設定をします。
htdocs以下のファイルやディレクトリの所有権の設定の仕方はいろいろな考え方があるとは思いますが、stack overflow:「How to set up file permissions for Laravel?」に答えのひとつがありました。
ただ先のサイトのまま従ってしまうと、本番環境でnpm run buildが実行できなくなってしまいます。なので筆者は次のようにしました。
- ファイルのコピー
筆者の環境のumaskの値は0022です。
- 「composer install」「npm update」を実行
- 所有権を、編集ユーザー:apacheグループに変更
# chown -R edit-user:www-data プロジェクトdir/
- すべてのファイルから「その他」の書き込み権限と実行権限を削除
# find プロジェクトdir/ -type f | xargs chmod o-wx
- すべてのディレクトリから「その他」の書き込み権限を削除
# find プロジェクトdir/ -type d | xargs chmod o-w
- すべてのファイルに「グループ」の書き込み権限を追加
# find プロジェクトdir/ -type f | xargs chmod g+w
- すべてのディレクトリに「グループ」の書き込み権限を追加
# find プロジェクトdir/ -type d | xargs chmod g+w
そのほかphp.iniを修正してエラーの出力と止めたり、apacheの設定でディレクトリ指定時のファイルのリストを止めたりします。
apacheのhttpd.confのdir_moduleを使って、publicディレクトリにアクセスした際にindex.phpを表示させようとすると、次のようなエラーになりうまくいきませんでした。
それに対する回避策は、index.htmlファイルを作ってidnex.phpにリダイレクトするようにするのが簡単です。
index.html
...
<head>
<!-- metaで書くパターン-->
<meta http-equiv="refresh" content="0;URL='./index.php'"/>
<!-- jsで書くパターン-->
<script>
window.onload = function () {
location.replace("./index.php");
};
</script>
ただ、他に設定しなければならないものもありますので、.htaccessファイルとmod_rewriteを使って次のようにするのがいいと思います。
.htaccessファイルを利用するために、httpd.confでは設定の上書きを許可します。同時に、ファイルのリストの出力をやめ、mod_rewriteを有効にします。
httpd.conf
...
# mod_rewriteを有効に(コメントアウトを外す)
LoadModule rewrite_module modules/mod_rewrite.so
...
<Directory "/usr/local/apache2/htdocs">
...
# Indexesを消してファイル一覧を出さないようにする
Options FollowSymLinks
# NoneからAllに変えて設定の上書きを許可する
AllowOverride All
</Directory>
プロジェクトディレクトリ直下のデータを読み込まれるのはよくないので、下流のディレクトリを参照できないようにします。
プロジェクトデイレクトリ/.htaccess
...
<Files "*">
deny from all
</Files>
publicディレクトリだけは例外に指定します。プロジェクトのpublicディレクトリにはLaravelが生成した.htaccessファイルが存在しますので、それに追記します。
ここにルートアクセスの際のリダイレクトも追加します。
プロジェクトデイレクトリ/public/.htaccess
...
<IfModule mod_rewrite.c>
<IfModule mod_negotiation.c>
Options -MultiViews -Indexes
</IfModule>
RewriteEngine On
# Handle Authorization Header
RewriteCond %{HTTP:Authorization} .
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
# Redirect Trailing Slashes If Not A Folder...
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} (.+)/$
RewriteRule ^ %1 [L,R=301]
# Send Requests To Front Controller...
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [L]
# pulicディレクトリアクセス時のリダイレクト設定
RewriteRule ^$ %{REQUEST_URI}index.php [L,R]
</IfModule>
# ファイル読み込み例外設定
<Files "*">
allow from all
</Files>
参考にさせていただきましたサイトの皆様、ありがとうございました。