PaaS入門　〜Heroku + wordpress〜


最近ちょめちょめaaSがかなり増えてきましたね。
今回はその中からPaaSというジャンルに分類されているHerokuをご紹介したいと思います。

PaaSって聞いたことはあるけど、サービスのイメージしにくいですよね。
本記事で、お金を使わずにアプリを動かしながらPaaSを体験してみませんか？？

ちなみに本記事は次のような方向けです。

* gitでmasterにコミットできるよ！
* コマンドライン（CLI）でディレクトリ移動とかできるよ！
* wordpress触ったことあるよ！
* でもあんまPHP知らないな。。。
* インフラ辛い、開発に集中したい！
* PaaS？？　何それ美味しいの？？？？

#PaaSとは
Platform as a serviceを略してPaaSと呼びます。
インフラ基盤はもちろん、ミドルウェアも提供してくれて、やその他リソース（DBやredis等）も呼び出せる仕組みを提供してくれるので簡単にアプリを動作させることができます。
ざっくり言うと開発に専念することができる環境を提供してくれるサービスですね。

#Heroku
Herokuとはセールスフォース社が提供しているPaaSです。
https://jp.heroku.com/
Buildpackという仕組みを用いることで様々な言語、フレームワークが動作可能な仕組みを提供しています。
現時点で公式にサポートされている言語は以下の通りです。

* Ruby
* Python
* Node.js
* PHP
* Go
* Java
* Scala
* Clojure

言語ごとのサポート範囲については以下のページが参考になります。
https://jp.heroku.com/languages

本記事ではPHP（wordpress）を動作させながらHerokuへの理解を深めていきます。

##Heroku専門用語たち

### Dyno（ダイノ）
アプリケーションを動作させているコンテナ（のようなもの）です。
Dynoには、webプロセスとworkerプロセスがあり、この数をスケールすることでリソース的なチューニングが行えます。
webプロセスは、apacheやnginxのようにHTTPリクエストを捌くプロセスで、
workerプロセスは、それ以外のバックグラウンドの処理を行います。

ちなみにCPUは共有です。
お金を積めば独占することも可能です。
https://devcenter.heroku.com/articles/dyno-types

### Add-on（アドオン）
アプリケーションが使用する外部リソースです。
代表的なものだとMySQLやPostgresといったRDB、
Redisのようなキャッシュがあります。

## 無料枠
Herokuには無料枠が存在して、クレジットカードを登録しているか否かで、制限が変わってきます。

Herokuではクレジットカード登録なしを未認証アカウントと呼び、
クレジットカード登録ありを認証済みアカウントと読んでいます。

###クレジットカード登録なしでできること
* デプロイ、自動パッチング、統計ログ、ビルドパック、ルーティング、dyno管理、ssh経由のdyno（heroku exec）
* １月辺り550時間分のDyno利用。
* 最大で5つのアプリ作成可能。
* ドメイン系（ワイルドカード、サブドメイン、カスタムドメイン）が使える

###クレジットカード登録後できるようになること
* １月辺り1000時間分のDyno利用。
* 最大で100つのアプリ作成可能。
* アドオンの利用可能（アドオン毎の無料プランであれば追加できるアドオンの数に制限なし）

Dynoの利用時間についてですが、持っているアカウント全体で利用時間のプールを共有します。
例えば、未認証状態でアプリを5つ立ち上げたとするとアプリ1つにつき、1月辺り110時間しか動作させることができません。

Herokuでは30分間アプリに動作がない場合はDynoがスリープ状態に移行します。
スリープ中は上記に書かれているDyno利用時間に含まれません。

Dynoのスペックは認証に関わらず、メモリ512MBのwebプロセス1つ、workerプロセス1つまでです。

これは本記事の最終更新日当時のお話なので、利用される際には公式ページをチェックしましょう。
https://jp.heroku.com/pricing
https://www.heroku.com/free


