2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Herokuデプロイ奮闘記2(後編)

Last updated at Posted at 2021-09-30

前編より、Gemsass-railsのバージョンを5系列にすると

[BUG] Segmentation fault at 0x0000000000000000
ruby 2.6.8p205 (2021-07-07 revision 67951) [x86_64-linux]

が発生しないことが分かったので、無理ゲーは度外視します。

今回の話の流れ

〜プロローグ〜 す べ て の は じ ま り
第1話 “[BUG] Segmentation fault at 0x0000000000000000” を除去
第2話 “[BUG] Segmentation fault at 0x0000000000000000” を回避
第3話 “NoMethodError: undefined method `[]' for nil:NilClass” を解決してデプロイ
第4話 Gemsass-railsを6系列にインストールし直して再度デプロイ
〜エピローグ〜 今回学んだこと

※ 後編は第3話から

すべてを解決に導く結論

image-urlヘルパーはSassによるものなので、これを使用しているスタイルシートは、CSSではなくSCSSファイルでなければならない。これだけだった...ホントに...orz

前提条件

  • OS**:**macOS Catalina 10.15.7
  • Git**:**2.28.0
  • Heroku**:**7.59.0 darwin-x64 node-v12.21.0
  • Ruby**:**2.6.8p205 (2021-07-07 revision 67951) [x86_64-darwin19]
  • Rails**:**6.0.4.1
  • Bundler**:**1.17.2
  • Google Chrome.app**:**93.0.4577.63(Official Build)(x86_64)

デプロイ成功までの全行程
①. *config/application.rb*に`config.assets.initialize_on_precompile = false`を追記する。 →失敗:droplet: ②. *config/initializers/assets.rb*に以下を追記する。
Rails.application.config.assets.configure do |env|
  env.export_concurrent = false
end

→失敗:droplet:
③. ②の状態から、①で追記したコードを削除する。
→失敗:droplet:
④. ②で追記したコードと同じ処理をすると思われる、ワンライナー
Rails.application.config.assets.export_concurrent = false
若しくは
Sprockets.export_concurrent = false
に書き換える。
→いずれも失敗:droplet:
⑤. ④で書き換えたコードを削除して、Gemsprocketsを3系列にして再インストール。
→失敗:droplet:
⑥. ⑤のsprockets含め、関連してそうなGemを全てバージョン指定して再インストール。
→失敗:droplet: “[BUG] Segmentation fault at 0x0000000000000000” の代わりに、“Ruby Sass has reached end-of-life and should no longer be used.” のメッセージと “NoMethodError: undefined method `[]' for nil:NilClass” のエラーが出現
⑦. Gemsass-railsだけを6系列に戻して再インストール。
→失敗:droplet: “[BUG] Segmentation fault at 0x0000000000000000” が再び発生
⑧. RUNTIME依存性を考慮しつつ、関連してそうなGemのバージョンを全て固定して再インストール。
→失敗:droplet:
⑨. ⑧の状態で、①と②を繰り返す。
→いずれも失敗:droplet: “NoMethodError: undefined method `export_concurrent=' for #<Sprockets::Environment:0x00007fd14362c188>” のエラーが発生
⑩. ⑧でバージョン固定したGemsprocketsを削除する。
→失敗:droplet:
⑪. config/initializers/assets.rbに追記した②のコードを削除して、代わりにconfig/application.rbに追記する。
→失敗:droplet:
⑫. ⑨で追記した①のコードを削除して、②のコードをconfig/application.rbの色んな場所に記入してみる。
→全部失敗:droplet: “NoMethodError: undefined method `config' for nil:NilClass” のエラーが発生することも
⑬. ⑫で追記した②のコードを削除して、⑧でバージョン固定したsass-rails以外のGemを全て削除し、sass-railsは5系列にして再インストール。
→失敗:droplet:⑥と同じ結果
⑭. application.cssの拡張子を、‘.scss’、若しくは‘.css.scss’に変更する。
→いずれも失敗:droplet:
⑮. ⑭のファイルの拡張子を元の‘.css’に戻して、①を繰り返す。
→失敗:droplet:
⑯. ⑮で追記した①のコードを削除して、*app/assets/stylesheets/*以下のアセットファイルの「background-image」プロパティの値にimage-urlヘルパーを使用している部分を、引数はそのままに全てurlに変更する。
→失敗:droplet:ローカルで背景画像が表示されなくなった
⑰. ⑯の状態から、‘ url('画像ファイル名') ’を‘ url('/assets/画像ファイル名') ’に変更する。
→デプロイは成功。しかし、ローカル環境では背景画像が表示されるようになったが、Herokuリモート上では未だに反映されてないままである
⑱. config/environments/production.rbの、config.public_file_server.enabledの値を‘ true ’に変更する。
→デプロイは成功。⑰と同じ結果
⑲. config.public_file_server.enabledの値を元の‘ ENV['RAILS_SERVE_STATIC_FILES'].present? ’に戻して、「background-image」プロパティの値を‘ url('/assets/画像ファイル名') ’から‘ image-url('画像ファイル名') ’に変更し、更にこのプロパティを使用しているアセットファイルの拡張子を全て‘.scss’に変更する。
→成功 :tada: リモート環境でも背景画像が表示されるようになった
⑳. Gemsass-railsを6系列に戻して再インストール。
→成功 :tada:

※ 後編は⑬から


第3話 “NoMethodError: undefined method `[]' for nil:NilClass” を解決してデプロイ

rake aborted!
NoMethodError: undefined method `[]' for nil:NilClass

今回からこのエラーの解決に注力する。「寿命を迎えたRuby Sassは使うな」というメッセージはスルー。
これの対処法には、主に以下のものが考えられる。

application.cssの拡張子を‘.scss’、若しくは‘.css.scss’に変更する

こちらこちらのQiita記事にあるように、拡張子を変更するだけで解決する事があるようだ。しかし、自分の環境だとどっちも変化しなかった。

Heroku上にmaster.key用の環境変数を設定する

結構あるあるっぽいのだが、最初のデプロイ前に設定済みなのでこれは関係なし。設定方法は、こちらのQiita記事等を参考にする。

config/environments/production.rbconfig.assets.compileの値を‘true’に変更する

実際にこれがどんな処理をしているのかが不明瞭なので、今回はスルーした。こちらのRailsガイドによると、Sprockets関係の設定を切り替えるものらしいが、正直よく分からない。

config/application.rbconfig.assets.initialize_on_precompile = falseを追記する

前編でも出てきた、アセットパイプラインを正常化するためのコード。今回のこのタイミングでも試してみたが、やはり意味がなかった。

不要なアセットファイルを削除する

今回は特に無いので省略...と思いきや、プリコンパイル用のコマンドにassets:cleanのオプションを追加すると、古いバージョンのアセットファイルがある場合、それらを同時に削除してくれるようだ。

terminal.app
開発環境内にエラーが無いかを確認
% RAILS_ENV=development bin/rails assets:precompile assets:clean

本番環境内にエラーが無いかを確認
% RAILS_ENV=production bin/rails assets:precompile assets:clean

試しにやってみたところ、幾つかのファイルが削除されたログを確認できたので、確かに効果はあるっぽい。使い得なので、次からはこのオプションも付けて実行することにする。

他にも調べたら色々あるかもしれないが、次の対処法でかなり進展した!

CSSやSCSSファイル内の「background-image」プロパティの値にimage-urlヘルパーを使っている箇所を全てurlに変更する

どういう事かと言うと

- background-image: image-url('ファイル名');
+ background-image: url('ファイル名');

とするだけである。スタイルシートの中身を編集したので、プリコンパイルする前にrails sでローカル上の背景画像がきちんと表示されるかを確認した。

今回のようにrails sしたタイミングだけでなく、プリコンパイルのコマンドを実行した時にもだが、稀に

Your Yarn packages are out of date!
Please run yarn install --check-files to update.

という2行のエラーが発生する事がある。これは、package.jsonにあるパッケージコンテンツのバージョンとハッシュ値が、yarn.lockに記載されているものと一致しない場合に起こるらしい。これが発生した時は、メッセージ通りに素直にyarn install --check-filesを実行するだけで良さそう。もしこれでも解決しない場合は、ググって調べてみる。

すると、画像が反映されなくなった!これはどうやら、app/assets/imagesディレクトリに設置してある画像に対してurl(‘ファイル名’)とすると、*/assets/‘コントローラ名’/‘ファイル名’*というパスが参照されるのが原因のようだ1

