A very specific post here to serve as a reminder the next time I have to do this: Setting up Ubuntu 7.04 with rails, Apache 2.2, mongrel (clustered), MySQL5, subversion, and deploying with capistrano 2.0. So bleeding edge!
Set up the server:
ssh into the server as a user with sudo privileges, and get to work...
Make sure the universe repository sources are enabled. Edit
/etc/apt/sources.listand make sure the following lines are present and uncommented:deb http://archive.ubuntu.com/ubuntu/ feisty main restricted universe deb-src http://archive.ubuntu.com/ubuntu/ feisty main restricted universe deb http://archive.ubuntu.com/ubuntu/ feisty-updates main restricted universe deb-src http://archive.ubuntu.com/ubuntu/ feisty-updates main restricted universe deb http://security.ubuntu.com/ubuntu feisty-security main restricted universe deb-src http://security.ubuntu.com/ubuntu feisty-security main restricted universeInstall everything needed to build software:
$: sudo apt-get install build-essentialInstall subversion:
$: sudo apt-get install subversionInstall apache:
$: sudo apt-get install apache2Enable the necessary apache modules:
$: sudo a2enmod proxy $: sudo a2enmod proxy_balancer $: sudo a2enmod proxy_http $: sudo a2enmod rewriteConfigure mod_proxy:
In
/etc/apache2/mods-available/proxy.conf, find the block that looks like this:<Proxy *> AddDefaultCharset off Order deny,allow Deny from all #Allow from .example.com </Proxy>And change it to look like this:
<Proxy *> AddDefaultCharset off Order deny,allow Deny from all Allow from mysite.com www.mysite.com </Proxy>Install MySQL:
$: sudo apt-get install mysql-server-5.0Create the production database:
$: mysql -u root -e 'create database myapp'Install ruby:
$: sudo apt-get install rubyInstall some header files that are need to build the gems we'll install later:
$: sudo apt-get install ruby1.8-devInstall rake:
$: sudo apt-get install rakeInstall rdoc:
$: sudo apt-get install rdocInstall gem:
The gem package installed by apt-get is old, so I install from source:
$: wget http://rubyforge.org/frs/download.php/20989/rubygems-0.9.4.tgz $: tar -xzvf rubygems-0.9.4.tgz $: cd rubygems-0.9.4/ $: sudo ruby setup.rb $: sudo gem updateInstall rails (assuming the app uses the latest version):
$: sudo gem install rails -yInstall mongrel:
$: sudo gem install mongrel -yThis command might ask to choose a version for some of the gems. I always pick the ruby version with the highest version number.
Install mongrel cluster:
$: sudo gem install mongrel_cluster -ySet up the app root:
In this example, I'm deploying an app called "myapp" to /var/www/apps/myapp as the user "justin". Adjust paths and usernames as needed.
$: cd /var/www $: sudo mkdir apps $: cd apps $: sudo mkdir myapp $: sudo chown justin myappSet up a repository:
$: cd myapp $: svnadmin create reposSet up the virtual host:
Assuming there's a domain ("myapp.com" in this example) that points to the server, here's an example of the virtual host entry:
<VirtualHost *:80> ServerName myapp.com ServerAlias www.myapp.com DocumentRoot /var/www/apps/myapp/current/public <Directory /var/www/apps/myapp/current/public> Options FollowSymLinks AllowOverride None Order allow,deny Allow from all </Directory> # Configure mongrel_cluster with 3 instances starting on port 8000 <Proxy balancer://myapp_cluster> BalancerMember http://127.0.0.1:8000 BalancerMember http://127.0.0.1:8001 BalancerMember http://127.0.0.1:8002 </Proxy> RewriteEngine On # Prevent access to .svn directories RewriteRule ^(.*/)?.svn/ - [F,L] ErrorDocument 403 "Access Forbidden" # 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://myapp_cluster%{REQUEST_URI} [P,QSA,L] # Deflate AddOutputFilterByType DEFLATE text/html text/plain text/xml 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 ErrorLog /var/www/apps/myapp/shared/log/apache2-error_log CustomLog /var/www/apps/myapp/shared/log/apache2-access_log combined </VirtualHost>Save that to
/etc/apache2/sites-available/myapp.conf, then run:$: sudo a2ensite myapp.conf
Now the server should be ready to roll (pending starting up the mongrels and restarting apache, which we'll do from the local machine).
Set up the local machine:
On the local machine, I need to install most of the same things I did on the server, plus capistrano. Assuming I've got ruby and rails installed already (hence my existing app), installing the rest will vary depending on OS. Here's how I do it on OS X with macports:
$: sudo port install subversion $: sudo port install mysql5 +server $: sudo gem install mongrel $: sudo gem install mongrel_cluster $: sudo gem install capistranoConfigure mongrel:
Assuming I want 3 mongrel instances starting on port 8000 (adjust the Proxy balancer section of the virtual host if that's not the case):
$: cd /path/to/myapp $: mongrel_rails cluster::configure -e production -p 8000 -a 127.0.0.1 -N 3 -c /var/www/apps/myapp/currentMake sure the
production:section ofconfig/database.ymlhas the proper settings for the database created in step 4 of the server setup.Mine looks like this:
production: adapter: mysql database: myapp username: root password: socket: /var/run/mysqld/mysqld.sockCreate the proper svn layout:
For subversion, I like to have the typical
myapp/trunk|branches|tagsdirectory structure.$: cd .. $: mkdir myapp.temp $: mv myapp myapp.temp/trunk $: mkdir myapp.temp/branches $: mkdir myapp.temp/tagsRemove files that shouldn't be under version control:
$: rm myapp.temp/trunk/log/* $: rm myapp.temp/trunk/db/schema.rb $: rm myapp.temp/trunk/tmp/sessions/* $: rm myapp.temp/trunk/tmp/cache/*Import into svn:
$: svn import myapp.temp svn+ssh://myapp.com/var/www/apps/myapp/repos -m "Initial import"Check out a working copy:
We're done with
myapp.temp. Check out the trunk asmyappand start using that.$: svn co svn+ssh://myapp.com/var/www/apps/myapp/repos/trunk myapp $: cd myappSet
svn:ignorewhere appropriate:$: svn propset svn:ignore "*.log" log $: svn propset svn:ignore "schema.rb" db $: svn propset svn:ignore "*" tmp/sessions $: svn propset svn:ignore "*" tmp/cache $: svn commit -m "Setting svn:ignore"Configure capistrano:
$: capify .Edit
config/deploy.rbto look like this:set :application, "myapp" set :domain, "myapp.com" set :repository, "svn+ssh://#{domain}/var/www/apps/#{application}/repos/trunk" set :deploy_to, "/var/www/apps/#{application}" role :app, domain role :web, domain role :db, domain, :primary => true set :mongrel_conf, "#{current_path}/config/mongrel_cluster.yml" desc "Restart apache" task :restart_apache, :roles => :app do sudo "/etc/init.d/apache2 reload" end # Capistrano 2.0 doesn't work with the mongrel recipes, so we need to override the defaults ourselves. # Thanks to http://thinedgeofthewedge.blogspot.com/2007/08/mongrel-and-capistrano-20.html for these tasks. namespace :deploy do namespace :mongrel do [ :stop, :start, :restart ].each do |t| desc "#{t.to_s.capitalize} the mongrel appserver" task t, :roles => :app do #invoke_command checks the use_sudo variable to determine how to run the mongrel_rails command invoke_command "mongrel_rails cluster::#{t.to_s} -C #{mongrel_conf}", :via => run_method end end end desc "Custom restart task for mongrel cluster" task :restart, :roles => :app, :except => { :no_release => true } do deploy.mongrel.restart end desc "Custom start task for mongrel cluster" task :start, :roles => :app do deploy.mongrel.start end desc "Custom stop task for mongrel cluster" task :stop, :roles => :app do deploy.mongrel.stop end endUpdate: You may have to add this if you're running Capistrano 2.1:
default_run_options[:pty] = trueExplanation here.
Run the capistrano setup:
$: cap deploy:setupMake sure capistrano is happy:
$: cap -q deploy:checkYou should see: "You appear to have all necessary dependencies installed"
Do a cold deploy:
$: cap deploy:coldNote: You only need to do a cold deploy the first time. Subsequent deploys can be done with
cap deployRestart apache:
$: cap restart_apacheNote: You do not need to restart apache after every deploy. We're only doing it here because we modified the apache config earlier.
Give the server a minute or so to "warm up", then hit http://myapp.com/ and you should have a working site. You can go ahead and commit the new files capistrano created in your app:
$: svn add Capfile
$: svn add config/deploy.rb
$: svn commit -m "Adding capistrano files"
That should be it.