DokkuにLaravel5のプロジェクトをデプロイする

2015新卒の@kadoyauです。学部時代はロボットをいじり、院で音の情報処理をC++で書いていましたが、入社後は楽しく[ちょっと大きな金槌](http://gihyo.jp/news/report/2015/12/1401)でWeb開発をしています。ヒヤッホォォォウ！PHP7は最高だぜぇぇぇぇ！！（ぐるぐる目）

# サンタさんが来るお家をつくろう
:tada: :pizza: クリスマス:christmas_tree: :bell: もとい[紅莉栖マス](http://steinsgate0.jp/)まであと2日になりました。
紅莉栖マスといえばサンタさん。今日は、あなたの会社に2日でサンタさんを呼ぶ方法をお教えしたいと思います。

## 煙突をビルドする
雇われサンタさんの収入は一定の時間内にデプロイできたプレゼントの価値で決まります。ということは、サンタさんはプレゼントを**高速にデプロイするための煙突**を有した家を好みそうです。煙突のない家にプレゼントをデプロイするのはめんどくさいですよね。
サンタさんを迎えるために、まず煙突を[Dokku](http://dokku.viewdocs.io/dokku/)をつかってビルドしましょう！

### Dokkuって何？
サービスをつくってデプロイするのは、ちょっと気合を入れないとめんどくさいものです。
サービスをつくりたいのに、それに必要なDBや環境の構築もいちいちしなくてはならない。サンタさんはプレゼント（サービス）をデプロイするのをためらうってわけです。

HerokuのようなPaaSが社内で使えると、環境構築はHerokuにまかせて、サンタさんはコードと設定を少し書くだけで気軽にデプロイできて便利です。
Dokkuを使うと、DockerベースのHerokuライクなPaaSを作ることができます。これが煙突です。まずは次の記事などを参考に、1日で煙突をビルドしましょう。

参考：[DockerでミニHeroku！「Dokku」をさくらのクラウドで試す - さくらのナレッジ](http://knowledge.sakura.ad.jp/tech/2356/)

これでサンタさんを呼ぶ準備は整いました。

## プレゼントをデプロイする
翌朝目を覚ますと、サンタさんからの手紙（中身は業務委託を契約するための書類でした）があなたのもとへ届いていました。
「やれやれ、仕方ないな」あなたはそう言うと、ワインレッドの古びた衣装に袖を通しました。

そうです。あなたがサンタです。残り1日で煙突に社内サービスをデプロイしましょう！
（ここでいい感じのBGMを再生してください）


# 本記事の目的
以降では一例として、実際にサンタから業務委託をうけた筆者が煙突（Dokku）にプレゼント（PHPアプリケーション）をデプロイする際に**失敗した**点を紹介します。
なお、プレゼントはLaravel 5というフレームワークを利用して開発しました。（主題的に:cake: PHPにすればよかった）
これを読んだみなさんは失敗しない世界線へ……！

## 仮定していること
### 環境
- Dokkuのインストールは終わっていて、デプロイもできる。
- Laravel 5を使ったプロジェクトをデプロイしようとしている。
- サーバはnginxを使用する。

### 表記
- 山括弧（＜＞）がついたものは、自分のプロジェクトで使う文字列に適宜置換してください。その際に山括弧は削除してください。
- あなたのdokkuをホストしているサーバの名前を`your.dokku.domain`とします。

## 参考：dokkuのバージョン確認方法

```
ssh dokku@your.dokku.domain version
0.3.21
```

# DokkuにLaravelのプロジェクトをデプロイする際にハマったポイント

## 単純にデプロイするとNode.jsアプリ扱いになる
PHPプロジェクトをデプロイする際にはいろいろ注意点があります。
Laravelのプレーンなプロジェクトをデプロイしようとすると次の結果が出ます。

```zsh
git push dokku <deploy branch>
```

```
（略）
-----> Node.js app detected
```
デフォルトだとnode.jsのプロジェクトだと勘違いされてしまいます。
rootに`composer.json`があるとPHPのアプリとみなしてくれるが、下位の階層に`package.json`が存在するとnodeのアプリだとみなされるようです。これはHerokuでも同じみたい。

参考：[HerokuでPHPを使うときに気を付けるところ - Qiita](http://qiita.com/hiro_y/items/bc631a27c9701f81a4f9)

### 解決策：PHPのプロジェクトとしてデプロイする
[Buildpacks](https://devcenter.heroku.com/articles/buildpacks)を使う。これはアプリケーションインストール時にサーバの設定を指定できるHerokuの仕組みです。
DokkuはHerokuのbuildpackをサポートしています。

> Dokku supports deploying applications via Heroku buildpacks with Herokuish or using a project's dockerfile.
[Dokku - The smallest PaaS implementation you've ever seen](http://progrium.viewdocs.io/dokku/application-deployment/#deploy-the-app)

heroku公式のPHP向けビルドパック[heroku/heroku-buildpack-php](https://github.com/heroku/heroku-buildpack-php)を使う。
[pauldub/dokku-multi-buildpack](https://github.com/pauldub/dokku-multi-buildpack)をインストールし、プロジェクトのルートに`.buildpacks`というファイルを作成し、中にGitHubのURLを書けば自動的に読み込まれる。

```text:.buildpack
https://github.com/heroku/heroku-buildpack-php
```
#### 豆知識：「Dokku PHP」で調べると一番上に出てくるリポジトリは2015年11月30日現在うまく機能しない
[bobmaerten/dokku-buildpack-php](https://github.com/bobmaerten/dokku-buildpack-php)は罠で、途中で以下のようにエラーを吐いてコケる。
そもそもPHP5.6にも対応していないし不安を煽る。

```
remote: /tmp/buildpack3k4mD/bin/compile: line 270: /app/bin/jq: No such file or directory
remote:  !     Script error in /tmp/buildpack3k4mD/bin/compile on or near line 270
```

## 勝手にテストがつくられて失敗してデプロイも失敗する
再度dokkuにデプロイするとまた失敗します。

```
（略）
-----> Preparing runtime environment...
       NOTICE: No Procfile, using 'web: vendor/bin/heroku-php-apache2'.
       Using release configuration from last framework (PHP).
-----> Discovering process types
       Procfile declares types -> web
-----> Releasing <App name>...
-----> Deploying <App name>...
-----> Running pre-flight checks
       For more efficient zero downtime deployments, create a file CHECKS.
       See http://progrium.viewdocs.io/dokku/checks-examples.md for examples
       CHECKS file not found in container: Running simple container check...
-----> Waiting for 10 seconds ...
remote: App container failed to start!!
=====> <App name> container output:
       No dyno detected; using defaults for 1X...
       4 processes at 128MB memory limit.
       Starting php-fpm...
       Starting httpd...
=====> end <App name> container output
remote: /usr/local/bin/dokku: 52 行: 18907 強制終了            dokku deploy "$APP"
（略）
```

### 解決策：CHECKSファイルを自作する
どうもpre-flight checksというものが自動で作られて、失敗しているようです。[エラーメッセージに出ているリンク](http://progrium.viewdocs.io/dokku/checks-examples.md/#checks-examples)を見ながら自作します。プロジェクトのルートに`CHECKS`というファイルをつくり、適当なテストを書きます。

```text:CHECKS
WAIT=5
```

以上のファイルを作成すると正常にデプロイすることができました。お疲れ様でした。


## dokku上でコマンド実行したら"cannot enable tty mode on non tty input"とかいわれる
出力結果が表示できない。

### 解決策：-tオプションをつけて実行する
> The -t is used to request a pty.
[Dokku - The smallest PaaS implementation you've ever seen](http://progrium.viewdocs.io/dokku/remote-commands/)


## Dokku上で作ったアプリケーションの環境変数の設定をしたい
必要があれば、アプリ固有の環境設定を追加することができます。

```
ssh dokku@your.dokku.domain config:set <アプリ名> ENV=development
```


## DokkuでDBをmigrationしたい
もしDBを使ったアプリケーションを作る場合、Dokku上でDBの設定をしなければなりません。やってみましょう。

### DBを作成する
```
ssh dokku@your.dokku.domain postgresql:create <App name>
```

#### 出力結果

```zsh
PostgreSQL container created: postgresql/<App name>

       Host: IP_ADDRESS
       Port: PORT
       User: 'user'
       Password: 'password'
       Database: 'db'

       Url: 'postgres://user:password@IP_ADDRESS:PORT/db'
```

このDBの設定を参考にLaravel側の設定ファイル（.env）を書き換えます。

```text:.env
DB_CONNECTION=pgsql
DB_HOST=IP_ADDRESS
DB_DATABASE=db
DB_USERNAME=user
DB_PASSWORD=password
```

さらに、Laravel側のconfig/database.phpで利用するポートを`PORT`に変更します。.envに`DB_HOST=IP_ADDRESS:PORT`などとしても動かないので注意。

### DBの設定を確認する
既にDBがある場合、以下のコマンドで同様の項目を確認することができます

#### DB一覧を取得
```zsh
ssh -t dokku@your.dokku.domain postgresql:info
```
#### DBの詳細を取得
```zsh
ssh -t dokku@your.dokku.domain postgresql:info <DB name>
```

## DBに初期値を設定したい

LaravelのSeederクラスをつくっておけば、artisanコマンドで初期値を設定することができます。
Seederクラスの作り方：[Database: Seeding - Laravel - The PHP Framework For Web Artisans](http://laravel.com/docs/5.1/seeding)

```
ssh -t dokku@your.dokku.domain run <App name> "php artisan migrate:refresh --seed"
```

```
Rolled back: 2014_10_12_100000_create_password_resets_table
Rolled back: 2014_10_12_000000_create_users_table
Migrated: 2014_10_12_000000_create_users_table
Migrated: 2014_10_12_100000_create_password_resets_table
Seeded: UsersTableSeeder
Connection to your.dokku.domain closed.
```

### 初期値が適切に設定できたか確認する
PHP artisanを使ってDBに登録されたユーザを確認する

```
ssh -t dokku@your.dokku.domain run <App name> "php artisan tinker \<App name>\User::all()"
```

正常に初期値が設定されていればこれでその値が出力されます。

## その他の設定
デフォルトだとrootのURLが`index.php`になりますが、your.domain.name/でindex.phpにアクセスして欲しいので、nginxの設定を書き換えています。
これらのDokkuに関連する設定ファイルは[Gistで公開しています](https://gist.github.com/kadoyau/802c7df3061b1e452b31)。