Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Agile Web Development With Rails, 1st Edition (2005).pdf
Скачиваний:
28
Добавлен:
17.08.2013
Размер:
7.99 Mб
Скачать

If you wanted to find the person with the most experience deploying and scaling Rails applications, you’d turn to Rails’ creator David Heinemeier Hansson. He’s successfully used Rails in a number of wildly successful sites, including Basecamp (http://www.basecamphq.com) and Backpack (http://backpackit.com/ ). I’m thrilled that in addition to his technical advice and the David Says... sidebars, David was kind enough to contribute this chapter to the book.

Chapter 22

Deployment and Scaling

Deployment is supposed to be the happy celebration of an application that is ready for the world. But in order to realize your dreams, you’ll need to prepare yourself and your application for the dangers, risks, and pitfalls of going live. Addressing concerns is exactly what this chapter is about. We’ll examine options that need to be tweaked and the software that needs to be injected as the development setting is replaced by the production setting.

Now that you have built it, they will come. You better be ready for them. As part of deployment process, we’ll discuss how to set your application up so that it will scale. Thankfully, Rails minimizes the concerns of scaling as an up-front activity and postpones most of the necessary steps until the masses are knocking down your door. But if we deal with the anxiety of the attacking hordes in advance, you can rest safely with the comfort of having a known path to follow.

22.1 Picking a Production Platform

Rails runs on a wide variety of web servers and runtimes. Just about any web server implements the CGI protocol, which is the baseline for running Rails.1 In this sea of options, we’ll pay special attention to three web servers and three ways of serving the application. Unless you’re bound to other technology choices, it would be wise to pick from the combinations presented next for a minimum of fuss and a maximum of available assistance.

1But you wouldn’t want to use CGI for real-life applications.

Prepared exclusively for Rida Al Barazi

PICKING A PRODUCTION PLATFORM

441

 

Ease of

Speed

Scalability

 

Setup

 

 

 

 

 

 

 

WEBrick

 

 

 

Apache-CGI

 

 

 

 

 

 

 

Apache-fcgi

 

 

 

 

 

 

 

lighttpd-fcgi

 

 

 

 

 

 

 

Figure 22.1: Comparing Deployment Options

Choosing a Web Server

The primary choices for serving a Rails application are WEBrick, Apache, and lighttpd.2 In some ways, that order also represents the progression most live Rails applications have gone through (or are aiming for). Start out with the ease and comfort of a Ruby-based server, then move to the standard Apache setup, and eventually consider playing around in the easier-to-scale world of lighttpd. The options are summarized in Figure 22.1 .

The good news is that making a choice doesn’t paint you into a corner. Rails is almost indifferent of the underlying web server—you could be running WEBrick in the morning, Apache in the afternoon, and lighttpd in the evening without changing a single comma in your application code.

WEBrick: All Ruby, No Configuration

WEBrick is a pure-Ruby web server that comes bundled with Ruby. It isn’t particularly fast or particularly scalable, but it is incredibly easy to run and free of dependencies. That makes it the first choice when starting out on Rails yet also uniquely suitable for deploying applications that don’t need to scale to thousands of concurrent users. Many internal applications have such humble scaling needs.

Also consider WEBrick as a platform for applications in need of wide distribution. As an example, the Wiki clone Instiki3 managed to become the

2Although lighttpd is not currently available on Windows.

3Instiki is also a creation of David Heinemeier Hansson and used early Rails ideas before

the framework was released.

Prepared exclusively for Rida Al Barazi

Report erratum

PICKING A PRODUCTION PLATFORM 442

most downloaded Ruby application from RubyForge thanks in large part to the promise of No Step Three. Using WEBrick as its web server enabled Instiki to be distributed with a trivial installation procedure. (The OS X version was even packaged with Ruby itself. Double-click the .app file and your personal Wiki is running.)

WEBrick quickly loses its appeal once you move away from internal or personal applications, but that shouldn’t stop you from starting out using it. An application developed under WEBrick requires no changes to be redeployed on Apache or lighttpd. You can even keep developing locally on WEBrick while running the production server on one of the C-based servers.

Apache: An Industry Standard

Apache is ubiquitous, and for good reasons. It’s incredibly versatile, reasonably fast, and well deserving of its near monopolistic role as the opensource web and application server. Therefore, it’s no surprise that Apache is also the most popular choice for taking a Rails application into production.

Out of the box, Apache is capable of running Rails in “only” CGI mode, which is why it’s the default configuration in Rails’ public/.htaccess file. But CGI is definitely not the place you want to be, as we’ll return to in the discussion on CGI. Thankfully, Apache is also capable of running FastCGI through mod_fastcgi.

Unfortunately, Apache development around mod_fastcgi has been dormant since late 2003, and it shows. The module has a number of issues with the 2.x line of Apache that has caused more than a few migrations back to 1.3.x.

While these problems don’t affect all Rails applications (some folks have reported “no problems here” on 2.x), they are still worrying. Deploying a Rails application on mod_fastcgi with Apache 2.x is only for the brave (and those willing to step back to 1.3.x if problems start occurring).

Despite the lack of attention around mod_fastcgi, Apache 1.3.x is still the recommended first step in taking your Rails application online in front of a large expected audience.

Configuration The default way of configuring an Apache Rails application is to dedicate a virtual host. Allocate an entire domain, or subdomain,

Prepared exclusively for Rida Al Barazi

Report erratum

PICKING A PRODUCTION PLATFORM 443

to the application by adding something such as this to your httpd.conf file.

<VirtualHost *:80> ServerName www.depot.com

DocumentRoot /path/application/public/ ErrorLog /path/application/log/server.log

<Directory /path/application/public/> Options ExecCGI FollowSymLinks AllowOverride all

Allow from all Order allow,deny

</Directory>

</VirtualHost>

This definition will work for both CGI and FastCGI serving, but you’ll need to install and configure FastCGI to make the latter work. We’ll look at that shortly.

If you don’t like dedicating an entire virtual host, perhaps because you want the Rails application to be part of a larger site, that’s possible too. All you need to do is make a symbolic link to your public directory from wherever you want the application to live.

Imagine that you have community site that needs a forum and you fancy the URL http://www.example.com/community/forum. On the filesystem that’s /var/www/example/community/forum, which is just a symbolic link to the application directory /var/applications/railsforum/public. Voila!

The symbolic link approach will automatically be picked up by Rails and all the links created by the view helpers, such as image_tag or link_to, will be rewritten to fit under the proper path. If you maintain manual HTML tags with absolute URLs, you’ll have to change them by hand. (This is an excellent reason to always use Rails helper methods to reference resources.)

lighttpd: Specialized and Lightweight

Apache does a great job of being everything to everyone. This opens the door to more targeted approaches, such as lighttpd. It doesn’t have the huge array of modules, years of documentation and tutorials, or the industry support that Apache has, but you might very well want to take a look anyway.

lighttpd is fast. For serving static content, it can be really fast, and it stays usable under much heavier loads than Apache. If nothing else, lighttpd makes an excellent asset server for delivering your JavaScript, stylesheets, images, and other file downloads.

Prepared exclusively for Rida Al Barazi

Report erratum

PICKING A PRODUCTION PLATFORM 444

But lighttpd is more interesting than just a fast server for static data. FastCGI is being actively developed and serves as lighttpd’s premier runtime for dynamic content in any language. The most compelling feature to come out of this attention is built-in load balancing for FastCGI processes on remote machines.

This means that you can have a single lighttpd web server serving as a front to any number of application servers in the back that do nothing but run FastCGI processes. The lighttpd server handles all static requests itself but then delegates the dynamic requests to the servers specified in the back. It even monitors the processes running on the remote machines and decommissions any that have problems. This makes it very easy to scale applications with lighttpd.

What’s holding lighttpd back from being our first choice? Stability, mostly. At the time of writing, lighttpd still had a number of major stability problems, along with critical issues regarding heavy file transfers. These may well have been resolved by the time you read this, but you’d be well advised to give lighttpd an exhaustive performance test before committing to a live rollout of a critical site.

Despite any pockets of instability or missing features, lighttpd should surely be on your radar from day one.

Configuration The minimal configuration for a lighttpd server destined to serve a Rails application is tiny, so instead of just showing a fragment, here’s an example of the whole thing.

server.port = 80 server.bind = "127.0.0.1"

# server.event-handler = "freebsd-kqueue" # needed on OS X

server.modules = ( "mod_rewrite", "mod_fastcgi" )

url.rewrite = ( "^/$" => "index.html", "^([^.]+)$" => "$1.html" ) server.error-handler-404 = "/dispatch.fcgi"

server.document-root = "/path/application/public" server.errorlog = "/path/application/log/server.log"

fastcgi.server = ( ".fcgi" => ( "localhost" =>

(

"min-procs" => 10, "max-procs" => 10,

"socket" => "/tmp/application.fcgi.socket", "bin-path" => "/path/application/public/dispatch.fcgi", "bin-environment" => ( "RAILS_ENV" => "production" )

)

)

)

Prepared exclusively for Rida Al Barazi

Report erratum

PICKING A PRODUCTION PLATFORM 445

This definition is only meant for FastCGI and for running a single application on that lighttpd instance. It’s certainly possible to run more than one application at the same time, though. Consult the lighttpd documentation for more on that.

Note that this configuration handles three tasks: the work of httpd.conf (setting up the basic web server), .htaccess (the caching instructions), and the FastCGI configuration. Very succinct.

If you place this configuration file in config/lighttpd.conf, you can start a server that runs it with lighttpd -f config/lighttpd.conf. (Remember that you normally need to be root to start a server on port 80).

Selecting How to Serve the Application

In some ways, the choice of web server matters less than how you serve the application. All the clever implementations in the world won’t help CGI on lighttpd beat FastCGI running on Apache. But on the other hand, it’s also less of a decision. The simple answer is: use FastCGI! A slightly longer answer follows.

WEBrick: Ease of Use

WEBrick takes the servlet approach. It has a single long-running process that handles each concurrent request in a thread. As we’ve discussed, WEBrick is a great way of getting up and running quickly but not a particularly attractive approach for heavy-duty use. One of the reasons is the lack of thread-safety in Action Pack, which forces WEBrick to place a mutex at the gate of dynamic requests and let only one request through at the time.

While the mutex slows things down, the use of a single process makes other things easier. For example, WEBrick servlets are the only runtime that make it safe to use the memory-based stores for sessions and caches. This is especially helpful since WEBrick is mostly used for development and ease-of-deployment scenarios where you want to cut down on the number of dependencies anyway.

CGI: Hello, World

CGI with Rails is a trial of patience. Requests that take seconds to complete are not at all uncommon. This is due to the nature of CGI. A clean Ruby interpreter is launched on every single request, which in turn has

Prepared exclusively for Rida Al Barazi

Report erratum

PICKING A PRODUCTION PLATFORM 446

to boot the entire Rails environment. All that work just to serve one lousy request. And as the next request comes in, the work repeats all over again.

So why bother with CGI at all? First, all web servers support it out of the box. When you’re setting up Apache with Rails for the first time, for example, it’s a good idea to start out by making it work with CGI. By doing so you sort out all the basic issues of permissions, vhost configurations, and the like before introducing the added complexity of FastCGI. Likewise, it can be a good idea to step down from FastCGI to CGI when you need to debug any such issues.

The second reason to use CGI is when you need to extend the code of Rails itself. Perhaps you’re working on a patch and are using your current application as a testing ground. Or perhaps you just want to tinker with the framework and see the effect of certain changes instantly. FastCGI and servlets will always cache Rails, so any change to the framework requires a restart of the server. With CGI, you can make a change to Rails and see results on the next refresh.

FastCGI: Getting Serious

With FastCGI, you’re strapping a rocket engine on Rails. FastCGI uses long-running processes that initialize the Ruby interpreter and the Rails framework only at start-up. The database connection is established on the first query and kept for the lifetime of the process. As if that wasn’t enough, even your application code is cached in the production environment.

Overhead is reduced because all these things are cached or initialized only once. When a request comes along, there’s no need to load or compile code, reconnect to a database, and so on. The only work that gets done is the work to process the current request. This is significantly faster than the hit-and-forget approach of CGI.

Additionally, the FastCGI processes are not married to the web server process, so you can have 100 web server processes that deal with all the static requests and perhaps just 10 FastCGIs dealing with the dynamic requests. This isn’t the case with servlets, CGI, and even mod_ruby (another deprecated approach to serving applications for Rails).

This is crucially important for memory consumption, as a single Apache instance will eat only about 5MB when doing static serving but can easily take 20–30MB if it needs to host the Ruby interpreter with a loaded application. Having 100 Apaches with 10 FastCGIs will use only 800MB of

Prepared exclusively for Rida Al Barazi

Report erratum

PICKING A PRODUCTION PLATFORM 447

memory while having 100 Apaches each containing mod_ruby process can easily use 3GB of memory. RAM may be cheap, but there’s no reason to be such a spendthrift about it.

The only slight disadvantage to FastCGI is the complication of getting it up and running. This is why you really should start out on WEBrick, then move to CGI when you’re getting closer to deployment, and then decide to tackle the FastCGI hurdle.

The confusing part is that you need three packages when installing on Apache: mod_fastcgi, the FastCGI Developer’s Kit,4 and ruby-fcgi.5 (lighttpd doesn’t need mod_fastcgi, so it’s a little easier there, but we’ll use Apache as the primary example for the rest of this discussion.) In either case, you need to install the Developer’s Kit before installing ruby-fcgi. See the README files for details.

Once it’s installed, you need to configure FastCGI on the web server. For Apache, an example of such a configuration could be.

<IfModule mod_fastcgi.c> FastCgiIpcDir /tmp/fcgi_ipc

FastCgiServer /path/to/app/public/dispatch.fcgi \ -initial-env RAILS_ENV=production \ -processes 15 -idle-timeout 60

</IfModule>

The important part here is the use of the FastCgiServer directive to configure what’s called a static server definition. If the directive wasn’t there, Apache would start a FastCGI server the first time you hit a .fcgi page. That’s called a dynamic server definition, and it leaves the responsibility of when and how many FastCGI servers to start to Apache.

While it might sound dandy having Apache take care of process loading, in reality it isn’t. First, Apache is rather conservative when it comes to adding more server processes. If your load requires 15 servers, it’s going to take Apache a good while to get there, which means a dead-slow site in the meantime. If you use a static server definition in your deployment, you ensure that all 15 servers are started right after the server is launched and that they don’t get decommissioned (and lose their cache) when Apache decides there’s no need for them in the next 30 seconds.

In addition to specifying the path of the static server, we’re also telling FastCGI that it should start Rails in the production environment (we’ll get

4Both available from http://www.fastcgi.com/dist.

5http://raa.ruby-lang.org/project/fcgi

Prepared exclusively for Rida Al Barazi

Report erratum