こんにちは、エンジニアの @akase244 です。
最近、Heroku(読み方は「ヘロク」)を触る機会があり、ちょっとハマったので今回はその話を書いてみようと思います。
久しぶりなHeroku環境
「Herokuとは」でググると山程有用な記事が見つかるので説明についてはそちらにお譲りしますが、いわゆるPaaSと呼ばれるものでインフラ環境をいい感じに準備してくれてアプリ開発に集中させてくれる頼れるやつです。
私がHerokuを触っていたのは3年ほど前で、Hubotを使ったSlackボットをHeroku環境で動かすといったことを趣味でやってたんですが、今回仕事で初めてプロダクション環境を構築する機会を得ました。
そうそう、3年前といえば当社のCTOがPHPカンファレンス福岡2015でHerokuについて発表したスライドをアップしてくれているので、そちらも参考になるかと思います。
Heroku buildpackとは
Herokuでアプリケーション環境を構築する際になくてはならない存在がHeroku buildpackです。 このHeroku buildpackを利用することで、アプリケーション環境を簡単に準備できたり、様々なカスタマイズを可能にしてくれます。
buildpackの追加画面
heroku-buildpack-monorepoとは
今回Heroku環境にデプロイしたいのは以下のようにGitのルートディレクトリ直下ではなく、srcディレクトリ直下にあるLaravelのアプリケーションが対象です。
Gitのルートディレクトリ
$ tree ./ -a -L 1 ./ ├── .git ├── .gitignore ├── README.md ├── docker ├── docker-compose.yml └── src
デプロイ対象のsrcディレクトリ(Laravelのアプリケーションディレクトリ)
$ tree ./src/ -a -L 1 ./src/ ├── .gitignore ├── Procfile ├── app ├── artisan ├── bootstrap ├── composer.json ├── composer.lock ├── config ├── database ├── heroku ├── package-lock.json ├── package.json ├── phpunit.xml ├── public ├── readme.md ├── resources ├── routes ├── server.php ├── storage ├── tests └── webpack.config.js
こういったサブディレクトリをデプロイ対象として選択できるようにしてくれるのがheroku-buildpack-monorepoというbuildpackです。(※heroku-buildpack-monorepo以外にもいくつか同じ機能のbuildpackがあるようです)
環境構築(失敗編)
さて、ここからはheroku-buildpack-monorepoを利用した環境構築時のコマンドを見ながら、実際にデプロイ時にハマったところを再現してみます。
※Heroku CLIについては事前にインストールしておきましょう。
※今回の記事内でDB接続部分については特に説明しないこととします。
Herokuにコマンドラインからログイン。
$ heroku login heroku: Enter your login credentials Email: ◯◯◯@innovator.jp.net Password: ********** Logged in as ◯◯◯@innovator.jp.net
アプリケーションの作成。
$ heroku create アプリ名 Creating ⬢ アプリ名... done https://アプリ名.herokuapp.com/ | https://git.heroku.com/アプリ名.git
今回はPHP製のフレームワークであるLaravelのアプリケーションがデプロイ対象なのでHerokuでPHPを利用可能にするためのbuildpackを追加。
$ heroku buildpacks:add heroku/php -a アプリ名 Buildpack added. Next release on アプリ名 will use heroku/php. Run git push heroku master to create a new release using this buildpack.
先程説明したとおり、srcディレクトリ直下をデプロイしたいのでheroku-buildpack-monorepoを追加。
$ heroku buildpacks:add https://github.com/lstoll/heroku-buildpack-monorepo -a アプリ名 Buildpack added. Next release on アプリ名 will use: 1. heroku/php 2. https://github.com/lstoll/heroku-buildpack-monorepo Run git push heroku master to create a new release using these buildpacks.
buildpackのインストール状況を確認。
$ heroku buildpacks -a アプリ名 === アプリ名 Buildpack URLs 1. heroku/php 2. https://github.com/lstoll/heroku-buildpack-monorepo
heroku-buildpack-monorepoを利用してどのディレクトリをデプロイするかをHerokuに教えてあげるためにAPP_BASEという環境変数をセット。
$ heroku config:add APP_BASE=src -a アプリ名 Setting APP_BASE and restarting ⬢ アプリ名... done, v3 APP_BASE: src
環境変数の設定状況を確認。
$ heroku config -a アプリ名 === アプリ名 Config Vars APP_BASE: src
デプロイのためにリモートリポジトリを追加。
※「heroku create」コマンド実行時に表示されるHerokuのGitのURLを指定します。
$ git remote add アプリ名 https://git.heroku.com/アプリ名.git
なお、↑この操作は「heroku create」時に「--remote」をつけることで省略可能です。
$ heroku create アプリ名 --remote アプリ名
あと、事前にProcfileを準備しておく必要がありますので、srcディレクトリ直下に以下のような感じで作成しておきます。
$ cat src/Procfile web: vendor/bin/heroku-php-nginx public/
はい、これで失敗する準備が整いましたのでデプロイしてみましょう。
$ git push アプリ名 master Counting objects: 2030, done. Delta compression using up to 4 threads. Compressing objects: 100% (1023/1023), done. Writing objects: 100% (2030/2030), 36.78 MiB | 3.85 MiB/s, done. Total 2030 (delta 1117), reused 1559 (delta 835) remote: Compressing source files... done. remote: Building source: remote: remote: -----> App not compatible with buildpack: https://buildpack-registry.s3.amazonaws.com/buildpacks/heroku/php.tgz remote: remote: ! ERROR: Application not supported by this buildpack! remote: ! remote: ! The 'heroku/php' buildpack is set on this application, but was remote: ! unable to detect a PHP codebase. remote: ! 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: ! 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. remote: ! remote: ! For more information, refer to the following documentation: remote: ! https://devcenter.heroku.com/articles/buildpacks remote: ! https://devcenter.heroku.com/articles/php-support#activation remote: remote: remote: More info: https://devcenter.heroku.com/articles/buildpacks#detection-failure remote: remote: ! Push failed remote: Verifying deploy... remote: remote: ! Push rejected to アプリ名. remote: To https://git.heroku.com/アプリ名.git ! [remote rejected] master -> master (pre-receive hook declined) error: failed to push some refs to 'https://git.heroku.com/アプリ名.git'
見事に失敗しました。エラーメッセージを確認するとcomposer.jsonを見つけることができず、PHPアプリケーションであることを判別できなかったようです。なぜか?
環境構築(解決編)
まず、デプロイが成功している環境のbuildpackの設定を見てみます。
次に今回失敗している環境の設定を見てみます。パッと見は問題なさそうに思えます。。。
ん?ちょっと待て、こいつ動くぞ???ということでピンと来ました。
どうやらbuildpackは動作時に優先順位があるようです。
そういえば、buildpack追加時に以下のようなメッセージが表示されていましたね。つまり、この「1. heroku/php」という数字は動作時の優先順位だったわけです。
Buildpack added. Next release on アプリ名 will use: 1. heroku/php 2. https://github.com/lstoll/heroku-buildpack-monorepo
なので、うまく動いている環境と同じように「heroku-buildpack-monorepo」を「heroku/php」より上に持ってきます。すると「Your new buildpack configuration will be used when this app is next deployed.」というメッセージが表示されました。これでうまくいきそうです。
それでは再度デプロイしてみましょう。
$ git push アプリ名 master ・ ・ 省略 ・ ・ remote: -----> Monorepo app detected remote: Copied src to root of app successfully remote: -----> PHP app detected remote: -----> Bootstrapping... remote: -----> Installing platform packages... remote: - php (7.2.11) remote: - ext-mbstring (bundled with php) remote: - nginx (1.8.1) remote: - apache (2.4.34) ・ ・ 省略 ・ ・ remote: Package manifest generated successfully. ・ ・ 省略 ・ ・ remote: -----> Launching... remote: Released v4 remote: https://アプリ名.herokuapp.com/ deployed to Heroku remote: remote: Verifying deploy... done. To https://git.heroku.com/アプリ名.git * [new branch] master -> master
今度は「Monorepo app detected」、「PHP app detected」というメッセージが表示されていますね!やはりbuildpackは動作時に設定した際の順序が関係しているようです。
まとめ(いつもの)
いかがだったでしょうか。私がHeroku環境に慣れてないことがよくわかっていただけたんじゃないかと思います。 ということで、Heroku、Laravelとか(AWS、Vue.jsもあるよ)に興味がある人がいれば、ぜひこちらまで。