자산 속도 향상 : Rails 3.1 / 3.2 Capistrano 배포로 사전 컴파일
내 배포가 느리고 3 분 이상 걸립니다. 배포 중 느린 Capistrano 작업은 assets : precompile입니다. 총 배포 시간의 99 %가 소요됩니다. 속도를 높이려면 어떻게해야합니까? 내 로컬 컴퓨터에서 내 자산을 미리 컴파일하고 내 git repo에 추가해야합니까?
편집 : 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'
따라서 개발 중에는 미리 컴파일 된 자산을 제공하지 않습니다.
Rails 내부에서이 문제를 해결하기 위해 turbo-sprockets-rails3 라는 gem을 작성했습니다 . assets:precompile
변경된 파일 만 다시 컴파일하고 한 번만 컴파일하여 모든 자산을 생성함으로써 속도를 높 입니다. 자산 디렉토리가 릴리스간에 공유되기 때문에 Capistrano에서 즉시 작동합니다.
This is much more bulletproof than the solutions that use git log
, since my patch analyzes the sources of your assets, even if they come from a gem. For example, if you update jquery-rails
, a change will be detected for application.js
, and only application.js
will be recompiled.
Note that I'm also trying to get this patch merged into Rails 4.0.0, and possibly Rails 3.2.9 (see https://github.com/rails/sprockets-rails/pull/21). But for now, it would be awesome if you could help me test out the turbo-sprockets-rails3 gem, and let me know if you have any problems.
tommasop's solution doesn't work when enabled cached-copy, my modified version:
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
You can save your server effort for pre-compiling assets by doing the same (pre-compiling assets) on your local system. And just moving to server.
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
The solution that Ben Curtis propose does not work for me, because I do not copy the .git folder when deploying (slow and useless) :
set :scm, :git
set :deploy_via, :remote_cache
set :copy_exclude, ['.git']
I'm using the following snippet, whitout 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
There are times when I need to force skip asset precompile when deploying a fix asap. I use the following hack as a complement to other answers to do the job.
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)
This script will change the built-in asset-precompile hooking, so it will be called based on the skip_assets parameter. I can call cap deploy -S skip_assets=true
to skip asset precompile completely.
The OP explicitly asked for Capistrano, but in case you are deploying without a dedicated deploy tool (via bash script, Ansible playbook or similar), you may use the following steps to speed up your Rails deploys:
Skip bundle installation
bundle check
returns1
if there are gems to install (1
otherwise) so it's easy to skip bundle installation if not necessary.Skip asset precompilation
Usegit rev-parse HEAD
before pulling changes and store the current version's SHA in a variable (say$previous_commit
). Then pull changes and find out if assets have changed with the commandgit diff --name-only $previous_commit HEAD | grep -E "(app|lib|vendor)/assets"
. If this returns$1
you can safely skip asset precompilation (if you use release-based deploys you may want to copy your assets to your new release's directory).Skip database migrations
If you are using MySQL, use the commandmysql --user=USER --password=PASSWORD --batch --skip-column-names --execute="USE MYAPP; SELECT version FROM schema_migrations ORDER BY version DESC LIMIT 1;"
from your applcation's root directory to get the name of the latest applied migration. Compare this to the output of the commandls db/migrate | tail -1 | cut -d '_' -f 1
(which returns the latest available migration). If they differ, you need to migrate. If not, you can skip database migrations.
Rails developers deploying with Ansible can further reduce their deploy time by turning facts gathering off if not needed (gather_facts: no
) and use SSH pipelining (export ANSIBLE_SSH_PIPELINING=1
).
If you want more detail, I recently wrote an article about this topic.
'code' 카테고리의 다른 글
Enum의 초기 값 (0) | 2020.11.30 |
---|---|
경로 대신 URL에 파일이 있는지 확인하십시오. (0) | 2020.11.30 |
사전의 키 값으로 사전의 NSArray 정렬 (0) | 2020.11.30 |
주어진 URL은 애플리케이션 구성에서 허용되지 않습니다. (0) | 2020.11.30 |
OrderedDict를 사용하지 않는 이유가 있습니까? (0) | 2020.11.30 |