そこで、正しく画像が反映されていると、*/assets/‘ファイル名’*というパスで参照されることが分かったので、引数に直接パスを指定して

- background-image: url('ファイル名');
+ background-image: url('/assets/ファイル名');

としてみた。開発環境では画像が反映されるように戻った。表示を確認できたので、ここでプリコンパイルのコマンドを試す。すると、ローカル・リモート共に

terminal.app
Everything's up-to-date. Nothing to do

のメッセージが!!!!遂にターミナルからエラーのログが消え失せた!ついでにデプロイもやってみる。

terminal.app
remote: Verifying deploy... done.

はい、デプロイ成功!:tada: しかし、喜ぶのはまだ早い。今度はHerokuリモート上でアプリを開くと、またしても背景画像が適用されてない状態になっていた..:droplet:もう少し、何か手を加える必要があるようだ。

第4話 Gemsass-railsを6系列にインストールし直して再度デプロイ

app/assets/以下のファイルを本番環境でプリコンパイルすると、フィンガープリントと呼ばれるハッシュ2がファイル名の末尾に付く。これは、アセットファイルを更新した時にブラウザのキャッシュによって更新前のファイルの内容が表示されるのを防ぐためのものである。詳しくはこちらのかたの記事等を参考にするとして、Rails 3以前はapp/assets/imagesディレクトリに設置された画像がプリコンパイルされると、通常の画像名のファイルとその画像名にフィンガープリントが付いたファイルの2種類が生成されるのに対して、Rails 4以降になると、フィンガープリントが付いたファイルの1種類のみしか生成されなくなった。そのため、単にurl(‘/assets/ファイル名’)と指定しただけじゃ参照することができず、Herokuリモート上で画像が反映されなくなる。

