Posts Tagged ‘apache’

Moving a Rails/Mongrel site to Phusion Passenger on Ubuntu

Sunday, March 15th, 2009

As I’ve noted before, several of our Rails projects are hosted using Phusion Passenger. We’ve been helping HitFix explore this option in recent weeks because it solves several minor deployment and restart frustrations they’ve been grappling with, and in the process I wrote up this walk-through for the installation.

This process is actually really easy and the instructions on the Passenger site and those provided during the installation process are excellent. The glitch we ran in to is that recent versions of Ubuntu and/or Apache 2.2.x use a slightly different method of loading modules and module configuration. (I haven’t run in to this method often enough to have learned whether it’s new in Apache or in Ubuntu.)

Instead of using one monolithic configuration file, sometimes offloading host configuration to included files, these Apaches instead globally include all modules found in the mods_enabled subdirectory of the Apache directory. Each module has two files, a .load file and a .conf file. The .load file contains only the LoadModule configuration directive; the .conf file contains any configuration directives.

Now, the actual files live in the mods_available subdirectory of Apache, and activating a module just means adding a symlink to the relevant files in mods_enabled, then restarting Apache. Deactivating it just means removing the symlink and restarting again.

With that in mind, here’s the Passenger process:

  • Install Passenger on the server. This should be as simple as:

sudo gem install passenger
sudo passenger-install-apache2-module

  • On one system, this second command actually told me that the Apache 2 Development Headers were missing. (Passenger needs these to compile the Apache module.) I needed to run sudo apt-get install apache2-prefork-dev to get these headers. This didn’t work on the first attempt, but running sudo apt-get update once or twice finally got me a successful install of that package. Running the passenger-install-apache2-module script then worked successfully. Note that you will need a compiler on the system in order to generate the Apache module. You would think you could take this for granted, but apparently not.

At this point, the module is built. This is where we have to adapt Phusion’s original installation instructions. Essentially, the next step is to make sure the module is loaded by Apache. Here’s how I did this:

  • Create a file at /etc/apache2/mods-available/passenger.load with the following content:

LoadModule passenger_module /usr/lib/ruby/gems/1.8/gems/passenger-2.0.6/ext/apache2/mod_passenger.so

  • Create a file at /etc/apache2/mods-available/passenger.conf with the following content (I think the IfModule container may be optional, but I played safe):

<IfModule passenger_module>
PassengerRoot /usr/lib/ruby/gems/1.8/gems/passenger-2.0.6
PassengerRuby /usr/bin/ruby1.8
</IfModule>

  • Create symbolic links to both those files from /etc/apache2/mods-enabled/, i.e.

sudo ln -s /etc/apache2/mods-available/passenger.load /etc/apache2/mods-enabled/
sudo ln -s /etc/apache2/mods-available/passenger.conf /etc/apache2/mods-enabled/

We can restart Apache at this point without disturbing the existing site. Note that we can change which ruby is used (e.g. for Ruby Enterprise Edition) by altering the path at the PassengerRuby directive and restarting Apache.

