対象読者
この記事は、私がローカルで作成済みのLaravelアプリをherokuでデプロイするまでの内容を整理したものです。
- ローカルでLaravelアプリを作った!
- 作ったサービスをherokuで公開したい!
- herokuで公開するにはどうすればいいの?
という方の参考になれば幸いです。
前提
環境
- macOS Monterey 12.3
- Homebrew 2.5.0
- Herokuでユーザー登録済み
- Heroku CLI導入済み
Heroku CLI未インストールの方は以下からどうぞ。
Heroku CLI | Heroku Dev Center
手順
事前準備〜デプロイ
- デプロイしたいLaravelアプリを準備する
- Gitリポジトリの初期化を行う
- Procfileを作成する
- herokuにリポジトリを作成する
- リモートのリポジトリとしてherokuのリポジトリにセットする
- 言語に対応したbuildpackを導入する
- ローカルのLaravelアプリをherokuにデプロイ
- デプロイエラー対応
デプロイ〜動作確認
事前準備からデプロイまで
デプロイしたいLaravelアプリを準備する
まずはローカルに、herokuへデプロイしたいLaravelアプリを準備します。
前提として、この記事ではデプロイしたいアプリのディレクトリ構造は以下のようになっているものとします。
※example-app
は任意のアプリ名です。
example-app
├── app
├── bootstrap
〜中略〜
├── composer.json
〜後略〜
Gitリポジトリの初期化を行う
ローカルでデプロイしたいLaravelアプリを準備したら、Gitリポジトリの初期化を行い、そのまま初期コミットを行います。
% cd example-app
% git init
Initialized empty Git repository in **/example-app/.git/
% git add .
% git commit -m "new laravel project"
Procfileを作成する
Procfileの詳細については下記参照
Procfile | Heroku Dev Center
ProckfileはHeorkuでのアプリ起動時に実行するコマンドを指定するファイルです。
エディタで直接作成するか、内容は短いので下記コマンドで生成してもOKです(※拡張子はありません)
% echo "web: vendor/bin/heroku-php-apache2 public/" > Procfile
Procfileを作成したらコミットしておきます。
% git add .
% git commit -m "Procfile for Heroku"
herokuにリポジトリを作成する
続いてpush先となるherokuに空のリポジトリを作成します。
% heroku create -a example-app
Creating ⬢ example-app... done
https://example-app.herokuapp.com/ | https://git.heroku.com/example-app.git
リモートのリポジトリとしてherokuのリポジトリにセットする
herokuにリポジトリを作成できたら、そのリポジトリをリモートとしてセットします。
% heroku git:remote -a example-app
set git remote heroku to https://git.heroku.com/example-app.git
ちゃんとリモートにセットできているか、以下のコマンドで確認します。
% git remote -v
//以下のように表示されればOK
heroku https://git.heroku.com/example-app.git (fetch)
heroku https://git.heroku.com/example-app.git (push)
言語に対応したbuildpackを導入する
Laravelアプリのデプロイにあたり、
以下のコマンドでnode.jsとphpのbuildpackを導入します。
% heroku buildpacks:set heroku/php
% heroku buildpacks:set heroku/nodejs
//それぞれ以下のようなメッセージが出ればOK(以下はheroku/php導入時の例)
Buildpack set. Next release on example-app will use heroku/php.
Run git push heroku main to create a new release using this buildpack.
buildpackの詳細については以下を参照
Heroku buildpack | Heroku
※buildpackについては私自身あまり理解できていません
現時点では、「Herokuでデプロイする際、言語やサービスの内容に応じて必要になるパッケージ」くらいのイメージです。
ローカルのLaravelアプリをherokuにデプロイ
buildpackの導入まで済んだら、いよいよアプリをherokuにpushします。
% git push heroku main
ここで成功した場合、以下のようなメッセージが出るはずです。
〜略〜
remote: Verifying deploy... done.
To https://git.heroku.com/example-app.git
〜略〜
herokuのActivityが以下のような状態になっていることを確認できればOK!
しかし何か問題があって失敗した場合は以下のようにpushがreject
されます。
〜略〜
remote: Verifying deploy...
remote:
remote: ! Push rejected to example-app.
〜略〜
rejectの場合、Acrivityは以下のようになります。
ここから先はherokuへのデプロイ時に遭遇したエラーとその対策について記述します。
デプロイエラー対応
エラー1:No default language could be detected for this app.
原因:ビルドパックをインストールせずにpushした際に発生するエラーです。
解決策:適切なビルドパックを導入することで解消します。
エラーの詳細は以下のとおり。
remote: -----> Building on the Heroku-20 stack
remote: -----> Determining which buildpack to use for this app
//デフォルトの言語が設定されていません
remote: ! No default language could be detected for this app.
//ヒント:この問題は、Herokuがこのアプリに使うべきbuildpackを自動的に検出できない時に発生します
remote: HINT: This occurs when Heroku cannot detect the buildpack to use for this application automatically.
remote: See https://devcenter.heroku.com/articles/buildpacks
remote:
remote: ! Push failed
remote: Verifying deploy...
remote:
remote: ! Push rejected to example-app.
エラーメッセージで原因がちゃんとわかるのがありがたいですね。
メッセージ内でHINTとして記載されているURLはこちらです。
Buildpacks | Heroku Dev Center
エラー2:Application not supported by this buildpack!
原因:不適切なbuildpackを導入していたり、ディレクトリ構成をミスっていると発生します。
私の場合はディレクトリ構成ミスで発生しました。
解決策:こちらは適切なbuildpackを導入するか、プッシュするディレクトリの構成を見直せば解決します。
// このビルドパックではサポートされていないアプリです!
ERROR: Application not supported by this buildpack!
remote: !
// phpのビルドパックをセットしてますがphpのアプリじゃないと思います。
remote: ! The 'heroku/php' buildpack is set on this application, but was
remote: ! unable to detect a PHP codebase.
remote: !
// phpのアプリだったらcomposer.jsonがルートディレクトリにある構造か、古いものならindex.phpが必要です。
remote: ! A PHP app on Heroku requires a 'composer.json' at the root of
remote: ! the directory structure, or an 'index.php' for legacy behavior.
remote: !
// もしphpのアプリとしてデプロイしたいなら、ディレクトリのトップレベルを確認してください。
remote: ! If you are trying to deploy a PHP application, ensure that one
remote: ! of these files is present at the top level directory.
remote: !
// もし違う言語で書かれたアプリなら、適切なビルドパックを使ってください。
remote: ! If you are trying to deploy an application written in another
remote: ! language, you need to change the list of buildpacks set on your
remote: ! Heroku app using the 'heroku buildpacks' command.
エラー3:WARNING: A post-autoload-dump script terminated with an error
原因:依存関係のインストール失敗によるエラーです。
私の場合はその時点でcomposer.lockに記載されたPHPのバージョンがherokuに対応していなかったため、古いバージョンを使おうとしていたことが原因と考えました。
解決策:以下の順番で作業を行い解決。
- composer.lockに記載されたphpのバージョンを上げるため
composer require php:"バージョン指定"
を実行したもののエラー継続 -
composer update
を実行するもエラー継続(※composer update
を使うときは要注意。せっかくlockファイルで固定しているバージョンを上書きしてしまう危険があるため。詳細はこちらの記事をどうぞ。) - 一度composer.lockを削除して、改めて
composer install
を実行し、再度pushしたところ解決。
// post-autoload-dumpスクリプトがエラーにより終了しました。
remote: ! WARNING: A post-autoload-dump script terminated with an error
remote:
// 依存関係のインストールの失敗です!
remote: ! ERROR: Dependency installation failed!
remote: !
// 'composer install'プロセスが失敗しました。原因はおそらく'composer.json'における、ダウンロードか、パッケージのインストールか、事前/事後インストールのフックによるものです
remote: ! The 'composer install' process failed with an error. The cause
remote: ! may be the download or installation of packages, or a pre- or
remote: ! post-install hook (e.g. a 'post-install-cmd' item in 'scripts')
remote: ! in your 'composer.json'.
remote: !
// 典型的なエラーケースとしては、コードが時代遅れ(=古い?)であることや、不足している部分があること、外部とのコネクション構築のタイムアウトやメモリの限界があります。
remote: ! Typical error cases are out-of-date or missing parts of code,
remote: ! timeouts when making external connections, or memory limits.
デプロイから動作確認まで
ここから先はpushに成功し、サービスがデプロイできた状態から行う作業になります。
流れとしては、以下のようになります。
- MySQLとの接続(JawsDBの導入)
- 環境変数の設定
- マイグレーション
- 動作確認
MySQLとの接続(JawsDBの導入)
HerokuでMySQLを使用するにあたっては、herokuのプラグインからアドオンを導入する必要があります。
アドオンにはClearDBとJawsDBの2種類がありますが、この記事ではJawsDBを扱います。
ここはご自身のデプロイしたいサービスで活用しているMySQLのバージョンに合わせて検討してみてください。
(ClearDBの対応は5.6まで。JawsDBは8.0も対応)
JawsDBの導入
今回導入するJawsDBは、Herokuアプリで使用できるMySQLサーバを提供してくれるアドオンです。
JawsDB MySQL | Heroku Dev Center
今回は無料で使えるKitefinプランを指定して導入します。
JawsDBのプランについてはJawsDB MySQL - Add-ons - Heroku Elementsをご覧ください。
なお、Kitefinプランは無料であるもののクレカ情報の登録が必要なため(登録しないとエラーになる)、事前に登録します。
アカウントの確認 | Heroku Dev Center
手順は以下の通り。
- Heroku Dashboardの
Account Settings
(アカウント設定) に移動 -
Billing
(請求) タブをクリック -
Add Credit Card
(クレジットカードの追加) をクリック
クレカの登録ができたら、下記のコマンドにより、KitefinプランのMySQL8.0を導入できます。
% heroku addons:create jawsdb:kitefin --version=8.0
Creating jawsdb:kitefin on ⬢ example-app... free
Database is being provisioned. Your config_var will be set automatically once available.
Created * as JAWSDB_URL
Use heroku addons:docs jawsdb to view documentation
うまくいったら以下のコマンドでアドオンがちゃんと追加されているか確認します。
% heroku addons
Add-on Plan Price State
───────────────────────────────── ─────── ───── ───────
jawsdb (*) kitefin free created
└─ as JAWSDB
The table above shows add-ons and the attachments to the current app (example-app) or other apps.
※ちなみにクレカ未登録でJawsDBを導入しようとすると以下のように怒られます。
% heroku addons:create jawsdb:kitefin --version=8.0
Creating jawsdb:kitefin on ⬢ example-app... !
▸ Please verify your account to install this add-on plan (please enter a credit card) For more information, see https://devcenter.heroku.com/categories/billing Verify now at
▸ https://heroku.com/verify
環境変数の設定
JawsDBの導入ができたらLaravelの環境変数をherokuで設定します。
Laravelの環境変数についてはこちらをどうぞ。(記事のLaravelは5.7ですが、非常に参考になりました。)
保存版!Laravelの.envでできること大全 – console dot log
herokuの環境変数はheroku config
コマンドで行います。
設定する環境変数は以下のとおりです。
- APP_KEY
- APP_URL
- JAWSDB_URL
- DB_CONNECTION
- DB_HOST
- DB_USERNAME
- DB_PASSWORD
- DB_PORT
- DB_DATABASE
APP_**について
まずはAPP_KEYを出力し、その内容をコピペでherokuの環境変数にセットします。
// APP_KEYの出力
% heroku run "php artisan key:generate --show"
Running php artisan key:generate --show on ⬢ example-app... up, run.3690 (Free)
// 以下をbase64:も含めてコピー
base64:**********************
// APP_KEYをherokuの環境変数にセット(コピーした内容を"APP_KEY="に続けてペーストする)
% heroku config:set APP_KEY=base64:**********************
Setting APP_KEY and restarting ⬢ example-app... done, v
APP_KEY: base64:**********************
続いてAPP_URLをセットします。
APP_URLはアプリの公開URLでheroku上で確認できるほか、heroku info --app example-app
とコマンド入力するとWeb URLとして表示できます。
% heroku config:set APP_URL=https://example-app.herokuapp.com/
JAWSDB_URL及びDB_**について
JAWSDB_URLの中にDB_**の環境変数が含まれているため、
JAWSDB_URLの値を確認→それぞれの環境変数にセット
という流れで設定します。
// JAWSDB_URLの値を確認
% heroku config:get JAWSDB_URL
> mysql://◯◯◯◯:△△△△@□□□□:####/××××
// 上記で表示したJAWSDB_URLは以下のような構造になっている
// mysql://"DB_USERNAME":"DB_PASSWORD"@"DB_HOST":"DB_PORT"/"DB_DATABASE"
// それぞれの環境変数をセットする
% heroku config:set DB_USERNAME=◯◯◯◯
% heroku config:set DB_PASSWORD=△△△△
% heroku config:set DB_HOST=□□□□
% heroku config:set DB_PORT=####
% heroku config:set DB_DATABASE=××××
登録した環境変数はheroku config
で確認できます。
なお、DB周りの環境変数はherokuのダッシュボードからJawsDBにアクセスすることでも確認可能です。
アクセス手順は以下のとおり。
Overviewタブ->Installed add-ons->JawsDB MySQL
マイグレーションと動作確認
無事に環境変数の設定が完了したら以下のコマンドを実行し、マイグレーションを行います。
途中で本当にコマンドを実行していいか確認を求められるのでyes
と入力します。
% heroku run php artisan migrate
Running php artisan migrate on ⬢ example-app... up, run.9658 (Free)
**************************************
* Application In Production! *
**************************************
Do you really wish to run this command? (yes/no) [no]:
> yes
Migration table created successfully.
Migrating: ...
〜以下略〜
ここまできたらheroku open
のコマンドを実行しサービスを開いてみます。
うまくいけばちゃんとサービスが表示されます!お疲れ様でした!
エラーレスポンス対応
500 Server Error:Trait "ライブラリ名" not found
原因:開発中は動いていたライブラリが見つからないと言われた場合、composer.jsonの記述が誤っているものと思われます。
解決策:composer.jsonのrequireに見つからないといわれているライブラリがあるかどうかを確認し、無ければcomposer require "ライブラリ"
で導入することで解決します。
composer.jsonには、require-devという欄がありますが、ここは開発環境用にインストールするプログラムを記述する場所で、ここに記述されたプログラムは本番環境ではインストールされません。(この辺りしっかりできている仕組みだなあと感心しました)
では本番環境でインストールされるプログラムはというとrequireという欄に記述されます。
私の場合は、ソート機能を導入するためにインストールしたkyslik/column-sortableが見つからない状況でした。
kyslik/column-sortableは、require-devに記述されていたため、composer require kyslik/column-sortable
によりrequireに記述を移し、変更内容をコミットしてpushすることで解決しました。
終わりに
自分にとって初めてのデプロイだったため、「簡単にできる!」と評判のherokuを使いましたが、そんなに甘くはなかったです 笑
ただデプロイに挑戦したことで環境変数や本番/開発環境についての理解がだいぶ進みました(多分)
この記事内で行ったエラー対応が誰かの役に立てば幸いです。