しかし問題は、本番環境でその生成されたファイルが管理される『場所』で、何故か*public/assets/*以下に配置される。「え゛?*app/assets/*以下のファイルの筈なのにパブリックでぃれくとりぃ!?」と思ったが、先程の記事こちらのかたの記事によると、どうもそうらしい。

プリコンパイルされたファイルは、Webサーバによって静的なアセットとして扱われます。
以下の設定を変更して、Webサーバから静的ファイルを読み込めるようにする必要があります。

とあるので、素直に変更してみる。

config/environments/production.rb
(省略)

# Disable serving static files from the `/public` folder by default since
# Apache or NGINX already handles this.
- config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present?
+ config.public_file_server.enabled = true    ←‘true’に変更

(省略)

結果は変わらず...デプロイは出来るが、やはり背景画像だけが適用されない。どういう事なんだと思いつつ、次に、上の2行のコメント文が気になったので翻訳してみた。「ApacheやNGINXは既にこれに対応しているから、デフォルトで*/pubilc*フォルダからの静的ファイルの読み込みを無効にしているよ」とのこと。Apache(アパッチ)やNGINX(エンジンエックス)とはWebサーバーのことである。また、こちらのRailsガイドでこの設定config.public_file_server.enabledについて説明している部分も翻訳してみた。要するに、「WEBrickを使用している(オススメしないけどね)なら‘true’にしなきゃだけど、それ以外ならそのままの設定でおk」みたいなことを言っている。どうやらこれは、デプロイ成功時に出てくる警告メッセージ

terminal.app
remote: ###### WARNING:
remote: 
remote:        No Procfile detected, using the default web server.
remote:        We recommend explicitly declaring how to boot your server process via a Procfile.
remote:        https://devcenter.heroku.com/articles/ruby-default-web-server

