A collection of computer systems and programming tips that you may find useful.
 
Brought to you by Craic Computing LLC, a bioinformatics consulting company.

Friday, July 31, 2009

Apache CGI scripts in arbitrary directories

In order to allow the execution of CGI scripts by Apache in an arbitrary directory you need to do two things. It's real easy but I forget the details every once in a while.

1: In the <Directory> block you need to add the ExecCGI option, to allow execution of scripts
2: Plus you need to add the AddHandler line, which defines which file extensions represent executable scripts
<Directory /var/www/>
AddHandler cgi-script .cgi
Options ExecCGI Indexes FollowSymLinks MultiViews
AllowOverride None
Order allow,deny
allow from all
</Directory>
And then restart the server.

Note that you want ExecCGI and NOT +ExecCGI - not really sure why the plus option doesn't work, but it doesn't.


 

Wednesday, July 29, 2009

Environment Variables, Rails 2.2.2 and Passenger 2.2.4

I want to pass an environment variable into my Rails app which represents the root of the server name that it will respond to (e.g. 'craic.com'). Note that this is not the default hostname of the machine. And I want to set this when I run the app on my laptop and when I deploy it to an EC2 node.

I'm running the app under Apache2 and Phusion Passenger.

Setting a UNIX shell environment variable in .bashrc, etc., doesn't work because the files are not sourced from Apache.

The way to do this is to set them within your Apache Virtual Host block using the SetEnv directive. For example, here is a vhost block for my app:
<VirtualHost *:80>
ServerName www.example.com

SetEnv FOOBAR "foobar"

DocumentRoot "/mnt/myapp/current/public"
RailsEnv development
RailsAllowModRewrite off
<directory "/mnt/myapp/current/public">
Order allow,deny
Allow from all
</directory>
</VirtualHost>
Then in your application, typically in your config/environment.rb file, you would access this as:
my_variable = ENV['FOOBAR']

This works great but ONLY with version 2.2.3 or higher of the Passenger Gem. To see what version you have installed:
$ gem list passenger
and to update:
$ sudo gem update passenger
$ sudo passenger-install-apache2-module




 

Tuesday, July 28, 2009

Rails, Git, Capistrano, EC2 and SSH

I wrote a post last year on configuring SSH to communicate with an EC2 via Capistrano. Here is a followup post that shows how to deploy a Rails app from your local machine to an EC2 node.

I'm skipping the database used in the Rails app and all the application start/restart/stop details that might involve Apache and Passenger. The focus is on getting the code copied over to EC2.

Here are the prerequisites:
1: An EC2 instance with all the necessary packages and Ruby gems needed for your app to run. That's a big topic in itself - but not for this post.
2: A client machine with Rails, git, and capistrano installed (In my case this is all on a Mac OS X 10.5 system, Rails 2.2.2, etc.)
3: A SSH key pair (see my previous post) - Note that this is NOT the same as your EC2 keypair (which you need as well)

I suggest you try creating an deploying a test app like this one before you try to deploy a real app.

1: Create a new Rails app - we'll call is 'deploytest'
$ rails deploytest
$ cd deploytest

2: Create a local Git repository for it
$ git init
$ git add *
$ git commit -a -m 'initial commit'
$ git status

3: Create a couple of Capistrano files
$ capify .

4: Edit config/deploy.rb
# The name of your app
set :application, "deploytest"
# The directory on the EC2 node that will be deployed to
set :deploy_to, "/mnt/#{application}"
# The type of Source Code Management system you are using
set :scm, :git
# The location of the LOCAL repository relative to the current app
set :repository, "."
# The way in which files will be transferred from repository to remote host
# If you were using a hosted github repository this would be slightly different
set :deploy_via, :copy

# The address of the remote host on EC2 (the Public DNS address)
set :location, "ec2-174-100-100-100.compute-1.amazonaws.com"
# setup some Capistrano roles
role :app, location
role :web, location
role :db, location, :primary => true

# Set up SSH so it can connect to the EC2 node - assumes your SSH key is in ~/.ssh/id_rsa
set :user, "root"
ssh_options[:keys] = [File.join(ENV["HOME"], ".ssh", "id_rsa")]
The only account on a default EC2 instance is root. You probably want to create a second user that is responsible for your application.

5: Copy your SSH public key to your EC2 node
$ scp -i ~/my-ec2-keypair ~/.ssh/id_rsa.pub root@ec2-174-100-100-100.compute-1.amazonaws.com:/root/.ssh/authorized_keys2
NOTE the filename authorized_keys2 - not authorized_keys!!

6: Setup the EC2 node for Capistrano deployment.
From your LOCAL machine, not the EC2 node:
$ cap deploy:setup

7: Finally, deploy your application
$ cap deploy
You will see lots of output and with this dummy application some of those will report errors/warnings. Don't worry about that for now.

8: Check that the Deployment was successful
Connect to the EC2 node with SSH the regular way, cd to the app directory and check that everything is there. If that is all working then you are ready to deploy a real application and add custom tasks for managing the database, restarting the server etc.

Bear in mind that Capistrano add new 'releases' of your software in separate directories and symlinks the 'current' directory to the latest. So the root of your deployed application is the 'current' subdirectory.


Good luck.



 

Friday, July 10, 2009

JAVA_HOME Environment variable in Ubuntu

If you want to use Java you need to set the JAVA_HOME environment variable in your .bashrc. The installer won't do it for you. But knowing what to set it to seems to vary widely between distributions.

