- •Contents
- •Preface to the Second Edition
- •Introduction
- •Rails Is Agile
- •Finding Your Way Around
- •Acknowledgments
- •Getting Started
- •The Architecture of Rails Applications
- •Models, Views, and Controllers
- •Active Record: Rails Model Support
- •Action Pack: The View and Controller
- •Installing Rails
- •Your Shopping List
- •Installing on Windows
- •Installing on Mac OS X
- •Installing on Linux
- •Development Environments
- •Rails and Databases
- •Rails and ISPs
- •Creating a New Application
- •Hello, Rails!
- •Linking Pages Together
- •What We Just Did
- •Building an Application
- •The Depot Application
- •Incremental Development
- •What Depot Does
- •Task A: Product Maintenance
- •Iteration A1: Get Something Running
- •Iteration A2: Add a Missing Column
- •Iteration A3: Validate!
- •Iteration A4: Prettier Listings
- •Task B: Catalog Display
- •Iteration B1: Create the Catalog Listing
- •Iteration B4: Linking to the Cart
- •Task C: Cart Creation
- •Sessions
- •Iteration C1: Creating a Cart
- •Iteration C2: A Smarter Cart
- •Iteration C3: Handling Errors
- •Iteration C4: Finishing the Cart
- •Task D: Add a Dash of AJAX
- •Iteration D1: Moving the Cart
- •Iteration D3: Highlighting Changes
- •Iteration D4: Hide an Empty Cart
- •Iteration D5: Degrading If Javascript Is Disabled
- •What We Just Did
- •Task E: Check Out!
- •Iteration E1: Capturing an Order
- •Task F: Administration
- •Iteration F1: Adding Users
- •Iteration F2: Logging In
- •Iteration F3: Limiting Access
- •Iteration F4: A Sidebar, More Administration
- •Task G: One Last Wafer-Thin Change
- •Generating the XML Feed
- •Finishing Up
- •Task T: Testing
- •Tests Baked Right In
- •Unit Testing of Models
- •Functional Testing of Controllers
- •Integration Testing of Applications
- •Performance Testing
- •Using Mock Objects
- •The Rails Framework
- •Rails in Depth
- •Directory Structure
- •Naming Conventions
- •Logging in Rails
- •Debugging Hints
- •Active Support
- •Generally Available Extensions
- •Enumerations and Arrays
- •String Extensions
- •Extensions to Numbers
- •Time and Date Extensions
- •An Extension to Ruby Symbols
- •with_options
- •Unicode Support
- •Migrations
- •Creating and Running Migrations
- •Anatomy of a Migration
- •Managing Tables
- •Data Migrations
- •Advanced Migrations
- •When Migrations Go Bad
- •Schema Manipulation Outside Migrations
- •Managing Migrations
- •Tables and Classes
- •Columns and Attributes
- •Primary Keys and IDs
- •Connecting to the Database
- •Aggregation and Structured Data
- •Miscellany
- •Creating Foreign Keys
- •Specifying Relationships in Models
- •belongs_to and has_xxx Declarations
- •Joining to Multiple Tables
- •Acts As
- •When Things Get Saved
- •Preloading Child Rows
- •Counters
- •Validation
- •Callbacks
- •Advanced Attributes
- •Transactions
- •Action Controller: Routing and URLs
- •The Basics
- •Routing Requests
- •Action Controller and Rails
- •Action Methods
- •Cookies and Sessions
- •Caching, Part One
- •The Problem with GET Requests
- •Action View
- •Templates
- •Using Helpers
- •How Forms Work
- •Forms That Wrap Model Objects
- •Custom Form Builders
- •Working with Nonmodel Fields
- •Uploading Files to Rails Applications
- •Layouts and Components
- •Caching, Part Two
- •Adding New Templating Systems
- •Prototype
- •Script.aculo.us
- •RJS Templates
- •Conclusion
- •Action Mailer
- •Web Services on Rails
- •Dispatching Modes
- •Using Alternate Dispatching
- •Method Invocation Interception
- •Testing Web Services
- •Protocol Clients
- •Secure and Deploy Your Application
- •Securing Your Rails Application
- •SQL Injection
- •Creating Records Directly from Form Parameters
- •Avoid Session Fixation Attacks
- •File Uploads
- •Use SSL to Transmit Sensitive Information
- •Knowing That It Works
- •Deployment and Production
- •Starting Early
- •How a Production Server Works
- •Repeatable Deployments with Capistrano
- •Setting Up a Deployment Environment
- •Checking Up on a Deployed Application
- •Production Application Chores
- •Moving On to Launch and Beyond
- •Appendices
- •Introduction to Ruby
- •Classes
- •Source Code
- •Resources
- •Index
- •Symbols
REPEATABLE DEPLOYMENTS WITH CAPISTRANO 618
every flavor of Unix (including Linux and Mac OS X) as well as Windows. And it has proven itself over the years. New releases of Apache get better and better and don’t tend to introduce regressions.
Out of the box, Apache 2.2 supports the load balancing of HTTP requests with the built-in mod_proxy_balancer. You can combine this module with Apache’s powerful mod_rewrite module to create truly impressive handling of requests based on any number of criteria that are important to you. If you have an existing complex web site, chances are that you’re running Apache to weave all the parts together.
Lighttpd: Up and Coming Contender
In contrast to the do-anything Apache, Lighttpd is a simpler, less flexible web server. Its raison d’etre is performance. For serving static content, it can be really fast and reportedly stays usable under heavier loads than Apache. It’s also the only server in recent memory that has an actively developed FastCGI implementation. So, if for some reason you want to use FastCGI to deploy your Rails applications, Lighttpd might be just the ticket.
The downside to Lighttpd is that it’s young and is under heavy development. Version to version stability hasn’t been one of its primary features: some versions have been much more stable than later versions. However, the development of this server is moving at a rapid pace, and it very well may be the wave of the future. It should be on your radar from day one.
Pound and Other Load Balancers
As we mentioned, when using Mongrel, many other web servers, including those built into hardware, become viable and powerful front ends for your Rails application. You might want to consider these once you have your application deployed and have figured out how you want to scale it to the next level. However, when you’re just getting started with deployment, it’s important to remember to leave yourself the option to integrate well with load balancers. Using HTTP-based proxying to Mongrel fits perfectly into place, so for now, just keep it in mind. Until you need it, you should probably just stick with Apache or Lighttpd.
27.4Repeatable Deployments with Capistrano
Now that we’ve talked about how a deployed Rails application is structured, we need to turn our focus just a bit and look at how we get Rails applications from our local machines up to the remote servers where it will run. When the first edition of this book was written, moving code into production was very much a duct tape and baling wire affair. Everyone did it differently, and life
Report erratum
SETTING UP A DEPLOYMENT ENVIRONMENT 619
was pretty miserable. Then, along to the rescue came Jamis Buck with a tool originally known as SwitchTower, later renamed to Capistrano.
Capistrano is a Ruby-based utility that was created in order to reliably and repeatably deploy Rails applications on remote servers. It uses SSH to communicate with the servers and execute commands on them. It’s a wildly configurable tool; however, for simple deployments, it’s straightforward to use. It’s somewhat like a turbocharged version of Rake in that Capistrano recipes are composed of a set of tasks to be performed. In Capistrano, however, tasks are set up with methods that will cause actions to happen on one or more remote servers.
Like Rails, Capistrano is opinionated software—it makes a few assumptions. The first is that you are deploying to a Unix-based system. The second is that your code is held in a repository that is accessible from both the machine you are deploying from and the machine to which you are deploying.1
The first assumption, that we’re using a Unix-based system, is important. Without getting into deep philosophical and sometimes religious arguments, it’s important noting that all of the major Rails deployments to date have been to Unix-based systems. As well, all of the Rails-core team members work on and deploy to Unix-based systems. If you have to venture off and deploy on Windows, you’re going to be on your own. Our recommendation is to not do it.
The second assumption—that you store your application in a supported source code control system—isn’t nearly as constraining. If you use a source code control system that Capistrano doesn’t support, it’s not too terribly hard to add support for it. And if you don’t use a source code control system, you really should.
So, to summarize: developers work as usual, checking code into and out of their source code repository. When it comes time to deploy into production, they issue a particular command, and Capistrano retrieves the application from the repository and deploys it onto one or more server machines. This process is illustrated in Figure 27.2, on the following page.
27.5Setting Up a Deployment Environment
As you have seen, there are many options when setting up a deployment environment. These options can be combined in many, many ways. Rather than spend two sentences in each, which wouldn’t give you much in the way of practical advice, we’ve decided to focus on detailed instructions for setting up a recommended environment using the Apache web server with mod_proxy_balancer, Mongrel, and MySQL.
1. Capistrano 1.1 supports applications hosted in Bazaar, Bazaar-NG, cvs, Darcs, Perforce, and Subversion repositories.
Report erratum
SETTING UP A DEPLOYMENT ENVIRONMENT 620
Developer |
Repository |
|
Application |
|||
Machines |
|
|
Server(s) |
|||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
. |
|
|
|
|
|
|
|
. |
|
|
Copies files under |
||||
|
|
|
control of deploy.rb |
$ rake remote:code_deploy
Figure 27.2: Capistrano Deploys from the Repository into Production
Most of the time you’ll be issuing commands on your local computer, but during initial setup you’ll also be working on your servers. We show which computer you’re using in the command prompts.
Step One: Setting Up a Machine
Once you have a suitable machine to deploy your application onto, your first job should be cleaning it up so that it’s ready. If it’s a PC, make sure it’s running the latest version of Linux, FreeBSD, or Solaris. If it’s a Mac, make sure it’s running the latest released version of Mac OS X.
The next step is to install the various software components you need. The items you’ll need to install are
•The Apache 2.2 (or latest released version) web server
•MySQL 5.0 (or latest released version) database server
•Ruby 1.8.4 or later
•RubyGems to manage Ruby-based packages
•MySQL/Ruby library
•Ruby Termios Library
For these components, we recommend that you use a package manager. On Ubuntu Linux, this means using apt-get. On Red Hat, use rpm. On Mac OS X, use MacPorts. Using a package manager means that it’s much easier to move between versions of software. With new versions of Apache, Ruby, and MySQL showing up all the time, each of which will contain security and performance
Report erratum
SETTING UP A DEPLOYMENT ENVIRONMENT 621
fixes that you will want, using a package manager will help you stay up-to- date.
As an example, here are the set of commands that you would use to install the above components onto a Mac OS X system using MacPorts.2
# On the server(s) and your client (if not already installed) $ sudo port install apache2
$ sudo port install mysql5 +server $ sudo port install ruby
$ sudo |
port |
install |
rb-rubygems |
|
$ sudo |
port |
install |
rb-termios |
# for readline support in irb |
Once you have the above native components installed, you’ll need to install the following RubyGems: Rake, Rails, Capistrano, and Mongrel.
To install these gems quickly, use the following.
# On the server(s) |
and your client (if not already installed) |
$ sudo gem install |
--include-dependencies rake |
$ sudo gem install |
--include-dependencies rails |
$ sudo gem install |
--include-dependencies termios |
$ sudo gem install |
--include-dependencies capistrano |
$ sudo gem install |
--include-dependencies mongrel |
$ sudo gem install |
--include-dependencies mongrel_cluster |
At this point, you have all the bits of software that you need to launch a basic Rails application. Of course, if your application relies on other Ruby gems, you should install those as well. Now it’s time to deal with your database.
Step Two: Setting Up Your Database
Once you have MySQL up and running on your server—either by installing the prebuilt version from MySQL or by using a package manager—you’ll need to create a database as well as a database user for your application. To do this, you’ll need to do the following.
# On your database server $ mysql -u root -p
Enter password: ********
Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 2080 to server version: 5.0.19
Type 'help;' or '\h' for help. Type '\c' to clear the buffer.
mysql> CREATE DATABASE myapplication;
Query OK, 1 row affected (0.01 sec)
mysql> GRANT ALL PRIVILEGES ON myapplication.* TO 'myapplication'@'localhost'
-> IDENTIFIED BY 'some_pass' WITH GRANT OPTION; Query OK, 0 rows affected (0.01 sec)
2. Debian and Ubuntu users might want to look at Chris McGrath’s write-up at http://mongrel.rubyforge.org/docs/debian-sarge.html.
Report erratum
SETTING UP A DEPLOYMENT ENVIRONMENT 622
You’ll also need to update your config/database.yml file in your application with the relevant information in the production block.
production: adapter: mysql
database: myapplication username: myapplication password: somepass
At this point, you’ve got the minimum configuration you need to for your application to talk to MySQL. Now you should also take a few minutes to think about the security implications around your database. Use firewalls and the database’s built-in mechanisms to restrict network access to the absolute minimum set of machines that need it. Make sure you have a password policy which restricts access to a need to know basis and that changes passwords frequently. Keep production passwords out of the checked-in configuration files—use Capistrano hooks to copy the production database.yml file in after each deployment. You could use something like the following.
task :after_update_code, :roles => :app do
db_config = "#{shared_path}/config/database.yml.production" run "cp #{db_config} #{release_path}/config/database.yml"
end
Finally, if you are working in a regulated environment, make sure that your policies meet or exceed the requirements of these regulations.
Configuring Mongrel
The next step is to add the mongrel configuration to your project. To do this, execute the following in your application’s directory (we’ve split this command onto two lines to make it fit the page).
# On your local computer
$ mongrel_rails cluster::configure -e production -p 8000 \ -a 127.0.0.1 -N 2 -c /deploy/path/current
(If you get an error, make sure that you are in the top level of your application’s directory when you issue the command.)
The parameters to this command are pretty important. The -p 8000 parameter specifies that the mongrel instances will start up on ports beginning with 8000 on your deployment server. The -a 127.0.0.1 parameter will set up Mongrel to listen to the localhost interface. The -N 2 parameter indicates that two instances of Mongrel will be started. And, the -c /deploy/path/current argument is where your application will be deployed to on the remote server. Note that the word current in this path is required: the deploy/path part is the path to your application, and current is a directory that Capistrano creates inside that application structure.
Report erratum
SETTING UP A DEPLOYMENT ENVIRONMENT 623
Step Four: Deploy with Capistrano
To add the necessary files to your project for Capistrano to do its magic, execute the following command.
# On your local computer
$ cap --apply-to /local/project/path [applicationname] exists config
create config/deploy.rb exists lib/tasks
create lib/tasks/capistrano.rake
From the output, you can see that Capistrano sets up two files. The first, config/deploy.rb, contains the recipes needed to deploy your application. The second, lib/tasks/capistrano.rake, adds some tasks to your application so that you can directly call Capistrano tasks using Rake. The next step is to edit the config/deploy.rb to set it up for your deployment. For a basic setup, you need to make only a few edits to the file. You’ll need to add a require statement to the top of the file to include the mongrel_cluster recipes that will make deploying with mongrel painless:
require 'mongrel_cluster/recipes'
After you add this line, you’ll need to edit several of the properties to match your application. The properties you’ll want to edit or add are
set :application, "applicationname"
set :repository, "http://svn.host.com/#{application}/trunk" set :deploy_to, "/Library/Rails/#{application}"
set :mongrel_conf, "#{current_path}/config/mongrel_cluster.yml"
role :web, "your.host.com" role :app, "your.host.com"
role :db, "your.host.com", :primary => true
Once you’ve made these edits, you’re ready to do the deployment. The first time you deploy your application, you’ll need to perform two steps. The first sets up the basic directory structure to deploy into on the server.
# On your local computer $ rake remote:setup
When you execute this command, Capistrano will prompt you for your server’s password. It then connects and makes the necessary directories. Once this command is done, you’re ready for step two: actually deploying your application. Use the following command.
# On your local computer $ rake remote:cold_deploy
This command will run, deploy your application to the server and then start the Mongrel instances. If all went well, your application will be running on
Report erratum
SETTING UP A DEPLOYMENT ENVIRONMENT 624
ports 8000 and 8001 on your remote system. You can verify that by executing the following command on your remote system.
# On your server
$ curl -I http://127.0.0.1:8000
HTTP/1.1 200 OK Content-Length: 7551
Date: Thu, 07 Sep 2006 18:02:50 GMT Cache-Control: no-cache
Server: Mongrel 0.3.13.3
Content-Type: text/html; charset=utf-8
This tells you all is well with your Mongrel setup and your application servers.
Step Five: Connect Apache to Mongrel
Once your application is deployed and running on a pack of Mongrels, the last step is to connect Apache to your app server instances. To do this, you’ll need to edit the Apache configuration on your server(s) and add the following.
<Proxy balancer://mongrel_cluster> BalancerMember http://127.0.0.1:8000 BalancerMember http://127.0.0.1:8001
</Proxy>
<VirtualHost *:80> ServerName myapp.com
DocumentRoot /Library/Rails/myapplication/current/public
<Directory "/Library/Rails/myapplication/current/public" > Options FollowSymLinks
AllowOverride None Order allow,deny Allow from all
</Directory>
RewriteEngine On
#Check for maintenance file and redirect all requests RewriteCond %{DOCUMENT_ROOT}/system/maintenance.html -f RewriteCond %{SCRIPT_FILENAME} !maintenance.html RewriteRule ^.*$ /system/maintenance.html [L]
#Rewrite index to check for static
RewriteRule ^/$ /index.html [QSA]
#Rewrite to check for Rails cached page RewriteRule ^([^.]+)$ $1.html [QSA]
#Redirect all non-static requests to cluster RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_FILENAME} !-f
RewriteRule ^/(.*)$ balancer://mongrel_cluster%{REQUEST_URI} [P,QSA,L]
</VirtualHost>
Report erratum