Railsアプリを本番環境にデプロイしたときにpublic配下に置いている画像が本番環境で読み込まれないという現象が起きたときの解決方法にとても苦しんだので備忘録として残しておきます!
目的
デプロイをした時のエラーで苦しんでいる人必見!
Railsアプリのassets配下とpublic配下の違い
(前提、環境)
mac
macOS Mojave 10.14.5
ruby 2.5.1
rails 5.2.3
nginx 1.16.1
unicorn 5.4.1
AWS
はじめに
サーバーはAWSのEC2インスタンス
webサーバーはnginx
アプリケーションサーバーはunicorn
を利用しています。
ローカル環境ではうまく画像が表示されるのですが、本番環境でのみうまく反映されないというエラー。。。
公式
NGINX公式
https://www.nginx.com/Rails Guide
https://guides.rubyonrails.org/asset_pipeline.htmlRails Guide(日本語版)
https://railsguides.jp/asset_pipeline.html
参考記事
assetsとpublicのどちらに画像を置くべきかについては下記の記事がとても参考になるかと思います!
【Ruby on Rails】画像は public と app/assets/images のどちらに設置すべき?
https://techblog.kyamanak.com/entry/2017/10/13/003818
はじめに
画像を用意しておくとき、assetsとpublicのどちらに記載すべきかという論争に関しては個人的な見解を述べるとpublic配下で良いかなと思っています。
理由はassetsパイプラインでフィンガープリントをつける意味があまり感じないからです!
画像のデータはバイナリーファイルという機械が読むためのファイル群です。(画素が一つ一つ機械が扱うために変換しています。
容量が多いほど、画素数が多いということで、変換しているバイナリーデータも多くなります。)
そのため、結局のところ一つにまとめきれないため、画像一つ一つにフィンガープリントをつけて管理しなければいけないのですが、publicに入れておけばassetsパイプラインの変換対象にならないので、その処理が省けるよね。というスタンスです。
※assets配下に入れておくとアセットファイルの中身が少しでも変更された場合、フィンガープリントが自動で更新され、ブラウザでキャッシュされていた既存の画像呼び出しが無効になるのでキャッシュの削減になるということですが、画像の差し替えをするシーンはそんなに発生しない前提です。
最初の背景など変更される必要がないものはpublicで良いと思います。
assets配下とpublic配下での画像呼び出しのパスについて
基本的な違いはimage_tagメソッドの引数で渡すときに/をつけるかどうかの違いです。
assets配下
<%= image_tag('super_engineer.png') %>
# 生成されるHTML
# <img src="/assets/super_engineer-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.png" alt="SuperEngineer" />
-xxxxxxxxxxxxxxxxxというのがフィンガープリントです。
一意性を持たせているわけですね。
public配下
<%= image_tag('/super_engineer.png') %>
# 生成されるHTML
# <img src="/super_engineer" alt="SuperEngineer" />
今回のエラーの結論
現象としてはnginxの設定の記述に誤りがありました。
質問者は同じEC2インスタンス上で複数のアプリを作っていたようで、その記述が残っていたためでした。
また、新しいgemを入れていたためか,
yarnが導入されていなかったなど複数の要因があり苦しんだ感じです。
仮説1 本番環境でのみエラーがおきているということは本番環境の設定に問題がある?
デプロイを走らせたときにYarnがないよと言われる。
assets:precompileの時に、ログにこんな記載が・・・
00:08 deploy:assets:precompile
01 $HOME/.rbenv/bin/rbenv exec bundle exec rake assets:precompile
01 Yarn executable was not detected in the system.
01 Download Yarn at https://yarnpkg.com/en/docs/install
本番環境にyarnというパッケージマネージャーがないとのこと。
Yarnのインストール
yarn公式インストールページ
https://classic.yarnpkg.com/ja/docs/install/#alternatives-stable
上記からAWSのOSはLinuxなので代替手段でインストールすることに
curl -o- -L https://yarnpkg.com/install.sh | bash
その後、export PATH="$PATH:/opt/yarn-[version]/bin"を入れてくだいねという案内があるので
下記でパスの登録をします。
echo export PATH="$PATH:/opt/yarn-[version]/bin" >> ~/.bash_profile
source~/.bash_profile
yarn -v
=>1.22.4
yarnのインストール完了です。
仮説2 本番環境でのみエラーがおきているということは本番環境の設定に問題がある?Yarnがパッケージを探せないというエラーが続く。
起きている現象の整理
パッケージがないということはnginxがファイルを読み込んでいない。という仮説が考えられ、設定ファイルを見直すことに。
# nginxを導入すると、下記のファイルが生成されており、設定を記入していく。
sudo vim /etc/nginx/conf.d/rails.conf
rootの記述が間違い
質問者のrootが別のアプリの名前になっていました。
また、読み込むassetsの設定も同じく別のアプリ名になっていた
server {
〜〜〜省略〜〜〜
server_name xxxxxxxxxxxxxxxxxx;
root /var/www/<別のアプリ名>/current/public;
# assetsファイル(CSSやJavaScriptのファイルなど)にアクセスが来た際に適用される設定
location ^~ /assets/ {
gzip_static on;
expires max;
add_header Cache-Control public;
root /var/www/<別のアプリ名>/current/public;
}
〜〜〜省略〜〜〜
}
結論
yarnというパッケージマネージャーがないという環境によるものと、
nginxはwebサーバーとして静的なファイルを置いておくものですが、nginxが別のアプリのフォルダを読み込もうとしていたために起きていたエラーでした。
紐解いていくのにとても時間がかかりましたが、それぞれがどのような意図で使われているのかを抑えていけば理由がわかりますね。
改めて自分の理解の復習にもなったので、良かったです笑