今日はこのへんで

プログラミングやプロダクト開発について。もしくはただの雑記。

HerokuをRailsアプリの本番環境として使う

Herokuというのは今さらだけれども、最近、本番環境をHerokuでセットアップする機会があったのでメモを残しておく。前提としてはRailsで作られたWebアプリケーションで、まだほとんどユーザがいないけど本番環境としてある程度まともにセットアップできているレベルを目指したもの。

Herokuインスタンスの無料の範囲

Herokuのインスタンスは、毎月少なくとも550時間は無料で使える。なんだけど、この無料インスタンスは30分間トラフィックがない場合自動的にシャットダウン(スリープ)してしまうため、本番環境としては使えない。なので基本的には $7/month の Hobby Dyno 以上のプランを利用して運用する必要がある。

If an app has a web dyno, and that web dyno receives no traffic in a 30 minute period, the web dyno will sleep. In addition to the web dyno sleeping, the worker dyno (if present) will also sleep.

https://devcenter.heroku.com/articles/free-dyno-hours

Herokuインスタンス(Dyno)で気をつけるべき仕様

有料プランであっても、Herokuのインスタンスは約1日のサイクルで勝手に再起動する。開発者側ではこのタイミングをどうにかすることはできない。特にアプリ側で長い処理を行う場合には、トランザクションに気をつける必要がある。

Dynos are also restarted (cycled) at least once per day to help maintain the health of applications running on Heroku. Any changes to the local filesystem will be deleted. The cycling happens once every 24 hours (plus up to 216 random minutes, to prevent every dyno for an application from restarting at the same time).

https://devcenter.heroku.com/articles/dynos

また、Herokuインスタンスでは、インスタンス内にファイルを保持しておく方法がない。Restart の度に全てのファイルは削除されるので、ファイルのアップロードなどで永続化するファイルはS3かどこかに保存する必要がある。

Each dyno gets its own ephemeral filesystem, with a fresh copy of the most recently deployed code. During the dyno’s lifetime its running processes can use the filesystem as a temporary scratchpad, but no files that are written are visible to processes in any other dyno and any files written will be discarded the moment the dyno is stopped or restarted.

https://devcenter.heroku.com/articles/dynos#ephemeral-filesystem

Heroku のインスタンスはDB用のインスタンスも含めて、基本的にプライベートなネットワーク内に置くことができない。( Heroku Private Spaces) という機能があるが、Heroku Enterpriseのみにしか提供されていない。)

なので、個人情報保護の問題で、DBなどをネットワーク的に隔離したい場合には、気軽に Heroku を使うことができない。そうしたい場合は素直にAWSでVPCを構成しましょう。

あと、現在Herokuインスタンスを立ち上げる際にサポートされるRegionはUSとEUの2つしかなく、インスタンスをTokyoに立ち上げることはできない。レイテンシは気にしないようにしましょう。

Two regions are available, US and EU. Each dyno is secured with strong firewall rules so even though all dynos run in a single, flat network, they are strongly isolated from each other.

https://devcenter.heroku.com/articles/dyno-runtime#common-runtime

各種モニタリングツールの活用

Heroku の良いところの一つに、色々な有料モニタリングツールがアドオンとして無料で使えるところがある。

有名どころとしては以下のようなものがあるので設定しておくと良い。

個人的なおすすめはAirbrakeとPapertrail。他にもDeploy HooksでSlackに通知したりするのも便利だし、Herokuダッシュボードのメトリクスツールもよくできている。

HTTPS化とLet’s Encrypt

今ではHerokuで運用しているサービスも自動でrenewされるLet’s EncryptでHTTPS化できる。もちろんLet’s Encryptでなくても自分で買った証明書を設定することも可能。

HerokuでのLet’s Encryptの設定は以下に詳しい。

https://devcenter.heroku.com/articles/automated-certificate-management

なんだけど、Heorkuで設定されるドメイン(*.herokussl.com)をCNAMEでトップレベルドメインに割り当てることができないので注意が必要。

Configuring your DNS provider for a root domain is similar to configuring a DNS provider for a subdomain. However, whereas with subdomains the type of record to configure is always a CNAME, with root domains the type of record depends on the DNS provider:

