RailsとHeroku、AWSでAwesomeな画像アップロードからのサムネイル作成

  • 54
    いいね
  • 0
    コメント
この記事は最終更新日から1年以上が経過しています。

はじめに

あのさぁ・・・Heroku使ってる? 1 Applicationあたり1 dynoまで無料なの素晴らしいよね!

無料という言葉が全てを許す。確認だけのステージングアプリケーションだったら1 dynoでいけるし、Twitter BOTや簡単な定期処理、まだダイヤの原石であるアクセス数の少ないウェブサービスなんかには持ってこい!デプロイもAWS Elastic Beanstalkなんかに比べたらめがっさ速いです!(アーキテクチャ上当たり前ということは置いといて)

立ちはだかる「画像アップロードからのサムネイル作成」

ただやはり制限があると難しいことも生まれてくるのです。ここではその中でも 画像アップロードからのサムネイル作成 を取り扱います。Herokuにおける問題は

  • リージョンがUSであることによる アップロード時間の増大
    • 30秒制限による画像アップロード+サムネイル作成の タイムアウト
    • 上記による 前のリクエストの処理待ち
  • 内部記憶領域が揮発性であることによる 画像(アップロード画像、サムネイル画像)の保存場所問題

RailsとHeroku、AWSでAwesomeな画像アップロードからのサムネイル作成

では早速本題に入りましょう。ここでは

  1. ブラウザからの画像アップロード
  2. (アップロードされた)元画像からのサムネイル画像作成
  3. サムネイル画像の保存

の大きく三つに分けて私の現時点でのベストプラクティスをお話したいと思います。
色々とぐぐって組み合わせた上でのベストプラクティスなので、恐らくもっと良い方法も出てくると思います。そういった際はコメント等して頂けると嬉しいです。

1. ブラウザからの画像アップロード

Herokuへのアップロードは北米リージョンでありアップロード時間がかかる上、記憶領域が揮発性ですので更に他のストレージへの転送が必要になってきます。そうなると30秒制限によりタイムアウトになったり、利用者が多い場合はアップロードの順番待ちが行われ(Request queueingを参照)アップロード時間が非常に長くなってしまう場合があります。
そこでS3へのダイレクトアップロードです。Herokuを通さずアップロードすることで、その部分の負荷をまるまる減らすことが出来ます。gemを以下に示します。

これらはCORSを使うことでHeroku上のページからS3へのダイレクトアップロードを可能にしています。二つ目のs3_file_fieldは一つ目のs3_direct_uploadを機能を削減しながら使いやすくしたもので、既存のfile_fieldの代わりにしやすいです。(これはダイレクトアップロード後、アップロード先のURLが入ったtext inputを生成したりしてくれます)
ちなみにS3のリージョンは個人的にはユーザに近い方をお勧めします。つまり日本に住む人が対象だったら東京リージョンですね。と言うのも、日本にいる人が画像の保存や取得をするのに都合が良いからです。(当たり前ですが)もっともサムネイルを作成する時などHerokuから画像を取得や保存する時にはUSリージョンより遅くなってしまいますが、サムネイル作成よりも画像の保存や取得の比率の方が大きいので総合的に見て前者をお勧めしています。

2. (アップロードされた)元画像からのサムネイル画像作成

この分野ではPaperclipcarrierwaveなどが有名ですが、今回のようにこれらのgemにあるアップロード機能を使わない場合はちょっと重厚長大です。
よってこの場合はon-the-flyで(サムネイルへのアクセス時に)サムネイルを作成でき、薄く軽いDragonflyがお勧めです。元画像をS3のものに指定することが出来、もちろん1でアップロードされた元画像を指定することも可能です。またon-the-flyの利点としてサムネイルサイズが変更になったとしても、Paperclipの様にこれまでの元画像のサムネイルを明示的に再作成する必要がありません。サムネイルの作成はアクセスがあった時に行われます。

3. サムネイル画像の保存

2で作成するサムネイル画像ですが、アクセスの度に生成していたらレスポンスタイムが遅くなって仕方がないです。Dragonflyでは簡単にローカルにサムネイルのキャッシュを持つことが可能ですが、dynoのディスクは揮発性なので再起動したらキャッシュが消えてしまいます。
そこでCloudFrontを使いましょう。CloudFrontからHerokuへアクセスするように設定し、画像へのアクセスをCloudFrontに飛ばすことで、間に挟まりキャッシュとして機能します。proxyとして機能しキャッシュをしてくれていると考えると分かりやすいかもしれません。
Heroku側での設定はDragonflyに一行足すだけです。以下は一つ目がやり方でそれ以降は参考文献です。

ただCloudFrontが行うキャッシュの有効期限が不安だったので(短いと結局何度もサムネイル作成しちゃう)、関連する記事を備忘録的に残しておきます。ここらへんのベストプラクティスや検証結果かもんかもんです。
→今Dragonflyが生成するサムネイルのヘッダを見てみたら以下の様に書いてありますね。問題無いみたい?

Cache-Control:public, max-age=31536000

CloudFrontの代わりに S3にサムネイルを保存 して、サムネイルの作成有無をデータベースに保存するということも可能です。Dragonflyは色々と載っていてドキュメント素晴らしいですね。

おわりに

Heroku最高!!!!!あとRailsというエコシステム最高!!!!!!!!

この投稿は Ruby on Rails Advent Calendar 201318日目の記事です。