Przyspieszenie zasobów: precompile z rails 3.1/3.2 Capistrano deployment

Moje wdrożenia są powolne, trwają co najmniej 3 minuty. Zadaniem slow Capistrano podczas wdrażania jest assets:precompile. Zajmuje to prawdopodobnie 99% całkowitego czasu wdrożenia. Jak mogę to przyspieszyć? Czy powinienem wstępnie skompilować Moje zasoby na moim lokalnym komputerze i dodać je do mojego Git repo?

Edit: dodanie config.assets.initialize_on_precompile = false do mojej aplikacji.plik RB zmniejszył czas prekompilacji z pół minuty, ale nadal jest powolny.

Author: Nicolai Reuschling, 2012-01-26

7 answers

Chodzi o to, że jeśli nie zmienisz swoich zasobów, nie musisz ich przekompilować za każdym razem:

To jest rozwiązanie, które Ben Curtis proponuje dla wdrożenia z 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

Oto inne podejście oparte na wieku aktywów (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

Jeśli wolisz prekompilować swoje zasoby lokalnie, możesz użyć tego zadania:

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 

Innym ciekawym podejściem może być użycie Git hook. Na przykład możesz dodać ten kod do .git/hooks/pre-commit, który sprawdza, czy są jakieś różnice w plikach zasobów, a następnie wstępnie je kompiluje i dodaje do bieżącego commita.

#!/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

Jeśli zdecydujesz się na takie podejście, prawdopodobnie będziesz musiał zmienić swoje config/environments/development.rb dodając:

config.assets.prefix = '/assets_dev'

Tak, że podczas rozwoju nie będziesz służył wstępnie skompilowanych aktywów.

 84
Author: tommasop,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-12-02 14:00:25

Właśnie napisałem klejnot do rozwiązania tego problemu wewnątrz szyn, o nazwie turbo-sprockets-rails3 . Przyspiesza assets:precompile tylko rekompilując zmienione pliki i kompilując tylko raz w celu wygenerowania wszystkich zasobów. To działa po wyjęciu z pudełka dla Capistrano, ponieważ twój katalog zasobów jest współdzielony między wydaniami.

Jest to o wiele bardziej kuloodporne niż rozwiązania, które używają git log, ponieważ moja łatka analizuje źródła Twoich aktywów, nawet jeśli pochodzą z klejnotu. Na przykład, jeśli zaktualizujesz jquery-rails, zostanie wykryta zmiana dla application.js, a tylko application.js zostanie skompilowana.

Zauważ, że próbuję również połączyć tę łatkę z Rails 4.0.0 i ewentualnie Rails 3.2.9 (zobacz https://github.com/rails/sprockets-rails/pull/21 ). ale na razie byłoby super, gdybyś mógł mi pomóc przetestowaćturbo-sprockets-rails3 gem, i daj mi znać, jeśli masz jakieś problemy.

 46
Author: ndbroadbent,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-03 00:11:28

Rozwiązanie Tommasopa nie działa, gdy włączona jest kopia w pamięci podręcznej, moja zmodyfikowana wersja:

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
 4
Author: yuanyiz1,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-09-28 09:33:02

Możesz zapisać wysiłek serwera dotyczący wstępnej kompilacji zasobów, wykonując to samo (wstępnie kompilowanie zasobów) w systemie lokalnym. I przechodzę na serwer.

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
 3
Author: Vimal,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-11 12:25:38

Rozwiązanie zaproponowane przez Bena Curtisa nie działa dla mnie, ponieważ nie kopiuję .folder git podczas instalacji (wolny i bezużyteczny):

set :scm, :git
set :deploy_via, :remote_cache
set :copy_exclude, ['.git']

Używam poniższego fragmentu, 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
 2
Author: pinguin666,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-06-18 19:18:13

Są chwile, kiedy muszę wymusić prekompilację skip asset podczas wdrażania poprawki jak najszybciej. Używam następujący hack jako uzupełnienie innych odpowiedzi do pracy.

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)

Ten skrypt zmieni wbudowany atut-precompile hooking, więc zostanie wywołany na podstawie parametru skip_assets . Mogę zadzwonić cap deploy -S skip_assets=true, aby całkowicie pominąć prekompilację aktywów.

 0
Author: lulalala,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-05-15 03:57:17

OP wyraźnie poprosił o Capistrano, ale jeśli instalujesz bez dedykowanego narzędzia wdrażania (poprzez skrypt bash, Ansible playbook lub podobne), możesz użyć następujących kroków, aby przyspieszyć wdrażanie Rails:]}

  • Pomiń instalację pakietu
    bundle check zwraca 1 jeśli istnieją klejnoty do zainstalowania (1 w przeciwnym razie), więc łatwo jest pominąć instalację pakietu, jeśli nie jest to konieczne.

  • Skip asset precompilation
    Use git rev-parse HEAD przed pobraniem zmian i zapisaniem aktualnej wersji SHA w zmiennej (powiedzmy $previous_commit). Następnie wyciągnij zmiany i sprawdź, czy zasoby uległy zmianie za pomocą polecenia git diff --name-only $previous_commit HEAD | grep -E "(app|lib|vendor)/assets". Jeśli to zwróci $1, możesz bezpiecznie pominąć prekompilację zasobów(jeśli używasz wdrożeń opartych na wydaniu, możesz chcieć skopiować zasoby do katalogu nowego wydania).

  • Pomiń migracje baz danych
    Jeśli używasz MySQL, użyj polecenia mysql --user=USER --password=PASSWORD --batch --skip-column-names --execute="USE MYAPP; SELECT version FROM schema_migrations ORDER BY version DESC LIMIT 1;" z katalogu głównego aplikacji, aby uzyskać nazwę najnowsza migracja stosowana. Porównaj to z wyjściem polecenia ls db/migrate | tail -1 | cut -d '_' -f 1 (które zwraca najnowszą dostępną migrację). Jeśli różnią się, musisz przeprowadzić migrację. Jeśli nie, można pominąć migracje baz danych.

Programiści rails wdrażający z Ansible mogą jeszcze bardziej skrócić czas wdrażania, wyłączając gromadzenie faktów, jeśli nie jest to konieczne (gather_facts: no) i używając SSH pipelining (export ANSIBLE_SSH_PIPELINING=1).

Jeśli chcesz więcej szczegółów, napisałem niedawno artykuł na ten temat.

 0
Author: Michael Trojanek,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-11 23:46:35