#触ってみよう！
## 検証環境
<dl>
<dt>OS</dt>
<dd>macOS High Sierra 10.13.5</dd>
<dt>heroku cli</dt>
<dd>heroku/7.7.8 darwin-x64 node-v10.7.0</dd>
<dt>git</dt>
<dd>git version 2.15.2 (Apple Git-101.1)</dd>
<dt>composer</dt>
<dd>Composer version 1.7.2 2018-08-16 16:57:12</dd>
<dt>php</dt>
<dd>PHP 7.1.16</dd>
</dl>

## 最初にやること
アプリのデプロイを体験される方は次の準備が必要となります。
### Herokuへの登録
本記事で行うHeroku操作は全て無料枠ですが、wordpressを扱うので、アドオン利用（MySQL）があります。
なのでクレジットカードを登録して認証済みアカウントとなっている必要があります。
https://signup.heroku.com/

### heroku cliのインストールとログイン
本記事でherokuへの操作はheroku cliを通して行いますので、heroku cliのインストールが必要です。
https://devcenter.heroku.com/articles/heroku-cli

## まずはデプロイ
### 最新版のwordpressをダウンロード

```
$ curl -sS https://wordpress.org/latest.zip > wordpress.zip
```

### 解凍と不要なアーカイブの削除

```
$ unzip wordpress.zip
$ rm wordpress.zip
```

### gitローカルレポジトリ作成

今回はHerokuへのデプロイはgitコマンドを通して行いますのでgitローカルレポジトリを作成します。
次のようにして下さい。

```
$ cd wordpress
$ git init
$ git add .
$ git commit -m "最初のコミット"
```

### wp-configをリネームし、DBへの接続情報の箇所を次のように修正

```
$ mv wp-config-sample.php wp-config.php
```

```wp-config.php
$url = parse_url(getenv('CLEARDB_DATABASE_URL'));
/** The name of the database for WordPress */
define('DB_NAME', trim($url['path'], '/'));

/** MySQL database username */
define('DB_USER', $url['user']);

/** MySQL database password */
define('DB_PASSWORD', $url['pass']);

/** MySQL hostname */
define('DB_HOST', $url['host']);

/** Database Charset to use in creating database tables. */
define('DB_CHARSET', 'utf8');

/** The Database Collate type. Don't change this if in doubt. */
define('DB_COLLATE', '');
```

ここまでの編集をコミットしておきましょう。

```
$ git add .
$ git commit -m "wp-configにDB接続情報を書き込み"
```

### Herokuでdyno（アプリ）を作成する

名前を指定する場合は次のように実行します。

```
$ heroku create sample-wordpress-app
```

ここで作成したアプリ名がそのままURLに反映されるので他の人がheroku上で既にその名前を使ってしまっているとコマンドが失敗してしまいます。

```
$ heroku create sample-wordpress-app
Creating ⬢ sample-wordpress-app... !
 ▸    Name sample-wordpress-app is already taken
```

「うわぁぁぁ！面倒くさい！！」という方は次のように引数をつけずに実行すればユニークな名前を付与してもらえます。

```
$ heroku create
Creating app... done, ⬢ pacific-island-67842
https://pacific-island-67842.herokuapp.com/ | https://git.heroku.com/pacific-island-67842.git
```

### DBを作成

今回はClearDBという名前のアドオンを使用します。
ClearDBとは、MySQLサービスをHerokuに提供してくれているアドオンです。
https://elements.heroku.com/addons/cleardb

以下のような制限がありますが、無料プランがあるのでこちらを使用します。

* データベースのサイズは合計で5MBまで
* コネクションは10コネクションまで

これは本記事の最終更新日当時のお話なので、利用される際には公式ページをチェックしましょう。

次のコマンドで作成します。

```
$ heroku addons:add cleardb:ignite
Creating cleardb on ⬢ pacific-island-67842... free
Created cleardb-rugged-22562 as CLEARDB_DATABASE_URL
Use heroku addons:docs cleardb to view documentation
```

### herokuへpush