To move a site to Passenger, you want to back up the file found at /etc/apache2/sites-available/yoursite, then edit it to make these changes:

  • Change the DocumentRoot to the public directory of the app, e.g. DocumentRoot /u/apps/yoursite/current/public
  • Comment out (with a #) all the lines related to the proxy, starting with the Proxy balancer definition and extending through all the ProxyPass, ProxyPassReverse and ProxyPreserveHost directives.
  • Restart Apache (/etc/init.d/apache2 restart). If something goes wrong, restore the backup you made of the configuration file and restart Apache again to restore the previous configuration.

The final trick is to update the restart task in Capistrano to handle restarting Passenger and not Mongrel. This is actually pretty easy, particularly if you’ve already overloaded the restart task in your config/deploy.rb file. Just replace the line reading run "mongrel_rails cluster::restart -C #{mongrel_config}" with one reading run "touch #{current_path}/tmp/restart.txt" and we’re good to go. The start and stop tasks probably also needs tweaking to remove the mongrel commands–probably pasting the same command in for “start” will do the trick unless we feel like putting Apache commands in.

Phusion Passenger, permissions, and Rails’ session store

Tuesday, November 25th, 2008

For various reasons, we’re moving three Rails apps hosted on one VPS to use Phusion Passenger rather than the Apache/Mongrel Cluster stack (via Coda Hale) we’ve been using since we got started.

The Passenger installation process really is as easy as it’s been described, particularly if you’re as comfortable with Apache configuration as we are, but by moving responsibility for serving your apps from Mongrel to Apache, file and process ownership becomes an issue in a way it wasn’t when Mongrel and Apache talked to each other and otherwise minded their own stores. Ben Hughes hinted at this in his walk-through of the Passenger installation, and we were not immune.

Our problem turned out to be session files. The first application we moved to Passenger (and it’s worth noting that apps running on Passenger and apps running Mongrel cluster can coexist under the same Apache) was Redmine, which uses file-based session store by default. If ownership of those session files creates a problem, and it did for us, switching to ActiveRecord session storage resolves the problem.

For those wondering about the point of Passenger, it’s memory use. A single Mongrel instance and an app running under Passenger have comparable RAM requirements, but because Passenger is an Apache module, it can spawn new instances as requests arrive; the Mongrel cluster solution requires multiple mongrel instances to be running all the time. This means Mongrel apps essentially require the RAM for peak capacity all the time. Passenger gives that RAM back at non-peak times, which allows more apps to share the same system, allowing us to get more mileage from our VPSes. (It’s worth noting that the Phusion guys claim a 33% memory savings per instance if Ruby Enterprise Edition is also used, so that will probably be our next step when all the apps are Passenger-ized.)

Update, 12/10: Another gotcha we found was file permissions. Again because of the permissions issues, Rails wasn’t writing to the shared/logs/production.log file. We didn’t notice this immediately, because the applications ran, they just didn’t log anything. The solution here was to change the ownership and permissions on the files. Specifically, we changed group ownership to “nobody” (the group Apache runs under) and the mode of the log file to 664.

Y Be Slow?

Thursday, March 27th, 2008

When Yahoo! released it’s YSlow application last year as a plugin for the Firebug Firefox extension (because really, what web developers don’t have Firefox installed, even if it isn’t their primary browser?) nearly everyone installed the tool and started going down the list of rules Yahoo! laid down for improving “front-end performance” on websites. Several people wrote up suggestions for using the output to improve Rails apps, including a good summary for Nginx, but we’re using Apache.

“Front-end performance” means attacking speed as a problem between the browser and the server, and not as a problem which exists solely behind the server. We can optimize an application as much as we want, but if the browser makes thirty-five round-trips to the server fetching CSS, image files, and JavaScripts, application optimization isn’t helping much. In addition to grading apps on these fourteen points, YSlow gives a load time (in milliseconds!), and as your grade improves, you can also see the load time improving by perceptible intervals.

This afternoon I ran YSlow on the current development version of the La Cucina Italiana site. The initial grade was 52, an F. When I was done, it was 88, a B, and if I circumvent a dubious aspect of YSlow, it becomes a 98 A. I made only four changes: three Apache configuration tweaks and one Rails change. If you’re a Rails developer with Apache 2.2 in your stack, here are the low-hanging fruit for a better YSlow score. You can make these changes in your site configuration or within an .htaccess file.

ETags: The Yahoo! explanation of how ETags are important is a little confusing. The summary is this: you want to turn ETags off. They’re ineffective with multiple servers (i.e. an asset-server setup) and even if everything is on one host, there are other, just as useful means of avoiding downloads of cached files. So spare yourself that many bytes per connection and turn them off. It’s a one-line fix:

FileETag none

GZip components: The time saved by sending down compressed files is greater than the time spent compressing them, but only for text-based file types. (Images and PDFs are already compressed, so re-compression won’t help and might hurt.) If you’re using Coda Hale’s configuration for Apache and Mongrel (and many of us are) the code for compressing text components before download is already in your Apache configuration. However, Coda’s config misses one file type which Rails seems to use for Javascript, application/x-javascript, so YSlow keeps dinging us for uncompressed JavaScript files. With that added, the configuration for compression looks like this:

AddOutputFilterByType DEFLATE text/html text/plain text/xml application/xml application/xhtml+xml text/javascript text/css application/x-javascript
BrowserMatch ^Mozilla/4 gzip-only-text/html
BrowserMatch ^Mozilla/4.0[678] no-gzip
BrowserMatch \bMSIE !no-gzip !gzip-only-text/html

(Note that there should be only four lines there: one AddOutputFilterByType and then three BrowserMatch lines.)

Far-future cache expiration: Yahoo! is looking for really far future expiration dates for cache expiration. The reasoning here is to maximize the odds that your users are arriving at your site with a “primed” cache, i.e. one where most of your components are already loaded. By putting a way-out expiration on components, users keep those bits in their cache longer. The flip side is that filenames need to change in order to prompt the browser to load a changed file. Rails does this by automatically appending a timestamp to the filenames of many components. (Check your page source and see what is actually getting called when you load a CSS file.) Therefore, we can set our default expires header way out in the future.

This is a two-line addition to the Apache configuration:

ExpiresActive On
ExpiresDefault "access plus 10 years"

Reducing connection count: This is the tough one. Default Rails apps load a zillion (OK, seven) Javascript files and at least one but possibly many more CSS files, and that’s before we start with page images. Most browsers will only open two or three connections at a time to a given server, so all the files are waiting in line to get loaded. You can maximize the number of concurrent connections by using an asset server; this fools the browser into opening more connections by giving it more hosts to connect to. (This is like opening more Starbucks’ in the same town.) That doesn’t really save you, though, and it boosts the number of DNS lookups the browser needs to run. To really cut the number of JS and CSS files, you need to bundle assets. It’s possible to bundle images, but that’s not a problem for LCI, which has only four or five images on the home page. Bundling JS and CSS is where the action is.

There are multiple ways to do this, including a nifty program called AssetPackager and the caching code built in to Rails 2.0. We went with what we saw as the simplest route, which was bundle_fu, but we have an eye on AssetPackager for future consideration. bundle_fu is a Rails plug-in which takes all the CSS and JS files for a template and concatenates them into one CSS and one JS file, sometimes even minifying the JS. It’s a quick installation, and not only does it convince YSlow to give your site a good grade on the number of connections, it also gives good grades for “Put JS at the bottom” and “Minify JS” because, hey, there’s only one file, right? This one move improved our YSlow score more than any other step.

The score at this point: 88 points, a high B. We’re still suffering for not having minified our JS (a little, and we’ve decided not to bother, since it’s being gzipped anyway) and for not using a CDN.

Cheating: The CDN point is one of the most hotly-contested on the YSlow report card, because most sites as small as ours see very little return on the investment of putting our site on a Content Delivery Network such as Akamai. It’s possible, though, to get YSlow to turn a blind eye to your CDN-less-ness. Just add your own site’s domain to the list of CDN servers in YSlow’s preferences. Doing that jumped our score up to 98–pretty close to perfect.

Unfixable: One thing which may keep your site from ever having a good YSlow report is using a lot of outside components. Common Kitchen gets dinged because its page loads include calls to two different ad networks, not to mention multiple images from Amazon on some pages and the Google Analytics code. This makes for a lot of un-bundle-able scripts, multiple DNS requests, and a lot of components where we don’t control their ETags (or lack thereof), expiration header, or compression. Of course, we can’t control their server uptime, either, so it may be that the YSlow scores are the least of our problems!