ボクココ

個人開発に関するテックブログ

Heroku x Rails 使うならオススメの環境構築方法

普通にRails扱うのと、Herokuにあげる用のRailsを扱うのとではやはり構造とかを多少変える必要がある。

f:id:cevid_cpp:20150831200227j:plain

以下のやり方は、厳密に言えば「できる限り本番と同じ環境にした方が良い」というやり方とは違うため、この方法をするのであれば、必ずHerokuにstaging環境のアプリをもう一つ作ることを推奨する。やり方は以下を参照していただきたい。

blog.ruedap.com

さて、今回はローカルとHerokuで共に開発しやすい環境を作るTIPSをお届けする。

Gemfile 構成

以下が基本的なテンプレートとなる。

group :production, :staging do
  gem 'pg'
  gem 'rails_12factor'
end

group :test, :development do
  gem 'sqlite3'
  gem 'dotenv-rails', '0.11.1'
end

gem 'asset_sync'
gem 'unicorn'
gem 'redis'
gem 'redis-rails'

pg と sqlite3 の使い分け

まず注意したいのが、HerokuはPostgresqlを利用するということで、production と staging 環境では pg をインストールするようにする。 反対に development と test ではsqlite3 を利用する。

ローカル環境で bundle install するときは、--without オプションをつけるようにする。

bundle install --without production staging

こうすることでローカルマシンに postgresql が入っていなくとも bundle install が成功する。

herokuにソースをデプロイする際は git push heroku master とかやれば勝手に 各環境だけの bundle install をしてくれるので心配はない。

やはりpostgresql と sqlite3 というDBの違いで問題が出てくることがある。 特に ActiveRecordを使わずに生のSQLを発行していたりすると問題が発生する確率が高まる。そんなときは心苦しいけども、

if Rails.env.development? || Rails.env.test?
  ..
else
  ..
end

と書けばOK。

そもそもなんで分けるのかというと、postgresql の環境構築が手間だからというだけなので、 本当なら ローカルに Postgresql を入れた方が良い。

まぁそんなこと気にせず vagrant とか使えばいいのだけども、わざわざ vagrant 起動して ssh とかで入って開発というのも面倒なので、という色々と楽をしたいがゆえのこの構成である。

環境変数の扱い

環境ごとの値の差分 (例えば外部サービスの Auth トークンなど) はそれぞれ分けたいところだろう。そこをローカル環境で便利に扱えるのが dotenv-rails だ。これはherokuを使うなら必須と言ってもいいgemである。

Railsプロジェクト直下に .env を配置し、

AWS_TOKEN: .......
AWS_SECRET: .......

などのように環境変数を定義し、ソースの中で ENV['AWS_TOKEN'] と書けばその設定を読み込むことができる。

Herokuの環境に環境変数を反映したい場合は、 heroku config を利用する。 heroku config:set AWS_TOKEN=.... とすればセット完了だ。

セッションストアとしての Redis と Cookie

ローカルでわざわざ Redis を立ち上げるのが面倒な場合、ローカルでは Cookie, Herokuでは Redisと切り替えることができる。これには config/initializers/session_store.rb にて

if Rails.env.development? || Rails.env.test?
  Rails.application.config.session_store :cookie_store, key: '_myapp_session'
else
  Rails.application.config.session_store :redis_store, servers: ENV['REDIS_URL'], expire_in: 1.day
end

とすれば良い。これでローカルでもRedisを起動せずともエラーが出ずに開発が可能だ。

Unicorn の設定

これは Heroku のときに Unicornなどのアプリケーションサーバを使のは必須。デフォルトではWebrick を使っているのでパフォーマンスが明らかに違う。 Heroku x Rails にするなら必ずやっておきたい設定だ。

config/unicorn.rb に以下をコピペしよう。詳細はDeploying Rails Applications with Unicorn | Heroku Dev Centerを参照していただきたい。

# config/unicorn.rb
worker_processes Integer(ENV["WEB_CONCURRENCY"] || 3)
timeout 20
preload_app true

before_fork do |server, worker|
  Signal.trap 'TERM' do
    puts 'Unicorn master intercepting TERM and sending myself QUIT instead'
    Process.kill 'QUIT', Process.pid
  end

  defined?(ActiveRecord::Base) and
    ActiveRecord::Base.connection.disconnect!
end

after_fork do |server, worker|
  Signal.trap 'TERM' do
    puts 'Unicorn worker intercepting TERM and doing nothing. Wait for master to send QUIT'
  end

  defined?(ActiveRecord::Base) and
    ActiveRecord::Base.establish_connection
end

ローカルで開発するぶんには、 rails server で普通に立ち上げれば良い。

Asset Sync

更新

Asset Syncを利用するのは公式で止めるよう勧告が出ている。CloudFrontを利用する方法が推奨されている。以下の内容は古いのでご注意を。


Heroku に画像やCSSなどを全て読み込ませるには効率が悪い。これらは Amazon S3などから読み込めるようにしよう。具体的な方法はAssetSync/asset_sync · GitHubにある。

config/asset_sync.yml にて

defaults: &defaults
  fog_provider: 'AWS'
  aws_access_key_id: "<%= ENV['AWS_ACCESS_KEY_ID'] %>"
  aws_secret_access_key: "<%= ENV['AWS_SECRET_ACCESS_KEY'] %>"
  fog_directory: "<%= ENV['AWS_S3_BUCKET'] %>"
  fog_region: "<%= ENV['AWS_REGION'] %>"
  existing_remote_files: delete
  gzip_compression: true
  fail_silently: true

development:
  <<: *defaults
  enabled: false

test:
  <<: *defaults
  enabled: false

staging:
  <<: *defaults

production:
  <<: *defaults

こんな感じでセットしてあげればOK。 ENVが各 heroku config から値を読み込んでくれる。