と関係しているようだ。よく分かんないけど、「デフォルトのWebサーバーを使っているから、Procfileが検出されなかった」らしい。このProcfileというのが何なのかはさておき、リンク先のページを開いてみると、これが無かったら、Rubyの標準ライブラリでRailsのデフォルトWebサーバーである『WEBrick』が採用されるが、これは本番環境では十分なパフォーマンスが発揮されないから別途本番環境用のサーバーを用意したほうが良いと説明されている。このメッセージが出るということは、多分サーバーはWEBrickになっているだろうから、書いてある通り別のWebサーバーソフトをインストールして使えるようにすれば良いのだろうか?いや何かおかしい。そもそもこれは警告でそのままでもデプロイは出来るんだから、ここまで設定する必要はない筈である。じゃあ何故、上のproduction.rbの設定変更でも画像が表示されなかったのか?
このコメント文の「`/public` folder」とは、元からそこに置かれていたファイル群を指して言っているのか?*app/assets/以下とpublic/*以下で、静的なアセットファイルとして読み込めるようになるのは後者のファイル群だけなんじゃないのか!?前者はフィンガープリントが付くから対象外になるんじゃないの!?

う〜む...わからん!(笑)

今はこの仮説が正しいとして、上の警告メッセージは取り敢えずスキップしよう...開発を進めていく段階で、*public/*以下に画像ファイル等を置くような実装をするようになってから検討することにする。Herokuさんは『Puma』を推奨しているみたいだから、以下のページなんかを参考にして使えるようにしてみるか。Gemfileには既に記載済みでインストールはされてるっぽいから、その後から設定していく。


...何はともあれ、これまでに色んな記事を読んできたから、実はもう答えは出ているようなもんだ(笑)。[結論](https://qiita.com/tsutsumin_pro/items/6b0816a18dc13665b549#すべてを解決に導く結論)に書いてあることが全て。**`image-url`ヘルパーを使えば、フィンガープリント付きのファイルでも参照してくれる**。但し、この**`image-url`ヘルパーはSass専用なので、CSSファイルでこのヘルパーを使うのならSCSSに変換する必要がある**。自分は最初、このヘルパーをCSSファイルのまま使っていたので、デプロイ時にエラーが発生した。早速ファイルを修正する。
config/environments/production.rb
(省略)

- config.public_file_server.enabled = true
+ config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present?  ←最初に戻す

(省略)
該当ファイルの拡張子を.cssから.scssに変更
- background-image: url('/assets/ファイル名');
+ background-image: image-url('ファイル名');    ←最初に戻す

これで、無事にデプロイが成功し、本番環境でも画像がきちんと表示されるようになった:tada:~~だいぶ回りくどくなったが、~~これが最大の原因であり、全ての元凶だったのだ!

めでたしめでたし...:tea:

と締めたいどころだったが、ここで(良い意味で)嫌な予感がした。「これ今ならsass-railsのバージョン戻してもバグ出ないでデプロイ出来るんじゃね!?」と。確かにこのまま寿命を迎えたGemを抱えながら開発を進めるのもなんか取り憑かれそうで気持ち悪いので

Gemfile
- gem 'sass-rails', '~> 5'
+ gem 'sass-rails', '~> 6'

試しにやってみた。

terminal.app
remote: Verifying deploy... done.

できた。(笑)
これで、全てが解決した!(わけじゃないけどw)

[BUG] Segmentation fault at 0x0000000000000000

NoMethodError: undefined method `[]' for nil:NilClass

の2つのエラーは、自分の現環境では同じ内容のものだった。ただ、Gemsass-railsのバージョンによって出力の仕方が異なるだけだった(本当はsprocketsとかの他のGemも関係しているのかもしれないけど)。今回は途中で下のNoMethodErrorで検索をかけて調べまくったから何とかかんとか正解まで辿り着けたけど、もし上のセグフォバグのエラーで固執してしまってずっとデプロイできないままだったらと思うと震える...

####【別解?(未検証)】

*public/assets/*以下に最初から画像を直接置くようにする

今回はヘルパーの該当変更箇所が自分の環境でたったの3つしかなかったので楽に修正できて良かったが、数が多くなると大変である。開発者がWebサイトを構成するために用意する画像ファイルも、予め*public/assets/*以下に置いとけばそのままurl('/assets/ファイル名')で参照できるようになる。但し、『Capistrano』を使用している環境では使えない。

フィンガープリントが付かないようにする
config.assets.digest = false

フィンガープリントは便利な機能だけど、画像ファイルに対してはあまり向いてないのかもしれない。この設定でファイル名の後ろにハッシュが付かなくなるのでそのままurl('/assets/ファイル名')で参照できるようになる。しかし、自分の環境(というかRails 6)には、上のconfig.assets.digestが記述されている箇所が見当たらない。直接config/environments/production.rbに記入すれば良いのだろうか?

〜エピローグ〜 今回学んだこと

  • 「Precompiling assets failed.」のエラーに引っ掛かってしまったら、まずはRAILS_ENV=production bin/rails assets:precompileでプリコンパイルだけを試してみる。これにより、git pushすることなくエラーの箇所を特定する事ができる。また、このコマンドにオプションassets:cleanを付与すると、古いバージョンのアセットファイルがあれば同時に削除してくれる
  • ネット上の記事を読み漁って、コードの編集等を一通り実行してみてもエラーが解決しない場合は、関連するGemのバージョンを変更してbundle installし直せば何か変わるかもしれない
  • それでも解決しない場合は、オブジェクトやメソッド単位でコードを疑ってみる

今回はデプロイをしながら何回も試行錯誤したので、git logがかなり汚れてしまった...英語の文献も徐々に読めるようになってきてるんで、成長できてると思えればいいな〜

参考記事

関連投稿記事

  1. 後に検証して分かった事だが、この時点でGemのsass-railsのバージョンが6系列だった場合、上の記法でも画像がきちんと表示されるようだ。何らかの仕様が変更になったと思われる。そのためこれは、少なくともバージョン5系列のsass-railsで起こる現象である。

  2. 別名:MD5digest

2
2
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?