Friday, August 14, 2009

Deploying a Rails Application to Dreamhost Part 2

I've created an empty Demo app in Dreamhost. I have a working Rails app locally. I need it working at Dreamhost remotely. I need a fast edit-and-deploy process. I need backups.

Assumptions, Versions:

I overload the term "Dreamhost" here to also denote the Dreamhost server that hosts all my domains and servers, including mikedll.com, demo.mikedll.com, and the hypothetical projectname.mikedll.com.

A MySQL deployment database has been created via Dreamhosts' Web Panel. Rails knows its contact info.

My demo app has been launched by Dreamhost using FastCGI, so I'll use FastCGI here (as oppose to Passenger).

Files like config/environment.rb, the logs/ directory, and the vendor/ directory don't need to be synced, but most other files do. A version control system can help with this. Beyond synchronization, it will help me recover when I break a feature. SCP and rsync won't do this. I'll use Git. Hidden Reflex says Git is better than Mercurial and compatible with Trac. Git has Github, which makes it trendy. Git also supports tiered revision, which Subversion doesn't. I've been used to tiered revisions since using AccuRev.

Syncplicity, which I've been using for nine months, will be used for semi-automatic backups.

Local and remote software versions compare as follows:

  • Operating System: Local Ubuntu 8.04.3 LTS (Hardy Heron) vs Dreamhost some variant of Linux 2.6.24
  • Ruby: Local 1.8.6 vs Dreamhost 1.8.5
  • Rails: Local 2.0.2 vs Dreamhost 2.2.2
  • RubyGems: Local 0.9 vs Dreamhost 1.3.1

My Ubuntu package manager is behind on versions for both RubyGems and Rails. Besides this problem, most of the community uses RubyGems for package management, not a default package manager.

Given that I'm on Rails 2.0.2 and Dreamhost is on 2.2.2, I anticipate version conflicts. I've encountered a similar situation before.

Develop a Plan

To avoid the version conflicts, I'll sync Rails versions. It'll be an upgrade for me.

I'll manually install RubyGems and use it to install Rails.

I will continue to use Ubuntu's package manager for non-ruby dependencies.

After that, I'll learn Git via the Git tutorial, and then put it in charge of my app.

I can test my edit-and-deploy process by making changes and then expecting to observe them on Dreamhost.

Carrying Out The Plan

I downloaded the following packages and gems into my ~/settings/packages directory.

I put RubyGems in charge.

# Remove Ubuntu's versions
sudo aptitude remove rails
sudo aptitude remove rubygems

# Install RubyGems.
# The only surprise here is that I have to create a symlink.
tar -xzf ./rubygems-1.3.5.tgz.tar -C ~/
cd ~/rubygems-1.3.5
sudo ruby setup.rb
sudo ln -s /usr/bin/gem1.8 /usr/bin/gem

I then installed some dependencies with Ubuntu. The ruby 1.8 development files may be required by the sqlite3-ruby gem. The sqlite3 development headers are definitely required. Without them, the sqlite3-ruby gem gives a "missing sqlite3.h" build error.

# Following above explanations, get sqlite3-ruby dependencies
sudo aptitude install ruby1.8-dev
sudo aptitude install libsqlite3-dev

While installing gems. I explicitly specified the Dreamhost versions. RubyGems recognized and used the downloaded .gem files, which were in the current working directory.

sudo gem install -V sqlite3-ruby --version 1.2.5
sudo gem install -V rails --version 2.2.2
cd ~/myapp
rake rails:update  # update my app's files to 2.2.2

Here, I deleted deprecated calls to cache_template_extensions= and cache_template_loading= in some config/environments/*.rb files.

I then learned and setup Git.

# On Dreamhost, create a remote repository:
mkdir -p ~/git/projectname
cd ~/git/projectname
git init

# On local laptop:
cd projectname # This is folder of my existing app that is not yet versioned
git init
git add .
git commit -m "First revisioned copy of my code."

# Still on local laptop, configure it as a clone the remote repository.
# The remote repository will then be "upstream" of this local one:
git remote add origin mrmike@mikedll.com:git/projectname # Name remote repo 'origin'
git config branch.master.remote origin               # Track remote 'origin'
git config branch.master.merge master                # ... the master branch
git push                                             # Inform 'origin' of commit

# Back to Dreamhost, we create a deployed working copy:
git clone ~/git/projectname ~/projectname.mikedll.com
cd ~/projectname.mikedll.com
git pull

My code was in deployment position. I reviewed my demo app experience to fix file permissions and configure FastCGI. I reproduce a couple steps from that experience here:

# Added this to my .bash_profile, for running rake commands
export RAILS_ENV='production' 

# Initialize the remote mysql database. Here, rails worked flawlessly.
rake db:migrate

My applicaiton should now be deployed and under version control.

Review the solution

I test my solution by editing files locally, commiting them with Git, logging into mikedll.com and pulling the changes, and viewing the website at its public URL. I expect to see my changes.

Then I test my backups by pulling changes into the Windows working copy of the central repository, letting Syncplicity see the changes and back them up to its servers, and viewing my backed up files through the Flash interface at the Syncplicity website. I expect to see the changes reflected in those files.

These tests passed. I had successfully deployed my web app to Dreamhost.

I could also use Git to send changes from Dreamhost to my laptop. This will be used when bugs are fixed on the remote production server.

git commit by definition doesn't require a password like Subversion would, but git push and git pull do. I setup private/public SSH keys to simplify this edit-and-deploy process further.

I used this result to converting my existing static website, mikedll.com, into an identically setup Rails/Git/Dreamhost configuration.

A struggle while carrying out the plan was that, due to extremely poor internet at the time, I had to manually fetch certain gems including actionpack, activesupport, activerecord, actionmailer, and activeresource. RubyGems kept timing out when trying to download them. I omitted these downloads from the above list because if my internet had been better, RubyGems would've installed them for me.

A failure while carrying out the plan was that I tried to do some things with Git that may not have made sense. Originally, I had hoped to avoid a central repository, and to instead push updates from my local laptop repository to remote and backup repositories. I couldn't get this to work, though. I might have been needlessly running from a centralized setup for the sake of being different, instead of trying to get things done.

Future and Related Work

Heroku offers special Rails hosting such that your edit-and-deploy process can consist of a single rake command.

Dealing with conflicts in Rails versions is a known problem and a popular solution is to freeze rails.

The user experiences that alerted me to the deprecations of cache_template_extensions= and cache_template_loading= were Paul Sturgess' snippets collection and the Morph Labs website, respectively.

A Stackoverflow entry showed me how to configure an existing repository as if it were created with the clone command. This avoids the dirty method of cloning a new local repository and deleting the existing repository, which I had done at first and felt embarrassingly weaksauce.

My local laptop environment is actually Ubuntu on VirtualBox, but Syncplicity only works on Windows. So, I created another windows working copy off the central one just for Syncplicity. It will be my (manual) responsibility to pull updates to this copy when I want a backup that is neither on Dreamhost's hard drives nor my own.

My edit-and-deploy process is still missing some vital steps for it to scale, but it will suffice for now. Months from now as bugs begin to pile up, how will I manage them? How will I integrate tickets and milestones? With Trac? With Lighthouse?

Seemingly, Capistrano helps with Rails deployments and many system administration tasks where you have to login to many servers, but I haven't examined it in any depth yet.

Dreamhost strongly recommends launching rails apps with Passenger instead of FastCGI. I will add that as a future todo for my project.

No comments:

Post a Comment