Heroku での Rails 4+ アセットパイプライン
最終更新日 2022年10月03日(月)
Table of Contents
アセットパイプラインは、バージョン 3.1 で Rails に導入されました。この記事には、Heroku 上の Rails バージョン 4 以上でアセットパイプラインを実行するために必要な情報が含まれています。 このガイドは包括的ではありません。デバッグやトラブルシューティングでのサポートについては、「Rails Asset Pipeline on Heroku Cedar」(Heroku Cedar での Rails アセットパイプライン) を参照してください。この記事では、Rails 4 での動作に固有の追加情報が追加されています。
アセットの提供
デフォルトでは、Rails 4 はアセットを提供しません。この機能を有効にするには、config/application.rb
に移動し、次の行を追加する必要があります。
config.serve_static_assets = true
あるいは、Gemfile に rails_12factor gem を含めることによって同じ結果を得ることもできます。
gem 'rails_12factor', group: :production
この gem は、静的アセットを提供するようにアプリケーションを設定するため、これを設定ファイルで手動で行う必要はありません。
ダイジェストアセットのみの生成
Rails 3 では、アセットパイプラインで使用されるバージョンの sprockets はアセットの “ダイジェスト” または “フィンガープリント” を生成し、それをそのファイル名にアタッチします。たとえば、次のファイル
app/assets/stylesheets/application.css.erb
は、事前コンパイルされると、次のようになります。
public/assets/application-c655834251f73d7714351aa287be8556.css
ここで、"c655834251f73d7714351aa287be8556" は MD5 によって生成された “ダイジェスト” または “フィンガープリント” です。"ダイジェスト" ファイル名に加えて、Rails 3 アセットパイプラインでは非ダイジェストバージョン public/assets/application.css
が生成されます。これは、受信者がそのアセットを適時にダウンロードするかどうかわからない場合のメールの送信などのためには有効でした。
Rails 4 では、sprockets はダイジェストファイル名のみを生成します。つまり、アセットを参照するには、次のような ERB ヘルパーを使用する必要があります。
<%= asset_path('logo.png') %>
ERB ヘルパーを使用する app/assets
内のファイルには必ず .erb
拡張子を追加するようにしてください。そのため、application.css
は application.css.erb
にする必要があります。
“メールの問題” を回避するために、sprockets は、変更された同じアセットの最大 3 つのコピーを同時に保持するようになりました。これはまた、古いアセットへのリクエストを引き続き可能にするための繰り返しのデプロイにも役立ちます。この動作があったとしても、CDN を使用してアセットを提供することをお勧めします。
キャッシング
Heroku では、中間ファイルを保存するアセットパイプラインのキャッシュディレクトリである 50 MB 分の tmp/cache/assets
をキャッシュするようになりました。つまり、これらのファイルを再計算する必要がないため、将来のアセットコンパイルはより高速になります。
これは Rails フレームワークではなく、Heroku での違いです。キャッシングを実質的に使用できなくするいくつかのバグがあるため、Rails 3 アセットはデプロイごとに生成されます。
ディレクトリ tmp/cache/assets/sprockets/
は、slug のサイズを削減するためにランタイムから削除されます (これにより、起動時間がより高速になる)。このディレクトリが削除されるのは、これらのファイルがアプリケーションに必要ない、つまり、動的なアセットコンパイルが無効になっている場合だけです。
config.assets.compile = false
heroku run bash
セッションで dyno を検査しているときに 50 MB を超える大きなディレクトリサイズを確認できる場合、それはスパースファイルのためである可能性があります。これは、オペレーティングシステムのブロックサイズより小さいファイルのディスク上の見かけのサイズが大きくなるためです。--apparent-size
フラグを指定して du
コマンドを実行することにより、デプロイ間でキャッシュに保存される正しいサイズを取得できます。たとえば、次のようになります。
$ heroku run bash
~ $ du -hd 1 tmp/cache/assets/
386M tmp/cache/assets/sprockets
~ $ du -hd 1 tmp/cache/assets/ --apparent-size
27M tmp/cache/assets/sprockets
この場合、du
の最初の呼び出しでは 386 MB 分のアセットが示されましたが、圧縮されてキャッシュに保存されるファイルの正確なサイズはわずか 27 MB です。
複数のバージョン
デフォルトでは、Rails アセットパイプラインには、アセットの最後に生成された 3 つのバージョンが保持されます。これは、数日または数週間後までレンダリングされない可能性がある、メールなどの場所で使用されるアセットをサポートするためです。
アセットを削除すると、そのアセットの最後の 3 つのバージョンが使用可能なままになります。 sprockets は削除されたアセットを追跡しないため、アセットがいつ削除されたかを認識できません。削除したアセットを除去したい場合は、キャッシュを消去できます。これはビルド間で public/assets
内に生成されたアセットを保持する方法です。キャッシュを消去すると、削除されたアセットだけでなく、古いバージョンのアセットもすべて除去されることに注意してください。
デバッグ出力
本番環境で rake assets:precompile
が実行されると、Rails 4 では tmp ファイルの生成中にデバッグ出力が表示されるようになりました。ただし、そのファイルが /tmp
内に残るわけではありません。これらのファイルは、生成後に適切な場所にコピーされます。
既知の問題
Rails 4 (sprockets) には、検出またはデバッグが困難な既知の問題が 2 つあります。これに役立てるために、本番環境で sprockets better errors gem を使用することをお勧めします。これらのチェックは Rails 4.1+ にマージされていますが、4.0.x バージョンにはこの gem が引き続き必要です。
Rails 4.1+ で sprockets_better_errors
を使用すると、エラーが表示されることがあります。
誤って使用された依存関係
別のファイルで参照されているアセットファイルを変更すると sprockets にエラーが表示されることがあり、その参照は変更されません。これは、sprockets の依存関係宣言がないためです。
たとえば、別のファイルを参照している CSS ファイル app/assets/stylesheets/application.css.erb
があるとします。
.logo {
background-image:url('<%= asset_path("logo.png") %> ');
}
ここでは、application.css.erb
が app/assets/images/logo.png
に依存しています。事前コンパイルが実行されると、両方のアセットが生成されます。logo.png
のクライアントリクエストあたりのサイズが大きくなるよう変更されたらどうなるでしょうか。次回の事前コンパイルの実行時、sprockets は、logo.png
の MD5 フィンガープリントが異なっていることからそれが変更されたことを認識するため、このアセットはコンパイルされます。ただし、application.css.erb
は変更されていないため、再コンパイルされることはありません。つまり、スタイルシートが画像の間違ったコピーを指すようになっています。これを解決するには、application.css.erb
ファイルの先頭に次のアセット宣言を追加します。
//= depend_on_asset "logo.png"
.logo {
background-image:url('<%= asset_path("logo.png") %> ');
}
これで、logo.png
が変更されたら、application.css.erb
の再コンパイルが必要になります。
アセットが事前コンパイルの一覧にない
sprockets での別の一般的な本番環境のエラーは、アセットが事前コンパイルされる必要があると宣言されていないことによって発生します。デフォルトでは、すべての “アプリケーション” アセットと画像が事前コンパイルされます。これには、application.css
と application.js
が含まれます。プロジェクト内の CSS または JS はすべて、これらのファイルにロールインされると見なされるため、プロジェクト内の追加の JS または CSS は事前コンパイルされません。sprockets_better_errors
gem は、アセットが開発環境の事前コンパイルの一覧にあるかどうかを確認します。
デバッグ
Heroku でアセットをコンパイルするには、それをローカルでコンパイルできる必要があります。アセットをデバッグするには、次のコマンドを実行します。
$ RAILS_ENV=production bundle exec rake assets:precompile
これはエラーなしで完了します。このコマンドを実行した後、アセットを Git にチェックインしないでください。
ページロードでエラーが表示されるが、アセットのビルド中でない場合は、heroku run bash
を使用してそれをデバッグできます。
$ heroku run bash
$ ls public/assets
manifest-<digest>.json
または .sprockets-manifest-<digest>.json
(<digest>
は長い英数字の文字列) と、すべてのアセットファイルが表示されます。
$ heroku run rails console
を実行して、Rails が提供しようとしている正確なファイル名を表示することもできます。コンソールで、helper
オブジェクトと asset_path
を使用してフルパスを特定できます。
$ heroku run rails console
> puts helper.asset_path("application.js")
/assets/application-6aae32862efc758cf08c7b7fc0e85e15.js
ハングアップしているアセットビルド
アセットコンパイルがハングアップしていると思われる場合は、vendor/assets
ディレクトリからアセットを削除し、それを app/assets
ディレクトリに配置してみてください。
その他のトラブルシューティング
アセットパイプラインのトラブルシューティングのセクションにある包括的な一覧を参照してください。