Sinatra&ActiveRecordなアプリケーションをHerokuにデプロイする

Rubyで簡単なWebアプリケーションを作ってとりあえず公開したい時に、気軽にデプロイできるPaaSがHerokuです。 ただ、自分は色々躓いてしまったのでデプロイの手順を残しておこうと思います (Herokuの説明は省きます)。

アプリケーションの構成

  • FWはSinatra
  • ORMはActiveRecord
  • クライアントサイドはテンプレートエンジンにslim、あとはCoffeeScript。これらの静的ファイルをGruntでコンパイルしている

事前準備

Herokuアカウントの作成、Herokuクライアント(Heroku Toolbelt)のインストールが必要です。 Herokuの公式ページから実施します。 また、HerokuにデプロイするにはプロジェクトをGitで管理している必要があります。

Herokuアプリケーションの初期化

heroku create

gitのリモートリポジトリを確認するとHerokuのリポジトリが追加されています。

ビルドパックの指定

アプリケーションがどの言語を使用しているかは通常Herokuが判断してビルドしてくれます。 今回のアプリケーションはフロント開発にGrunt、npmを使用しているため、package.jsonが存在します。
このファイルのせいでHerokuがNodeJSアプリケーションと判断してしまう可能があるので、今回は環境変数にRubyアプリケーションであることを明示的に設定します。

heroku config:set BUILDPACK=https://github.com/heroku/heroku-buildpack-ruby

Heroku Postgresの追加

Heroku上にDBを作成します。

heroku addons:add heroku-postgresql

環境変数を確認するとDATABASE_URLが追加されています。以下のコマンドで確認できます。

heroku config

設定するとローカル環境からHeroku環境のDBにアクセスすることもできます。

heroku pg:psql

Heroku環境変数の設定

passwordなどgitで管理したくないものはHerokuの環境変数に指定できます

heroku config:add password="パスワードの値"

Procfileの作成

Herokuでアプリケーションを起動させるにはProcfileというファイルに起動コマンドを記述します。

web: bundle exec rackup config.ru -p $PORT

とりあえずWebのインスタンスだけ指定します。PORT環境変数はデフォルトで5000番が割り当てられてます。

とりあえずローカル環境で起動してみる

アプリケーション起動はforemanコマンドで行います。ローカルにはPORT環境変数がないので引数で与えます。

env PORT=5000 foreman start

デプロイする

ここまでの設定でHerokuにアプリケーションをデプロイできます。次のコマンドを打ちます。

git push heroku master

ところが...Heroku環境のDB接続ができない

苦戦しました。アプリケーション内でpostgresのgemを使って接続しているのですがコケました。 アプリケーション内では次のようにDBに接続しようとしていました。

@db = PG::connect(conf["development"])

エラーはDBサーバが見つからないという内容です。

could not translate host name ... to address: name or server not known 

Heroku環境ではconfigディレクトリを見つけると自動でdatabase.ymlを作成します。 自動生成されたdatabase.ymlはActiveRecord用のものでローカル環境で使っていたdatabase.ymlとは微妙に内容が違っていました(ローカル環境のDatabase.ymlはPostgresに直接接続するために独自の内容を記述していました)。

自動で作成されたdatabase.ymlは以下の内容です。

{"production"=>{"adapter"=>"postgresql", "database"=>"データベース名", 
"username"=>"ユーザ名", "password"=>"パスワード", "host"=>"ホスト名", "port"=>ポート番号}}

一方、ローカル環境で使用しているdatabase.ymlに設定した値(postgresのgemでDBに接続するのに必要なパラメータ)は以下です。

  • host
  • user
  • password
  • dbname
  • port

自動生成されたyamlとキー名が少し異なっています(databaseとdbname)。またパラメータも足りていません(adapter)。
結局、自動生成されるdatabase.ymlの内容をHeroku環境の場合のみDB接続時に使用するようにして、エラーを解消しました。

if ENV['DATABASE_URL']
  pg_conf = { host: conf["production"]["host"], user: conf["production"]["username"],
            password: conf["production"]["password"], dbname: conf["production"]["database"],
            port: conf["production"]["port"] }
  @db = PG::connect(pg_conf)
else
  @db = PG::connect(conf["development"])
end

最後に

純粋なRuby on RailsやSinatraアプリケーションだとすごく簡単にデプロイできるのですが、今回のアプリケーションの場合はDB接続で躓きました。 Paasを利用する場合、アプリケーション自体をもう少しデプロイする環境に最適化すべきだと感じました。