```
$ git push heroku master
```

#### ブラウザで開く

```
$ heroku open
```

無事にwordpressが動作したらOKです！

※ もし「あれ、表示が変じゃね・・・・」ってなったらhttpsではなく、httpでアクセスしてみてください。

## 解説

さて、git pushした際、他のレポジトリ（github等）にpushした時とは違う出力がいっぱいありましたね。

```
git push heroku master
Counting objects: 1639, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (1614/1614), done.
Writing objects: 100% (1639/1639), 8.54 MiB | 1.05 MiB/s, done.
Total 1639 (delta 168), reused 0 (delta 0)
remote: Compressing source files... done.
remote: Building source:
remote:
remote: -----> PHP app detected
remote:
remote:  !     WARNING: No 'composer.json' found!
remote:  !
remote:  !     Your project only contains an 'index.php', no 'composer.json'.
remote:  !
remote:  !     Using 'index.php' to declare app type as PHP is deprecated and
remote:  !     may lead to unexpected behavior.
remote:  !
remote:  !     Please consider updating your codebase to utilize Composer and
remote:  !     modern dependency management in order to benefit from the latest
remote:  !     PHP runtimes and improved application performance, as well as
remote:  !     control over the PHP versions and extensions available.
remote:  !
remote:  !     For an introduction to dependency management with Composer and
remote:  !     how to get the most out of PHP on Heroku, refer to the docs at
remote:  !     https://getcomposer.org/doc/00-intro.md and
remote:  !     https://devcenter.heroku.com/articles/getting-started-with-php
remote:
remote: -----> Bootstrapping...
remote: -----> Installing platform packages...
remote:        NOTICE: No runtime required in composer.lock; using PHP ^5.5.17
remote:        - nginx (1.8.1)
remote:        - php (5.6.37)
remote:        - apache (2.4.34)
remote: -----> Installing dependencies...
remote:        Composer version 1.7.2 2018-08-16 16:57:12
remote: -----> Preparing runtime environment...
remote:        NOTICE: No Procfile, using 'web: heroku-php-apache2'.
remote: -----> Checking for additional extensions to install...
remote: -----> Discovering process types
remote:        Procfile declares types -> web
remote:
remote: -----> Compressing...
remote:        Done: 22.1M
remote: -----> Launching...
remote:        Released v4
remote:        https://pacific-island-67842.herokuapp.com/ deployed to Heroku
remote:
remote: Verifying deploy... done.
To https://git.heroku.com/pacific-island-67842.git
 * [new branch]      master -> master
```

これを紐解いていくと、Herokuが裏側が少しだけ見えてきます。

### PHPのランタイム
まずは以下の部分に注目して下さい。

```
remote: -----> PHP app detected
remote:
remote:  !     WARNING: No 'composer.json' found!
remote:  !
remote:  !     Your project only contains an 'index.php', no 'composer.json'.
remote:  !
remote:  !     Using 'index.php' to declare app type as PHP is deprecated and
remote:  !     may lead to unexpected behavior.
remote:  !
remote:  !     Please consider updating your codebase to utilize Composer and
remote:  !     modern dependency management in order to benefit from the latest
remote:  !     PHP runtimes and improved application performance, as well as
remote:  !     control over the PHP versions and extensions available.
remote:  !
remote:  !     For an introduction to dependency management with Composer and
remote:  !     how to get the most out of PHP on Heroku, refer to the docs at
remote:  !     https://getcomposer.org/doc/00-intro.md and
remote:  !     https://devcenter.heroku.com/articles/getting-started-with-php
```

Heroku側で、アプリの言語がPHPであることを自動検出した後、WARNINGが発生しました。
index.phpがあるため、PHPアプリケーションであることはHeroku側で自動検出しましたが、
composer.jsonが存在しないため、WARNINGが出てしまいました。

Herokuではcomposerを用いて、phpのランタイムを指定したり、ライブラリの依存関係解決、ランタイム及びバージョンの指定を行うことができます。

