Deploying on Dreamhost
This document assumes that you know how to operate the basics of the following required software. Me? I'm a Linux Systems Administrator, but a Rails deployment newb. I've decided to cut my teeth at Deployment using a mix of Dreamhost/Radiant. Things look to be going OK.
Know these tools...
- svn
- ssh
- rails
Recent Updates to Document
- 7/26/06 - Caylan
- Added recent updates, how meta!
- Added bluecloth dependency in vendor
- Modified tmp directory creation to use rake task
- 8/3/06 - Jason
- Modified bluecloth install instructions
Goals
- Passwords never in the clear
- Automated deployment via Capistrano
- A responsive web application
Todo
- user login passwords in server logs
- database.yml in svn contains database password
- first few connections after deployment get "rails application failed to start" (this could be due to one of the dispatch.fcgi hacks since it wasn't fully tested, see next bullet)
- shared hosts are not robust/stable, but this should work OK for modest needs. This recipe needs to be tested test testified.
- when dreamhost supports mongrel, we use that instead. fcgi stinks.
Ready? Go!
SSH Setup
Capistrano enjoys using SSH to connect to your server. SVN also can be forced to enjoy connections via SSH. Let's have another quick bulleted list look at what's going to be happening.
- You type "ssh example.com" and it connects without a password to your server
- You're logged into your server (from above), and you type "ssh example.com" and logs you in again. This may seem redundant, but this is what capistrano will do on the server-side when exporting your latest release for deployment.
Let's set these up right now.
Generate a set of Fairly Insecure Passphrase-less Public/Private SSH Keys
$ ssh-keygen -d Generating public/private dsa key pair. Enter file in which to save the key (/Users/joeuser/.ssh/id_dsa): Created directory '/Users/joeuser/.ssh'. Enter passphrase (empty for no passphrase): Enter same passphrase again: Your identification has been saved in /Users/joeuser/.ssh/id_dsa. Your public key has been saved in /Users/joeuser/.ssh/id_dsa.pub. The key fingerprint is: XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX joeuser@client.example.com
Copy the public key to your remote server's authorized_keys file
$ scp .ssh/id_dsa.pub joeuser@example.com: # Don't forget about the semicolon $ ssh joeuser@example.com Password: XXXXX example.com $ mkdir .ssh example.com $ cat id_dsa.pub >> .ssh/authorized_keys # Used instead of copy, JIC you have other auth keys example.com $ chmod -R go-rwx .ssh # This protects the files from other users example.com $ exit Connection to example.com closed. $ ssh joeuser@example.com # If it works, you will not be prompted for a password example.com $
On the *remote server*, let's create that "redundant" public/private keys
example.com $ ssh-keygen -d example.com $ cat .ssh/id_dsa.pub >> .ssh/authorized_keys example.com $ ssh joeuser@example.com example.com $ exit Connection to example.com closed. example.com $ exit Connection to example.com closed. $ # This shell should be your client machine
Dreamhost Panel
Login to your panel and create the following Fully Hosted domain.
- Check "FastCGI Support"
- Web Directory: /home/username/sites/example.com/current/public
Under Goodies --> Manage MySQL
- Create a pleasing to the eye username, and an ugly to the eye password
Once this is done cooking, the web directory tree will exist. In order for Capistrano to work correctly, you must remove the "current" directory.
$ ssh example.com example.com $ cd sites/example.com example.com $ rmdir current/public example.com $ rmdir current example.com $ exit
SVN Setup
We will not be creating a SVN repository from the Dreamhost account. Instead, we will access it via SSH using your dreamhost account. Create the SVN repository on the remote filesystem with the following commands.
$ ssh example.com example.com $ mkdir -p svn/example # example.com $ svnadmin create svn/example example.com $ exit
Local SVN Bootstrap
$ REPO=svn+ssh://example.com/home/joeuser/svn/example $ mkdir example $ mkdir example/trunk example/tags example/branches $ svn import -m="Initial project layout" example $REPO
Disassemble, reassemble!
$ rm -rf example $ svn co $REPO example
Radiant Code
$ gem install --include-dependencies radiant $ radiant -u example $ cd example $ rake tmp:create
TODO: If using Radiant 0.50 you must copy over a public/.htaccess file from another rails project. Type "rails foo" to generate a temporary directory where you may harvest one from.
Create a database.yml file similar to this
development: adapter: sqlite3 database: db/radiant_dev test: adapter: sqlite3 database: db/radiant_test production: adapter: mysql database: example_production username: example password: XXXXXXXX host: mysql.example.com
Remove the other database files in config
$ rm config/database.*.yml
SVN Setup
There are probably errors here, which I'll fix and announce to the list when they're made. If you edit this, please announce to the list.
$ gem install svn_conf_generator $ script/generate svn_conf $ rake svn:add $ rake svn:configure $ svn propset svn:executable "*" `find script -type f | grep -v '.svn'` public/dispatch.* $ svn commit --message="New Rails/Radiant Project"
Now we add the dependencies to the project, paste text below
$ svn propedit svn:externals vendor
Paste the following in the command above
rails http://dev.rubyonrails.org/svn/rails/tags/rel_1-1-2 radius svn://rubyforge.org//var/svn/radius/tags/rel_0-5-1/radius redcloth http://code.whytheluckystiff.net/svn/redcloth/tags/RELEASE_3_0_4
Save. Now run this command to download the dependencies
$ svn update vendor
You will also need bluecloth because of a bug(?), but it's an easy install. The bluecloth.rb file goes in your RAILS_ROOT directory (above vendor, config, etc.).
$ curl -O http://www.deveiate.org/code/BlueCloth-1.0.0.tar.gz || wget http://www.deveiate.org/code/BlueCloth-1.0.0.tar.gz (alternative) $ tar -zxf BlueCloth-1.0.0.tar.gz $ cp BlueCloth-1.0.0/lib/bluecloth.rb . $ rm -rf BlueCloth-1.0.0
Remember, whenever you add lots of files to your project, you must add to svn (and commit, and deploy).
$ rake svn:add $ svn commit
Application Server Setup
The next two sections comes from http://wiki.dreamhost.com/index.php/Capistrano. I whole-heartedly recommend that you read through the linked document, then compare to what I'm doing here. You will learn something, and probably teach me something along the way.
Uncomment in config/environment.rb
ENV['RAILS_ENV'] ||= 'production'
Modify public/dispatch.* shebang to
#!/usr/bin/env ruby
Modify public/.htaccess, add the "f" to fcgi below
RewriteRule ^(.*)$ dispatch.fcgi [QSA,L]
public/.htaccess continued... Paste in the following after "RewriteEngine? On"
RewriteCond %{DOCUMENT_ROOT}/system/maintenance.html -f
RewriteCond %{SCRIPT_FILENAME} !maintenance.html
RewriteRule ^.*$ /system/maintenance.html [L]
There are obviously hacks (to public/dispatcher.fcgi), but it's supposed to fix the "Error 500" problems.
RailsFCGIHandler.process! nil, 10
class RailsFCGIHandler
private
def frao_handler(signal)
dispatcher_log :info, "asked to terminate immediately"
dispatcher_log :info, "frao handler working its magic!"
restart_handler(signal)
end
alias_method :exit_now_handler, :frao_handler
end
Capistrano Deployment Configuration
$ cap --apply-to . $ rake svn:add
Here is what my deploy.rb looks like when everything is said/done.
set :application, "example"
set :repository, "svn+ssh://example.com/home/joeuser/svn/#{application}/trunk"
role :web, "example.com"
role :app, "example.com"
role :db, "example.com", :primary => true
set :deploy_to, "/home/caylan/sites/example.com"
set :use_sudo, false
set :checkout, "export"
# Basic radiant install
desc "Setup basic radiant installation"
task :radiant_big_red_button do
run "#{current_path}/script/setup_database -o -t #{current_path}/db/templates/styled-blog.yml production";
end
# Reaper
desc "Reaper restart for dreamhost"
task :restart do
run "#{current_path}/script/process/reaper -d dispatch.fcgi"
end
I DARE YOU TO HIT THE BIG RED BUTTON
$ svn commit -m "Primed the machine with new configuration." $ cap radiant_big_red_button $ cap deploy
Speed Things Up
Your fastcgi processes will be paged out of memory after a period of time. That is, unless you apply this hack...
$ ssh joeuser@example.com example.com $ crontab -e
A magic editor opens, insert the following
*/5 * * * * lynx --source example.com > /dev/null
Parting Words
If you have any questions, or want to beef this up with some rake tasks, let me know at caylan at mac dot com.
