私の展開は遅く、少なくとも3分かかります。デプロイ中の遅いCapistranoタスクは、assets:precompileです。これにはおそらく、合計展開時間の99%がかかります。これをどのようにスピードアップできますか?ローカルマシンでアセットをプリコンパイルし、gitリポジトリに追加する必要がありますか?
編集:config.assets.initialize_on_precompile = false
をapplication.rbファイルに追加すると、プリコンパイル時間が30分短縮されましたが、依然として遅いです。
アイデアは、アセットを変更しなければ、毎回それらを再コンパイルする必要がないということです:
これは Ben Curtisが提案する解決策 gitを使用した展開の場合:
namespace :deploy do
namespace :assets do
task :precompile, :roles => :web, :except => { :no_release => true } do
from = source.next_revision(current_revision)
if releases.length <= 1 || capture("cd #{latest_release} && #{source.local.log(from)} vendor/assets/ app/assets/ | wc -l").to_i > 0
run %Q{cd #{latest_release} && #{rake} Rails_ENV=#{Rails_env} #{asset_env} assets:precompile}
else
logger.info "Skipping asset pre-compilation because there were no asset changes"
end
end
end
end
資産の年齢に基づいた別のアプローチ( https://Gist.github.com/2784462 ):
set :max_asset_age, 2 ## Set asset age in minutes to test modified date against.
after "deploy:finalize_update", "deploy:assets:determine_modified_assets", "deploy:assets:conditionally_precompile"
namespace :deploy do
namespace :assets do
desc "Figure out modified assets."
task :determine_modified_assets, :roles => assets_role, :except => { :no_release => true } do
set :updated_assets, capture("find #{latest_release}/app/assets -type d -name .git -Prune -o -mmin -#{max_asset_age} -type f -print", :except => { :no_release => true }).split
end
desc "Remove callback for asset precompiling unless assets were updated in most recent git commit."
task :conditionally_precompile, :roles => assets_role, :except => { :no_release => true } do
if(updated_assets.empty?)
callback = callbacks[:after].find{|c| c.source == "deploy:assets:precompile" }
callbacks[:after].delete(callback)
logger.info("Skipping asset precompiling, no updated assets.")
else
logger.info("#{updated_assets.length} updated assets. Will precompile.")
end
end
end
end
アセットをローカルでプリコンパイルする場合は、次のタスクを使用できます。
namespace :deploy do
namespace :assets do
desc 'Run the precompile task locally and rsync with shared'
task :precompile, :roles => :web, :except => { :no_release => true } do
from = source.next_revision(current_revision)
if releases.length <= 1 || capture("cd #{latest_release} && #{source.local.log(from)} vendor/assets/ app/assets/ | wc -l").to_i > 0
%x{bundle exec rake assets:precompile}
%x{rsync --recursive --times --rsh=ssh --compress --human-readable --progress public/assets #{user}@#{Host}:#{shared_path}}
%x{bundle exec rake assets:clean}
else
logger.info 'Skipping asset pre-compilation because there were no asset changes'
end
end
end
end
別の興味深いアプローチとして、git hookを使用する方法があります。たとえば、このコードを.git/hooks/pre-commit
に追加すると、アセットファイルに違いがあるかどうかをチェックし、最終的にそれらをプリコンパイルして現在のコミットに追加できます。
#!/bin/bash
# source rvm and .rvmrc if present
[ -s "$HOME/.rvm/scripts/rvm" ] && . "$HOME/.rvm/scripts/rvm"
[ -s "$PWD/.rvmrc" ] && . "$PWD/.rvmrc"
# precompile assets if any have been updated
if git diff-index --name-only HEAD | egrep '^app/assets' >/dev/null ; then
echo 'Precompiling assets...'
rake assets:precompile:all Rails_ENV=production Rails_GROUPS=assets
git add public/assets/*
fi
このアプローチを使用することにした場合は、おそらくconfig/environments/development.rb
を追加して変更する必要があります。
config.assets.prefix = '/assets_dev'
そのため、開発中にプリコンパイルされたアセットを提供しません。
turbo-sprockets-Rails と呼ばれる、Rails内でこの問題を解決するgemを作成しました。変更されたファイルを再コンパイルするだけで、すべてのアセットを生成するために一度だけコンパイルすることにより、assets:precompile
を高速化します。アセットディレクトリはリリース間で共有されるため、Capistranoの場合はそのまま使用できます。
これは、git log
を使用するソリューションよりもはるかに安全です。なぜなら、私のパッチは、gemからのものであっても、アセットのソースを分析するからです。たとえば、jquery-Rails
を更新すると、application.js
の変更が検出され、application.js
のみが再コンパイルされます。
また、このパッチをRails 4.0.0、そしておそらくRails 3.2.9にマージしようとしていることに注意してください( https://github.com/ Rails/sprockets-Rails/pull/21 )。しかし、今のところ、 turbo-sprockets-Rails gemのテストを手伝ってくれて、何か問題があればお知らせください。
キャッシュコピーを有効にすると、tommasopのソリューションは機能しません。私の修正版は次のとおりです。
task :precompile, :roles => :web, :except => { :no_release => true } do
from = source.next_revision(current_revision)
if capture("cd #{shared_path}/cached-copy && git diff #{from}.. --stat | grep 'app/assets' | wc -l").to_i > 0
run %Q{cd #{latest_release} && #{rake} Rails_ENV=#{Rubber.env} #{asset_env} assets:precompile:primary}
else
logger.info "Skipping asset pre-compilation because there were no asset changes"
end
end
ローカルシステムで同じ(アセットのプリコンパイル)を実行することで、アセットのプリコンパイルのサーバー作業を節約できます。そして、サーバーに移動します。
from = source.next_revision(current_revision) rescue nil
if from.nil? || capture("cd #{latest_release} && #{source.local.log(from)} vendor/assets/ app/assets/ | wc -l").to_i > 0
ln_assets
run_locally "rake assets:precompile"
run_locally "cd public; tar -zcvf assets.tar.gz assets"
top.upload "public/assets.tar.gz", "#{shared_path}", :via => :scp
run "cd #{shared_path}; tar -zxvf assets.tar.gz"
run_locally "rm public/assets.tar.gz"
else
run "ln -s #{shared_path}/assets #{latest_release}/public/assets"
logger.info "Skipping asset pre-compilation because there were no asset changes"
end
Ben Curtisが提案する解決策 は機能しません。デプロイ時に.gitフォルダーをコピーしないためです(遅くて役に立たない):
set :scm, :git
set :deploy_via, :remote_cache
set :copy_exclude, ['.git']
私は次のスニペットを使用していますが、load 'deploy/assets'
task :assets, :roles => :app do
run <<-EOF
cd #{release_path} &&
rm -rf public/assets &&
mkdir -p #{shared_path}/assets &&
ln -s #{shared_path}/assets public/assets &&
export FROM=`[ -f #{current_path}/REVISION ] && (cat #{current_path}/REVISION | Perl -pe 's/$/../')` &&
export TO=`cat #{release_path}/REVISION` &&
echo ${FROM}${TO} &&
cd #{shared_path}/cached-copy &&
git log ${FROM}${TO} -- app/assets vendor/assets | wc -l | egrep '^0$' ||
(
echo "Recompiling assets" &&
cd #{release_path} &&
source .rvmrc &&
Rails_ENV=production bundle exec rake assets:precompile --trace
)
EOF
end
修正をできるだけ早く展開するときに、アセットのプリコンパイルを強制的にスキップする必要がある場合があります。私は、仕事をするために他の答えを補完するものとして、次のハックを使用します。
callback = callbacks[:after].find{|c| c.source == "deploy:assets:precompile" }
callbacks[:after].delete(callback)
after 'deploy:update_code', 'deploy:assets:precompile' unless fetch(:skip_assets, false)
このスクリプトは、組み込みのアセットプリコンパイルフックを変更するため、skip_assetsパラメーターに基づいて呼び出されます。 cap deploy -S skip_assets=true
を呼び出して、アセットのプリコンパイルを完全にスキップできます。
OPは明示的にCapistranoを要求しましたが、専用の展開ツールを使用せずに展開する場合(bashスクリプト、Ansibleプレイブックなどを使用)、次の手順を使用してRails deploys :
バンドルのインストールをスキップする
_bundle check
_は、インストールするgemがある場合は_1
_を返します(そうでない場合は_1
_)。したがって、必要でない場合はバンドルのインストールを簡単にスキップできます。
アセットのプリコンパイルをスキップする
変更をプルする前に_git rev-parse HEAD
_を使用し、現在のバージョンのSHAを変数に保存します(たとえば_$previous_commit
_)。その後、変更をプルし、アセットにコマンドgit diff --name-only $previous_commit HEAD | grep -E "(app|lib|vendor)/assets"
で変更されます。これにより_$1
_が返される場合、アセットのプリコンパイルを安全にスキップできます(リリースベースのデプロイを使用する場合、アセットを新しいリリースのディレクトリにコピーできます)。
データベース移行をスキップする
MySQLを使用している場合は、アプリケーションのルートディレクトリからコマンド_mysql --user=USER --password=PASSWORD --batch --skip-column-names --execute="USE MYAPP; SELECT version FROM schema_migrations ORDER BY version DESC LIMIT 1;"
_を使用して、最新の適用された移行の名前を取得します。これをコマンド_ls db/migrate | tail -1 | cut -d '_' -f 1
_(利用可能な最新の移行を返す)の出力と比較します。それらが異なる場合は、移行する必要があります。そうでない場合は、データベースの移行をスキップできます。
Ansibleを使用してデプロイするRails開発者は、不要な場合にファクト収集をオフにして(_gather_facts: no
_)、SSHパイプライン(_export ANSIBLE_SSH_PIPELINING=1
_)を使用することで、デプロイ時間をさらに短縮できます。
さらに詳細が必要な場合は、最近このトピックについて 記事 を書きました。