问题
I'm deploying a simple Rails app to this small ubuntu precise64 server setup :
* 1 web server running nginx
* 2 app servers running unicorn
* 1 db server running postgresql
My servers are provisionned with Puppet and I'm pushing the app with capistrano.
My Capfile
load 'deploy'
# Uncomment if you are using Rails' asset pipeline
load 'deploy/assets'
load 'config/deploy' # remove this line to skip loading any of the default tasks
The deploy.rb file is super simple
# Execute "bundle install" after deploy, but only when really needed
require "bundler/capistrano"
# Name of the application in scm (GIT)
set :application, "devops-test-app"
set :repository, "https://github.com/geoffroymontel/devops-test-app.git"
set :scm, :git
set :deploy_to, "/var/www/#{application}"
# server there the web server is running (nginx)
role :web, "172.16.0.2"
# server there the db is running
# This is where Rails migrations will run
role :db, "172.16.0.3", :primary => true
# servers there the app servers are running (unicorn)
role :app, "172.16.0.4", "172.16.0.5"
set :rails_env, :production
# user on the server
set :user, "deployer"
set :use_sudo, false
namespace :deploy do
task :start, :roles => :app, :except => { :no_release => true } do
run "service unicorn_#{application} start"
end
task :stop, :roles => :app, :except => { :no_release => true } do
run "service unicorn_#{application} stop"
end
task :restart, :roles => :app, :except => { :no_release => true } do
run "service unicorn_#{application} restart"
end
task :copy_in_database_yml do
run "cp #{shared_path}/config/database.yml #{latest_release}/config/"
end
# Precompile assets
# I have to precompile the assets on the app servers too, and I don't really know why...
# namespace :assets do
# task :precompile, :roles => [:web, :app], :except => { :no_release => true } do
# run %Q{cd #{latest_release} && #{rake} RAILS_ENV=#{rails_env} #{asset_env} assets:precompile}
# end
# end
end
before "deploy:assets:precompile", "deploy:copy_in_database_yml"
If I don't precompile assets on the app servers, the application fails.
$ cap ROLES="app" COMMAND="cat /var/www/devops-test-app/current/log/production.log" invoke
* 2013-05-01 21:43:10 executing `invoke'
* executing "cat /var/www/devops-test-app/current/log/production.log"
servers: ["172.16.0.4", "172.16.0.5"]
[172.16.0.5] executing command
** [out :: 172.16.0.5] Connecting to database specified by database.yml
** [out :: 172.16.0.5] Connecting to database specified by database.yml
** [out :: 172.16.0.5] Started GET "/posts" for 172.16.0.2 at 2013-05-01 19:42:10 +0000
** [out :: 172.16.0.5] Processing by PostsController#index as HTML
** [out :: 172.16.0.5] Rendered posts/index.html.erb within layouts/application (25.7ms)
** [out :: 172.16.0.5] Completed 500 Internal Server Error in 122ms
** [out :: 172.16.0.5]
** [out :: 172.16.0.5] ActionView::Template::Error (application.css isn't precompiled):
** [out :: 172.16.0.5] 2: <html>
** [out :: 172.16.0.5] 3: <head>
** [out :: 172.16.0.5] 4: <title>DevopsTestApp</title>
** [out :: 172.16.0.5] 5: <%= stylesheet_link_tag "application", :media => "all" %>
** [out :: 172.16.0.5] 6: <%= javascript_include_tag "application" %>
** [out :: 172.16.0.5] 7: <%= csrf_meta_tags %>
** [out :: 172.16.0.5] 8: </head>
** [out :: 172.16.0.5] app/views/layouts/application.html.erb:5:in `_app_views_layouts_application_html_erb__677166568443748084_17536100'
** [out :: 172.16.0.5] app/controllers/posts_controller.rb:7:in `index'
If I uncomment the commented lines in deploy.rb
, everything is fine.
But why ?? I thought you would need to compile the assets only on the web server, not on the app server.
Please help me understand why :)
Thanks, best regards
Geoffroy
回答1:
Because, among other things, Rails generates a manifest of the precompiled assets including hashes of the files in the filenames, and then uses those names when you include the assets in a page. When you say image_url('foo.jpg')
, Rails will end up generating foo-b48cf0140bea12734db05ebcdb012f1d265bed84.jpg
in the source code.
Rails needs to know what compiled names to use for these assets, so it has to have the manifest, so the precompilation needs to be done on the app server.
Take a look at public/assets/manifest.yml
- that's the file that Rails needs in order to serve precompiled assets properly.
回答2:
This is a known bug in Capistrano.
1) Comment
load 'deploy/assets'
on your Capfile
.
2) Add the following lines at the top of your deploy.rb
file :
set :assets_role, [:web, :app]
load 'deploy/assets'
That's it !
回答3:
Be sure you configured Nginx to serve static files without hitting the application server. It's likely this is not configured, thus the assets exist but Nginx is not serving them and is falling back to the application.
来源:https://stackoverflow.com/questions/16344022/why-do-i-need-to-precompile-rails-assets-on-app-servers-and-not-web-servers-only