On Ubuntu 9.04 jaunty I set mine to this:
export JAVA_HOME=/usr/lib/jvm/java-6-openjdk
Note that I did note explicity install java, instead it was a dependency of apt-get install ec2-api-tools, so other installation paths may use a different JDK.



 

Thursday, July 9, 2009

Simple Example of Customizing AWS EC2 Instances at Launch

With Amazon Web Services EC2, you fire up a new Instance (compute node) from a given AMI (machine image) and you can build custom AMIs to suit your specific needs. But there are many things that you only want to specify when you fire up your instance. And in my case there is always some stupid detail that I forgot to specify when I built the AMI.

So having a way to customize the instance at launch time is important.

One guide to doing this is by PJ Cabrera on the AWS site. This is great but it is relatively complex for many needs. Here is a simpler tutorial on the steps needed for basic customizations:

I'm using one of the EC2-tailored Ubuntu AMIs as my base. Eric Hammond provides a valuable service to the EC2 community maintaining these. Specifically I'm using an Amazon EC2 Ubuntu 9.04 jaunty AMI that I've added lots of custom packages to.

In /etc/init.d you'll find several init scripts that start with 'ec2-'. ec2-run-user-data is the one that matters here, written by Eric Hammond. This will run automatically on start up and look for a user-supplied script at the URL http://169.254.169.254/2008-02-01/user-data. This is not a generally accessible URL and has a special role in EC2.

If ec2-run-user-data finds a file at this URL that begins with the characters '#!' it assumes that this is an executable script and executes it. You can create this script and have it set environment variables, run other scripts, etc.

This custom script actually comes from your desktop machine and you specify it when you start up a new EC2 instance. Somehow in the innards of EC2 the file is uploaded and is made available to your instance only via this special URL.

Here is how I fire up a new instance from my desktop:
$ ec2-run-instances -k mykeypair -f ec2_custom_script.sh -t c1.medium -z us-east-1a ami-12345678
You specify your file after the -f flag and it needs to be an executable script file. Bash, Perl, Ruby, etc. should all be fine as long as your AMI has that interpreter installed.

So what goes into a launch script? One common use is to set up your AMAZON_SECRET_ACCESS_KEY and AWS_ACCESS_KEY_ID keys that you need for accessing S3, etc. You don't want to hard wire these into your AMI, but you do need these to be available when you login to an instance. So in my launch script I add these to my .bashrc file which is loaded when I actually login. I also create a couple of custom mount points this way where I can mount EBS volumes.

Here is a simple launch script:
#!/bin/bash
# Simple custom EC2 launch script
mkdir /mnt/craic
mkdir /mnt/data
BASHRCFILE=/root/.bashrc
AMAZON_SECRET_ACCESS_KEY=<yourkeygoeshere>
AWS_ACCESS_KEY_ID=<yourkeygoeshere>
echo "export AMAZON_SECRET_ACCESS_KEY=${AMAZON_SECRET_ACCESS_KEY}" >> ${BASHRCFILE}
echo "export AMAZON_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID}" >> ${BASHRCFILE}
# Need this for Rails on Ubuntu
echo "export PATH=/var/lib/gems/1.8/bin:$PATH" >> ${BASHRCFILE}
The great thing about Eric's ec2-run-user-data script is that it is all set up and ready to go. Pass it a valid script and it should just work.

Note that there is a limit of 16kb the size of launch files using this mechanism. That is quite a lot for a script but you still want to be frugal. If you need more than this then put additional steps into one or more secondary scripts and have your primary launch script fetch these from S3 and execute them.

Also note that you cannot specify a file when you launch an instance using the AWS Management Console. You should be able to cut and paste in the contents of your script into the user-data field in the advanced options, but this is an ugly way to do it. You may be best off to use the command line ec2-run-instances command as shown above. I'm not sure if other interfaces like ElasticFox can handle this.

You can get fancy by rolling your own /etc/init.d scripts and passing a compressed zip file, etc., to it via this mechanism. But I've found that a pain in the neck to troubleshoot when I've had issues. As long as you are using Ubuntu the simple approach is the way to go.

 

Installing Rails on Ubuntu - Path Problem

There is an annoying issue when installing recent versions of Rails on Ubuntu (and possibly other Linux variants).

You install the necessary gems but when you try to run rails you get this:
# rails -v
The program 'rails' is currently not installed. You can install it by typing:
apt-get install rails
-bash: rails: command not found
You can see that the gem is installed:
# gem list rails
*** LOCAL GEMS ***
rails (2.3.2, 2.2.2)
So where is it? Turns out you need to add this link to your PATH variable (in your .bashrc file for example):
# export PATH=/var/lib/gems/1.8/bin:$PATH
That does the trick:
# rails -v
Rails 2.3.2
You will find mention of this on page 25 of the 3rd Edition of 'Agile Web Development with Rails' but it needs to be more widely known. Gotchas like this that just prevent you from doing anything with a piece of software are BAD NEWS - someone make it go away...

 

Monday, July 6, 2009

Passing Arguments to Rake with a Rails application

In order to pass arguments to a Rake task you specify them as named arguments in the task definition and then pass the actual values in square brackets following the rake task on the command line.

Here is a simple example:
task :mytask, [:word0, :word1] do |t, args|
puts "You typed: '#{args.word0}' and '#{args.word1}'"
end

You run this from the command line thus:
$ rake mytask[hello,world]
You typed: 'hello' and 'world'

Interacting with a Rails app involves adding a dependency of :environment. Add this after the array of arguments like this:
task :mytask, [:word0, :word1] => :environment do |t, args|
puts "You typed: '#{args.word0}' and '#{args.word1}'"
end
The syntax looks weird but it works.


 

Archive of Tips