Ускорение активов: предварительная компиляция с использованием Rails 3.1 / 3.2 Capistrano deployment
мои развертывания медленные, они занимают не менее 3 минут. Медленный задач Капистрано в ходе развертывания активы:предварительной компиляции. Это занимает, вероятно, 99% от общего времени развертывания. Как я могу ускорить это? Должен ли я предварительно скомпилировать свои активы на локальном компьютере и добавить их в свое РЕПО git?
Правка: Добавление config.assets.initialize_on_precompile = false
Мои приложения.RB-файл сбросил время предварительной компиляции с полминуты, но он все еще медленный.
7 ответов:
идея заключается в том, что если вы не измените своих активов вам не нужно перекомпилировать каждый раз:
это решение, которое предлагает Бен Кертис для развертывания с 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-rails3. Это ускоряет ваш
assets:precompile
только перекомпиляция измененных файлов и только компиляция один раз для создания всех активов. Он работает из коробки для Capistrano, так как ваш каталог активов является общим между выпусками.Это гораздо более надежные, чем решения, использующие
git log
, так как мой патч анализирует источники ваших активов, даже если они исходят из камня. Для например, если вы обновляетеjquery-rails
изменение будет обнаруженоapplication.js
, и толькоapplication.js
будут перекомпилированы.обратите внимание, что я также пытаюсь объединить этот патч в Rails 4.0.0 и, возможно, Rails 3.2.9 (см. https://github.com/rails/sprockets-rails/pull/21 но сейчас, было бы здорово, если бы вы могли помочь мне проверить turbo-sprockets-rails3 камень, и дайте мне знать, если у вас есть какие-либо проблемы.
решение 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
The решение, которое предлагает Бен Кертис не работает для меня, потому что я не копировать .папку git при развертывании (а) :
set :scm, :git set :deploy_via, :remote_cache set :copy_exclude, ['.git']
Я использую следующий фрагмент, 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
бывают случаи, когда мне нужно заставить пропустить предварительную компиляцию активов при развертывании исправления как можно скорее. Я использую следующий хак в качестве дополнения к другим ответам, чтобы сделать эту работу.
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)
этот скрипт изменит встроенное подключение asset-precompile, поэтому он будет вызван на основе skip_assets
OP явно запросил Capistrano, но в случае, если вы развертываете без специального инструмента развертывания (через скрипт bash, Ansible playbook или аналогичный), вы можете использовать следующие шаги для ускорения развертывания Rails:
пропустить установку пакета
bundle check
возвращает1
если есть драгоценные камни для установки (1
в противном случае), поэтому легко пропустить установку пакета, если это не необходимо.пропустить предварительная компиляция активов
Используйтеgit rev-parse HEAD
перед вытягиванием изменений и сохранением SHA текущей версии в переменной (скажем$previous_commit
). Затем потяните изменения и узнайте, изменились ли активы с помощью командыgit diff --name-only $previous_commit HEAD | grep -E "(app|lib|vendor)/assets"
. Если это возвращаетвы можете безопасно пропустить предварительную компиляцию активов (если вы используете развертывание на основе выпуска, вы можете скопировать свои активы в каталог нового выпуска).
пропустить миграцию базы данных
Если вы используете 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
(который возвращает последнюю доступную миграцию). Если они отличаются, вам нужно мигрировать. Если нет, можно пропустить миграцию базы данных.разработчики Rails, развертывающие с Ansible, могут еще больше сократить время развертывания, отключив сбор фактов, если это не требуется (
gather_facts: no
) и использовать конвейер SSH (export ANSIBLE_SSH_PIPELINING=1
).если вы хотите более подробно, я недавно писал статьи об этой теме.