idiot’s guide to linux on amazon ec2 – part 2

In Part 1 I covered how to remove the root login, create a new user, and add this user to the list of sudoers on an linux ec2 instance. In this section I will cover how I got Ruby on Rails, MySQL, Nginx and Thin working together on the Ubuntu instance.

First up, I think it’s worth taking a moment to explain what Nginx and Thin actually are, as they are maybe not as well known as the others.

Nginx is a very fast web/proxy server developed by Igor Sysoev. According to wikipedia it currently runs, amongst others, the WordPress and Github websites.

Thin is a ruby web server that “glues together 3 of the best Ruby libraries in web history”[1]:

  1. the Mongrel parser, the root of Mongrel speed and security
  2. Event Machine, a network I/O library with extremely high scalability, performance and stability
  3. Rack, a minimal interface between webservers and Ruby frameworks

Right on to the job at hand and first up was getting apt-get working!

To my surprise (but probably widely known) the Ubuntu ec2 instance did not come with apt pre-configured – unlike yum on a Fedora instance I had previously used. Instead you first have to run apt-get update to download the list of package locations. Now that we’ve done this we can get to work installing the other bit of software required.

MySQL
The first thing we need to install are the MySQL client and server. To do this run the commands:

sudo apt-get install mysql-server
sudo apt-get install mysql-client

Then you need to make sure that the root password for MySQL is set to something secure. This can be done using:

sudo mysqladmin -u root a_good_secure_password

Ruby
Now it’s time to install Ruby on Rails. First we need to install ruby, rake, rubygems, and a couple of other useful packages. The following commands should add the required binaries to your path:

sudo apt-get install rubygems
sudo apt-get install build-essential
sudo apt-get install rake
sudo apt-get install ruby-full

We can now use gem to install rails:

sudo gem install rails

As we will be using MySQL you probably also want to install the MySQL client development library in order to get the ruby gem to build/install correctly. This can be done by running:

sudo apt-get install libmysqlclient15-dev

Obviously the version of the libmysqlclient will depend on the MySQL version that you are using. Finally we can install the mysql gem by running:

sudo gem install mysql

Nginx and Thin
To install the nginx package we run the command:

sudo apt-get install nginx

Nginx then needs to be started so we run:

sudo /etc/init.d/nginx start

By default the package should also add the entries required to restart nginx if the instance is rebooted – you can always check by looking in the /etc/rcX.d directory (where X is the run-level number).

Now it’s time to install thin:

sudo apt-get install thin

Creating application config files for Thin and Nginx
It is a good idea to create config files that can be used to restart your thin clusters. To do this we use the thin config command. Now, let’s assume the app is called myapp and so we run the following command:

sudo thin config -C /etc/thin/myapp.yaml -c ~user/www/myapp --servers 3 -e production

This creates a thin config file /etc/thin/myapp.yaml that starts 3 instances of the rails application found in ~user/www/myapp using the production environment. By default it will start the first server on port 3000 and the next on 3001, and so on. Should you wish to specify the port you can supply it with the -p option, i.e. -p 6666.

You can now start your thin clients using:

sudo /etc/init.d/thin start -C myapp.yaml

It’s worth noting that if you don’t specify the -C option thin will use the config files found in /etc/thin and start the thin clients for each config file found in this directory.

As we want to use nginx as a proxy to our thin client instances we must create a nginx config file for our application. An example of such a config file is shown below:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
upstream myapp {
    server 127.0.0.1:3000;
    server 127.0.0.1:3001;
    server 127.0.0.1:3002;
}
 
server {
    listen   80 default;
    server_name example.co.uk;
 
    access_log /home/user/www/myapp/log/access.log;
    error_log /home/user/www/myapp/log/error.log;
 
    root   /home/user/www/myapp/public/;
    index  index.html;
 
    location / {
        #auth_basic "Please supply login details";
        #auth_basic_user_file /home/user/www/myapp/public/protect.passwd;
        proxy_set_header  X-Real-IP  $remote_addr;
        proxy_set_header  X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_redirect off;
 
        if (-f $request_filename/index.html) {
            rewrite (.*) $1/index.html;
            break;
        }
 
        if (-f $request_filename.html) {
            rewrite (.*) $1.html;
            break;
        }
 
        if (!-f $request_filename) {
            proxy_pass http://myapp;
            break;
        }
    }
}

Lines 1-5 set up the proxy for our thin clients that we started on ports 3000-3002. The values that you include here obviously depend on the number of clients that you started and the ports they are running on. The rest of the file is dedicated to setting up the web server with the majority of settings being pretty self explanatory, so I’ll only highlight the important bits.

First, we see that the server waits for requests on port 80 and the domain used for this site is example.co.uk (lines 8-9). It’s worth noting that hosting a subdomain, say subdomain.example.co.uk, is as easy as replacing example.co.uk in line 9 with subdomain.example.co.uk. Lines 20-23 take care of things like forwarding the real IP address to rails as well as some other set up required for https. Finally the remaining lines in the file check to see if an index.html file is available at the url specified and if so displays displays it (lines 25-28), serve static files straight up (lines 30-33), and finally if the file specified by the url does not exit on the file system it sets headers and proxies for our thin clients and passes it on.

As a side note, lines 18 and 19 that are commented out enable basic http authentication in nginx. You can uncomment out these lines if you require this feature. The password file for http auth can be generated using the apache util htpasswd – you will need to install the package that contains the htpasswd utility.

The config file (let’s call it myapp) is placed in /etc/nginx/sites-available, and finally a sim link is set up between the sites-available directory to the sites-enabled directory to enable the website:

sudo ln -s sites-available/myapp sites-enabled/myapp

That’s it. All we need to do now is restart nginx (/etc/init.d/nginx restart) and assuming your config is ok the site should now be up and running. (If nginx is already running and you want to parse the config without restarting you can always get the pid of the nginx process, ps aux | egrep '(PID|nginx)', and run sudo kill -HUP PID – in fact this is all you actually need to do to get your site up and running)

[1] The Thin homepage – http://code.macournoyer.com/thin/