CakePHP2 アプリケーションを Heroku 上で動かす設定についてです。
以前のエントリにも書きましたが、Heroku で Web アプリケーションを動かす際に重要なのは、Web サーバ自体(Heroku では、Dyno)に、アプリケーションの状態(データ、セッション情報、ログ等)を保持させないということです。
Heroku の Dyno は、デプロイの際や、定常的な再起動により、破棄されるため、記録されたファイルは消えてしまいます。よって、こうしたデータファイルは、アドオンなど外部に記録する必要があります。
Heroku では、アドオンを活用するのがポイントですので、ここでは、主に CakePHP アプリケーションからこうしたアドオンと連携する方法を見ていきます。
Environments Library as a plugin
まず、開発環境と Heroku 環境で設定値を切り替えるために Environments Library as a plugin を使います。
https://github.com/josegonzalez/cakephp-environments
これは、CAKE_ENV
という環境変数の値で、環境に応じて、データベースやキャッシュなどの設定を切り替えるものです。昨今のフレームワークではお馴染みの機能ですね。
ここでは、下記の設定について切り替えを行います。
- ログ
- データベース
- キャッシュ / セッション
このプラグインでは、まず Config/bootstrap/environments.php
を作成します。このファイルで、環境毎の設定ファイルの読み込みを行います。下記では、Heroku 環境用の heroku.php
と、開発環境用の development.php
を読み込んでいます。
- Config/bootstrap/environments.php
<?php CakePlugin::load('Environments'); App::uses('Environment', 'Environments.Lib'); include dirname(__FILE__) . DS . 'environments' . DS . 'heroku.php'; include dirname(__FILE__) . DS . 'environments' . DS . 'development.php'; Environment::start();
次に、Heroku 環境用の Config/bootstrap/environments/heroku.php
です。
環境の設定は、Environment::configure()
で行います。第一引数に環境名(CAKE_ENVで指定するもの)、第二引数は適用条件(true なら、CAKE_ENV に合致しなければデフォルトとして適用)、第三引数に設定値の連想配列(Configure::write() する)、第四引数にコールバックを指定します。
第三引数で設定する方法もあるのですが、より柔軟な設定を行うために、ここでは、第四引数のコールバックを指定します。実際の設定は、以降で順に指定していきます。
- Config/bootstrap/environments/heroku.php
<?php Environment::configure('heroku', false, [ ], function () { // Heroku 用設定 });
開発環境用の Config/bootstrap/environments/development
です。コールバックで、デフォルトの設定を記述しています。
- Config/bootstrap/environments/development.php
<?php Environment::configure('development', true, [ ], function () { // Log settings App::uses('CakeLog', 'Log'); CakeLog::config('debug', array( 'engine' => 'File', 'types' => array('notice', 'info', 'debug'), 'file' => 'debug', )); CakeLog::config('error', array( 'engine' => 'File', 'types' => array('warning', 'error', 'critical', 'alert', 'emergency'), 'file' => 'error', )); // Database settings Configure::write('DATABASE_OPTIONS', [ 'datasource' => 'Database/Postgres', 'persistent' => false, 'host' => 'localhost', 'login' => 'shin', 'password' => '', 'database' => 'app', ]); Configure::write('TEST_DATABASE_OPTIONS', [ 'datasource' => 'Database/Postgres', 'persistent' => false, 'host' => 'localhost', 'login' => 'shin', 'password' => '', 'database' => 'app_test', ]); // Cache settings Cache::config('default', array('engine' => 'File')); });
次に、database.php
での接続情報を、環境ごとに設定できるように下記のようにします。各環境ファイルで Configure::write()
した値を、DATABASE_CONFIG クラスに設定します。
- Config/database.php
class DATABASE_CONFIG { public $default = []; public $test = []; public function __construct() { $this->default = Configure::read('DATABASE_OPTIONS'); $this->test = Configure::read('TEST_DATABASE_OPTIONS'); } }
このプラグインを有効にするために、bootstrap.php
を以下のように変更します。コメント部分は削除しています。
- Config/bootstrap.php
<?php require_once __DIR__ . '/../vendor/autoload.php'; include __DIR__ . '/bootstrap/environments.php'; Configure::write('Dispatcher.filters', array( 'AssetDispatcher', 'CacheDispatcher' ));
Heroku で環境変数 CAKE_ENV をセット
このアプリケーションが、Heroku にデプロイした際に、heroku.php を利用するように、環境変数CAKE_ENV
にheroku
をセットします。これで、デプロイしたアプリケーションは、heroku
設定で動作するようになります。
$ heroku config:set CAKE_ENV=heroku
ログ
Heroku 環境でのログの設定です。ログは、ファイルではなく、標準出力もしくは標準エラー出力に出力します。
こうしておくと、heroku logs
コマンドでログが確認できます。また、Papertail や FlyData のようにログを各サービスへ転送するアドオンを使うと、出力されたログを閲覧したり、S3 などに転送することができます。
ログは、まずは標準出力もしくは標準エラー出力にして、あとそれをどう活用するかは、アドオンに任せるという考え方です。
下記では、アプリケーションログを標準出力へ、エラー出力を標準エラー出力へ出力しています。
- Config/bootstrap/environments/heroku.php
<?php Environment::configure('heroku', false, [ ], function () { // Log settings App::uses('CakeLog', 'Log'); App::uses('ConsoleOutput', 'Console'); CakeLog::config('debug', [ 'engine' => 'ConsoleLog', 'stream' => new ConsoleOutput(), ]); CakeLog::config('error', [ 'engine' => 'ConsoleLog', 'stream' => new ConsoleOutput('php://stderr'), ]); });
あとは、アドオンと追加すれば、出力されたログがアドオンで利用できます。例えば、Papertrailを以下のコマンドで追加して、アプリケーションを動作させると、Papertrail の画面でログが確認できます。
$ heroku addons:add papertrail
データベース
Heroku のデータベースといえば、PostgreSQL なので、Heroku Postgres を使います。(もちろん、MySQL が良ければ、ClearDB なり、RDS なり使えます。)
$ heroku addons:add heroku-postgresql
Heroku 内では接続情報が環境変数で渡されるので、これをパースします。パースされた情報を、heroku.php で、Configure::write() で、セットしていきます。
- Config/bootstrap/environments/heroku.php
<?php Environment::configure('heroku', false, [ ], function () { // Log settings (snip) // Database settings if (empty(getenv('DATABASE_URL'))) { throw new CakeException('no DATABASE_URL environment variable'); } $url = parse_url(getenv('DATABASE_URL')); Configure::write('DATABASE_OPTIONS', [ 'datasource' => 'Database/Postgres', 'persistent' => false, 'host' => $url['host'], 'login' => $url['user'], 'password' => $url['pass'], 'database' => substr($url['path'], 1), ]); });
キャッシュ / セッション
キャッシュ / セッションのデータストアには、Redis を使います。
ここでは、Redis サービスのアドオンとして、Redis To Go を利用します。
$ heroku addons:add redistogo
データベースと同じく、接続情報が環境変数で渡されるので、それをパース(ホスト、ポート、パスワード)して、セットするだけです。セッションハンドラには、Cache を使いたいので、その指定も行います。
これで、キャッシュもセッションも、Redis To Go に保存されます。
- Config/bootstrap/environments/heroku.php
<?php Environment::configure('heroku', false, [ ], function () { // Log settings (snip) // Database settings (snip) // Cache settings if (empty(getenv('REDISTOGO_URL'))) { throw new CakeException('no REDISTOGO_URL environment variable'); } $url = parse_url(getenv('REDISTOGO_URL')); Cache::config('default', [ 'engine' => 'Redis', 'server' => $url['host'], 'port' => $url['port'], 'compress' => false, 'password' => $url['pass'], 'serialize' => 'php' ]); // Session Settings Configure::write('Session', [ 'defaults' => 'cache', ]); });
さいごに
ベーシックな部分だけですが、Heroku で CakePHP アプリケーションを動かす設定を見てきました。
ここで紹介した以外では、メールやデータファイル(画像等)など、他にもアプリケーションでは必要なものがありますが、ほとんどのものは、ここで見てきたようにアドオンを活用することで実現できます。
こうした構成にしておけば、Dyno が再起動したり、インスタンス数を増減しても、アプリケーションデータはアドオンに保持されているので、動作に支障はありません。こうしたスケーラブルナ構成にしておくのが、Heroku のような PaaS を活用する肝ですね。
このエントリは、CakePHP Advent Calendar 2014 の 3 日目でした。まだ少し空き枠があるので、CakePHP な何かを書いてみてはどうでしょうか。