https://devcenter.heroku.com/articles/custom-domains#add-a-custom-root-domain

これはRFC1912で規定されているのでしょうがない。DNSプロバイダによってはALIASやANAMEといった代替手法が使える場合もある。個人的には結局AWSのCloudFrontをかませてAWS側でHTTPS化した。

CI を使ってデプロイする

Herokuへのデプロイは、Gitを利用してHeroku上のリポジトリにpushすることで簡単に行うことができる。もう少しまともにやろうと思ったら、CIなどでテストが通ったときにデプロイしたいけど、これも難しくない。

Circle CIでは、Herokuとインテグレーションがされていて、HerokuのAPI Keyを登録して circle.yml に以下のように追記するとCIからデプロイできるようになる。便利。

deployment:
  production:
    branch: master
    heroku:
      appname: heroku-appname

デプロイ時のコマンドをカスタマイズするなど詳しくは以下。

https://circleci.com/docs/1.0/continuous-deployment-with-heroku/

Worker を動かす

Herokuでは、WorkerのプロセスをWeb用のインスタンスとは別に動かす必要がある。今回はActiveJobのプロバイダとして mperham/sidekiq を利用した。

基本は以下の設定のとおりにやった。 https://github.com/mperham/sidekiq/wiki/Active-Job

各種ファイルの設定は以下のような感じ。

Procfile

web: bundle exec puma -C config/puma.rb
worker: bundle exec sidekiq -C config/sidekiq.yml

environments/production.rb

config.active_job.queue_adapter     = :sidekiq
config.active_job.queue_name_prefix = "myapp_#{Rails.env}"

config/sidekiq.yml

:verbose: false
:concurrency: 5
:queues:
  - myapp_production_default
  - myapp_production_mailers

yamlに処理するqueueのkey名を列挙しておかないと処理されない。またconcurencyの数とRedisの同時接続上限に気をつける。

そして、バックエンドにRedisを使う場合はもう少し設定が必要。(最初やってなくてQueueが処理されず困った)

https://github.com/mperham/sidekiq/wiki/Using-Redis

REDIS_PROVIDER環境変数をセットする必要がある。値はRedisのURLがセットされている環境変数のkey名(url自体ではない)。

heroku config:set REDIS_PROVIDER=REDISCLOUD_URL
# config/initializers/sidekiq.rb
Sidekiq.configure_server do |config|
  config.redis = { url: ENV['REDISCLOUD_URL' }
end

Sidekiq.configure_client do |config|
  config.redis = { url: ENV['REDISCLOUD_URL'] }
end

Redis プロバイダとその活用

Herokuでは、様々なRedisホスティングのプロバイダがいるのでサービスを選ぶと良い。無料枠でメモリが一番多いのが Redis Cloud っぽいので、自分はそれを使っている。

また、Heroku には Memcached のプロバイダがいないので、RailsではSessionやCacheにもRedisを使うのが楽で良い。以下のようにすれば設定可能。

# config/environments/production.rb
  config.cache_store = :redis_store, ENV['REDISCLOUD_URL'], { expires_in: 2.hours }
  config.session_store :redis_store, servers: [ENV['REDISCLOUD_URL']]

cron ではなく Heroku Scheduler

定期的なタスクを実行したい場合は、cronではなくHeroku Scheduler というアドオンを使うのが一般的。実行頻度は Daily/Hourly/Every 10min から選択することができる。

Heroku Schedulerで起動されたインスタンスはその起動時間に応じて請求が発生する(もしくは無料範囲の時間が消費される)。

また注意点として、2分以上かかるような長いJobはHeroku Schedulerのインスタンス内では実行できないので、WorkerにJobを登録するなどといった対策が必要。

Scheduled jobs are meant to execute short running tasks or enqueue longer running tasks into a background job queue. Anything that takes longer than a couple of minutes to complete should use a worker dyno to run.

https://devcenter.heroku.com/articles/scheduler


Heroku は頑張らないインフラにおすすめです。

f:id:Kechol:20170524223347p:plain