Ускорьте ресурсы: прекомпилируйте с использованием Rails 3.1 / 3.2 Capistrano - PullRequest
61 голосов
/ 26 января 2012

Мои развертывания медленные, они занимают не менее 3 минут.Медленная задача Capistrano во время развертывания - это: assets: precompile.Это занимает, вероятно, 99% от общего времени развертывания.Как я могу ускорить это?Должен ли я предварительно скомпилировать свои ресурсы на локальном компьютере и добавить их в мое git-репо?

Редактировать: Добавление config.assets.initialize_on_precompile = false в мой файл application.rb сократило время предварительной компиляции на полминуты, но оно все еще медленно.

Ответы [ 7 ]

84 голосов
/ 26 января 2012

Идея состоит в том, что если вы не меняете свои активы, вам не нужно каждый раз перекомпилировать их:

Это решение , которое Бен Кертис предлагает для развертывания с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'

, чтобыв процессе разработки вы не будете обслуживать предварительно скомпилированные активы.

46 голосов
/ 03 октября 2012

Я только что написал гем для решения этой проблемы в Rails, он называется 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 и сообщить мнеесли у вас есть какие-либо проблемы.

4 голосов
/ 28 сентября 2012

Решение 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
3 голосов
/ 11 октября 2012

Вы можете сэкономить ресурсы сервера для предварительной компиляции ресурсов, выполнив то же самое (предварительная компиляция ресурсов) в локальной системе. И просто переход на сервер.

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
2 голосов
/ 16 июня 2012

Решение , которое Бен Кертис предлагает , не работает для меня, потому что я не копирую папку .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
0 голосов
/ 12 марта 2016

ОП явно запрашивал 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". Если это возвращает $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 (которая возвращает последнюю доступную миграцию). Если они отличаются, вам нужно мигрировать. Если нет, вы можете пропустить миграцию базы данных.

Разработчики Rails, внедряющие с помощью Ansible, могут еще больше сократить время их развертывания, отключив сбор фактов, если в этом нет необходимости (gather_facts: no), и используют конвейерную передачу SSH (export ANSIBLE_SSH_PIPELINING=1).

Если вам нужны подробности, я недавно написал статью на эту тему.

0 голосов
/ 15 мая 2013

Бывают случаи, когда мне нужно принудительно пропустить прекомпиляцию ресурсов при развертывании исправления как можно скорее. Я использую следующий хак в качестве дополнения к другим ответам, чтобы сделать работу.

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, чтобы полностью пропустить прекомпиляцию ресурсов.

...