LoginSignup
3
2

More than 3 years have passed since last update.

capistranoで構築した環境でassetsの中身を1から作り直したい

Last updated at Posted at 2020-06-02

なぜそんなことをしたくなったのか

capistrano-railsのassets:precompileが走っている時にEC2がメモリ不足に陥り、インスタンスを再起動させた結果、deployが中途半端なところで終わってしまいました。
もし中途半端なコンパイル結果をもとにWebサーバーが動いて実行時エラーなんて起きたときには目も当てられないので、なんとかまっさらな状態に戻そうとしてあれこれしました。

バージョン情報

rails (5.2.4.2)
capistrano (3.11.0)
capistrano-rails (1.6.1)
sprockets (3.7.2)
sprockets-rails (3.2.1)

前提条件

current_pathはこのパスとする

  • /var/www/app_name/current

shared_pathはこのパスとする

  • /var/www/app_name/shared

先に結論

結論から言うと、やるべきことはこれです。

  1. /var/www/app_name/shared/public/assets の中身を消す
  2. /var/www/app_name/shared/tmp/cache/assets の中身を消す
  3. いつもどおりデプロイ

注意点があります。
capistranoにはdeploy:clobber_assetsのようなassetsをいかにも消しそうなコマンドが標準で用意されていますが、
これを使わずちゃんと手で消すということです。理由は後述します。

説明

capistranoのディレクトリ構成

まず、capistranoによってデプロイされたサーバーの/var/www/app_nameの中は、下記のような構造になっています。

├── current (releases内の最新のディレクトリにシンボリックリンクが貼られてる)
├── releases
│   ├── 20200601101105
│   ├── 20200601102714
│   └── 20200601105159
├── repo
├── revisions.log
└── shared

このcurrentの下、つまり、releases/20200601105159の下には、あなたのプロジェクトのリポジトリにあるような構成になっているでしょう。
そこにはapp/があり、config/があり、public/があるはずです。

このpublic/の下にはビルドされた静的ファイルが詰め込まれたassets/が入っている……ように見せかけて、
/var/www/app_name/current/public/assets/var/www/app_name/shared/assetsへのシンボリックリンクになっています。これはrails対応の公式gemであるcapistrano-rails側でlinked_dirsの設定がされるためです。

そしてちゃんと設定をしていたのであれば、/var/www/app_name/current/tmp/cacheは同様に/var/www/app_name/shared/tmp/cacheへのシンボリックリンクになっているはずです。 capistrano-railsのREADMEに書かれている通りに設定していた場合はそうなっているはずです。

config/deploy.rb
append :linked_dirs, 'log', 'tmp/pids', 'tmp/cache' ...以下省略

assets:precompileのキャッシュの仕組み

Railsガイド アセットのキャッシュストア

デフォルトのSprocketsは、development環境とproduction環境でtmp/cache/assetsにアセットをキャッシュします。

こう書かれている通り、tmp/cache/assetsにコンパイル結果のキャッシュがあるので、これを消します。
capistranoだと/var/www/app_name/shared/tmp/cache/assetsがこれにあたるので消します。

Sprocketsのデプロイ結果のキャッシュの仕組み

assets:precompileのキャッシュとは別に、Sprocketsはデプロイ結果のファイルも取っておいてあります。
Sprocketsのコードにこんなのが書かれています。

module Sprockets
  module Exporters
    # Writes a an asset file to disk
    class FileExporter < Exporters::Base
      def skip?(logger)
        if ::File.exist?(target)
          logger.debug "Skipping #{ target }, already exists"
          true
        else
          logger.info "Writing #{ target }"
          false
        end
      end

      def call
        write(target) do |file|
          file.write(asset.source)
        end
      end
    end
  end
end

deploy:assets:precompileしているときに、標準出力でログがずらっと出てきているのを目にしていませんか? アレを実現しているのがおそらくは上のコードです。
logger.info "Writingのところを見てください。

こんな感じ:

      01 I, [2020-06-01T08:56:09.691790 #10361]  INFO -- : Writing /var/www/app_name/releases/20200601085319/public/assets/develop/application-232387090aed00e6b038…
      01 I, [2020-06-01T08:56:09.692557 #10361]  INFO -- : Writing /var/www/app_name/releases/20200601085319/public/assets/develop/application-232387090aed00e6b038…
      01 I, [2020-06-01T08:56:17.418572 #10361]  INFO -- : Writing /var/www/app_name/releases/20200601085319/public/assets/lb-0002/application-d1bedc9937772b59211a…
      01 I, [2020-06-01T08:56:17.418856 #10361]  INFO -- : Writing /var/www/app_name/releases/20200601085319/public/assets/lb-0002/application-d1bedc9937772b59211a…
      01 I, [2020-06-01T08:56:21.414096 #10361]  INFO -- : Writing /var/www/app_name/releases/20200601085319/public/assets/lb-0003/application-5a60e0a26af1e40a9188…
      01 I, [2020-06-01T08:56:21.414368 #10361]  INFO -- : Writing /var/www/app_name/releases/20200601085319/public/assets/lb-0003/application-5a60e0a26af1e40a9188…

このskip?の判定に引っかかった場合。
つまり、この例だと/var/www/app_name/releases/20200601085319/public/assets/develop/application-232387090aed00e6b038…のdigest込みのファイルが既に存在していた場合、
Sprocketsはファイルを書き出しません。

なので、capistranoだと/var/www/app_name/shared/assetsに同じファイルがあった場合、このskip?でスキップされてしまうということです。
消しておく必要がありますね。

おや? 便利なコマンドがありそうだが……?

capistranoはcap -Tで全てのコマンドを表示してくれます。
assets関係だとこんなコマンドが見つかります。

cap deploy:cleanup_assets          # Cleanup expired assets
cap deploy:clobber_assets          # Clobber assets

標準でdeploy:clobber_assetsというコマンドが用意されているではありませんか!
clobberとはぶん殴るという意味です。強制的に新しいassetsにしてくれそうだ! こりゃいいや! と思って使うと、実は思ったような挙動をしてくれません。

$ bundle exec cap review deploy:clobber_assets

00:00 deploy:clobber_assets
      01 bundle exec rake assets:clobber
      01 I, [2020-06-01T11:22:46.062204 #6756]  INFO -- : Removed /var/www/app_name/releases/20200601094959/public/assets
      01 Removed webpack output path directory /var/www/app_name/releases/20200601094959/public/packs

いかにもassetsをディレクトリごと消してくれていそうな雰囲気ですが、よく見るとRemoveしているのはただのシンボリックリンクで、肝心の中身は消えていません。マジか。
そして、そもそもコンパイル時のキャッシュであるtmp/cacheの方は消してくれていません。

このことについてはissueも上がっているようです。

なので、「結論」に書いた通り、手動でコマンドを実行して消すか、自分でコマンドを定義してやるのが良さそうです。

3
2
0

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
3
2