HerokuでサポートされているPHPランタイムには、「HHVM」か「PHP」がありますが、
HHVMでは今後PHPのサポートをしないようなので、ランタイムはPHP一択かと思います。
https://mag.osdn.jp/18/09/14/162000

少し実験してみましょう。
まずは挙動の観察がしやすいようにphpinfoを導入します。

```info.php
<?php
phpinfo();
```

```
$ git add info.php
$ git commit -m "phpinfoを追加"
$ git push heroku master
```

この状態で
http://アプリケーションのURL/info.php
にアクセスすると今のphpがどんな状態で動いているかが分かります。

では、動作しているPHPのバージョンを上げてみましょう。
まずはお手元の環境にphpとcomposerが動作する状況であるか確認してください。
実はHerokuではcomposer.jsonがないとWARNINGですが、composer.jsonが存在し、composer.lockがないと次のようなエラーが出るため手元の環境でcomposer.jsonからcomposer.lockを作成する必要があります。
これはcomposerのベストプラクティスに従ったもので、複数の環境にわたる依存関係の再現性のある動作を保証するためにlockファイルが必須となっています。
https://devcenter.heroku.com/articles/deploying-php#manage-dependencies

```
remote:  !     ERROR: No 'composer.lock' found!
remote:  !
remote:  !     A 'composer.lock' file was not found in your project, but there
remote:  !     is a 'composer.json' file with dependencies inside 'require'.
remote:  !
remote:  !     The lock file is required in order to guarantee reliable and
remote:  !     reproducible installation of dependencies across platforms and
remote:  !     deploys. You must follow the Composer best practice of having
remote:  !     your lock file under version control in order to deploy. The
remote:  !     lock file must not be in your '.gitignore'.
remote:  !
remote:  !     Please perform the following steps locally on your computer to
remote:  !     resolve this issue before attempting another deploy:
remote:  !     1) remove 'composer.lock' from file '.gitignore', if present
remote:  !     2) if no 'composer.lock' exists, run 'composer update'
remote:  !     3) stage the lock file changes using 'git add composer.lock'
remote:  !     4) if you edited '.gitignore', also run 'git add .gitignore'
remote:  !     5) commit the change using 'git commit'
remote:  !
remote:  !     Please remember to always keep your 'composer.lock' updated in
remote:  !     lockstep with 'composer.json' to avoid common problems related
remote:  !     to dependencies during collaboration and deployment.
remote:  !
remote:  !     Please refer to the Composer documentation for further details:
remote:  !     https://getcomposer.org/doc/
remote:  !     https://getcomposer.org/doc/01-basic-usage.md
remote:
```

ちなみにcomposerは以下のページを参考にして導入しました。
https://qiita.com/m_snow/items/f9397760fb20733c9e5b

composer.jsonに以下を記載しphpのバージョンを指定します。

```composer.json
{
    "require": {
        "php": "~7.1.0"
    }
}
```

composer.lockファイルの生成を行います。

```
$ composer install
Loading composer repositories with package information
Updating dependencies (including require-dev)
Nothing to install or update
Writing lock file
Generating autoload files
```

composerの特性上、vendorディレクトリも一緒に生成されますが、こちらはデプロイしなくても大丈夫です。
むしろ一緒にデプロイするとWARNINGが出ます。
これもcomposerのベストプラクティスに従っているためですね。
composerでは、venderディレクトリの中身をgit管理すると更新のたび発生する差分や管理コード全体のサイズが大きくなってしまうため、すべての開発者にComposerを使用して依存関係をインストールさせることを推奨しています。
https://getcomposer.org/doc/faqs/should-i-commit-the-dependencies-in-my-vendor-directory.md

なので、composer.lockはgit管理下に置きつつ、venderディレクトリは無視するようにしましょう。

```
$ echo vendor > .gitignore
$ git add .gitignore composer.lock
$ git commit -m "composer.lockの生成とvendorディレクトリの無視"
```

この状態でHerokuにデプロイして、info.phpにアクセスしてみましょう。
phpのバージョンが上がっているはずです。

```
$ git push heroku master
```

また、先ほどまで出ていた「WARNING: No 'composer.json' found!」がなくなっていることにも注目して下さい。

### フロントサーバの選択

さて、続いては最初のgit push時出力の以下の部分に注目していきます。

```
remote: -----> Bootstrapping...
remote: -----> Installing platform packages...
remote:        NOTICE: No runtime required in composer.lock; using PHP ^5.5.17
remote:        - nginx (1.8.1)
remote:        - php (5.6.37)
remote:        - apache (2.4.34)
remote: -----> Installing dependencies...
remote:        Composer version 1.7.2 2018-08-16 16:57:12
remote: -----> Preparing runtime environment...
remote:        NOTICE: No Procfile, using 'web: heroku-php-apache2'.
remote: -----> Checking for additional extensions to install...
remote: -----> Discovering process types
remote:        Procfile declares types -> web
```

PHPのバージョンについては前述で解説した通りですね。

Herokuでは、Dyno内でPHPと一緒に動作させるフロントサーバの選択ができます。
なお、フロントサーバのバージョン選択はできず、
本記事の執筆段階では、以下のバージョンのみサポートされています。

* Apache 2.4 (2.4.34) 
* Nginx 1.8 (1.8.1) 

参考：
https://devcenter.heroku.com/articles/php-support#web-servers

PHPの組み込みサーバを使用することも可能ですが、本番運用では非推奨です。

フロントサーバの選択ですが、Procfileと呼ばれるファイルに使用するサーバを記載して行います。
ProcfileはHeroku側でforemanというツールによって読み込まれて、Dyno内で実行されます。

記載する書式ですが、<プロセスタイプ>: <実行するコマンド> という書式で記述します。
今回はプロセスタイプwebを利用してフロントサーバの定義を記載しますが、その他にも次のようなことが定義可能です。

* アプリのウェブサーバー（プロセスタイプ＝web）
* 複数のタイプのワーカープロセス（プロセスタイプ＝worker）
* クロックなどのシングルトンプロセス（プロセスタイプ＝clock）
* 新しいデプロイ資源が展開される前に実行するタスク（プロセスタイプ＝release）

Herokuでは、webプロセスとして定義したものが、HerokuのルータからHTTPリクエストを受け付けるプロセス、すなわちフロントサーバになります。

先ほどの出力を見るとデフォルトだとapacheが選択されたようですね。

```
remote: -----> Preparing runtime environment...
remote:        NOTICE: No Procfile, using 'web: heroku-php-apache2'.
```

では実際にフロントサーバをnginxに変更してみましょう。
Procfileに以下を記載します。

```
web: vendor/bin/heroku-php-nginx
```

git addを行い、herokuにpush。

```
$ git add .
$ git commit -m "フロントサーバをnginxに変更"
$ git push heroku master
```

先ほどのinfo.phpを開いて、SERVER_SOFTWAREをみてみると
フロントサーバが切り替わったことが確認できることと思います。

### 最後に

さて、今回はHeroku上でwordpress（php）を動かしながら、
PaaSへの理解を深めていきました。
振り返ってみて以下のような作業をやりましたが、これをIaaS（AWSのEC2等）でやるのは大変です。

* PHPランタイムの選択
* DBのセットアップと接続
* フロントサーバの変更

このようにサービス利用時に必要となるサーバから、ミドルウェア、DBやcacheといった外部リソースを簡単に呼び出せて利用できる、より開発のビジネスロジックに専念できることが、PaaSのサービスなのです。

ただのクラウドサーバ利用だけに留まらず、クラウドのリソースを最大限に利用し総合的なコストの削減を目標とするクラウドネイティブが叫ばれている昨今では、PaaSというのは持っていて損はないカードだと思います。

また機会があればもう少し詳しいところに踏み込んでいきますね。
お読みいただきありがとうございました。