Setting up an AWS ec2 instance for Nginx, Django, uWSGI, and MySQL

Getting an ubuntu server set up on AWS

EC2 Setup

Log into your AWS panel and navigate into the EC2 management console. Click “Launch Instance”. Select “Classic Wizard” and then click continue.

  • Choose an AMI
  • Choose “Ubuntu Server 12.04.1 LTS” AMI and click continue.
  • Instance Details
  • Make sure that the number of Instances is set to “1” and that instance Type is set to “T1 Micro” and click Continue.
  • Under “Advanced Instance Options” leave all options defaulted and click Continue.
  • Under “Storage Device Configuration” leave all options defaulted and click Continue.
  • K/V Pairs
    • Add a Key “Name” with a value describing the app/apps that will be living on this server.
    • Add a Key “Stack” with a value that describes the application stack “django, MySQL, nginx”
    • Click Continue
  • Create Key Pair
    • Under Create a new Key Pair, enter the same value as you used in your “Name” K/V in the last step.
    • Click the “Create and Download Key Pair” link and note where this saves to on your computer (this is your .pem file referred to later).
    • Then Click Continue
  • Configure Firewall
  • Create a new Security Group
    • For “Group Name” enter “Developers”.
    • For “Group Description” enter “Anyone Collaborating”.
    • Create Rules for MySQL, SSH, and HTTP.
    • Click Continue.
  • Review
  • look over everything and click Launch.

Elastic IP

  • Navigate to the EC2 management console.
  • Under “NETWORK & SECURITY” click on “Elastic IPs”.
    • At the top of the page click “Allocate New Address”.
    • For “EIP used in” select “EC2” and click “Yes, Allocate”.
    • Select the New EIP by clicking the checkbox next to it and click “Associate Address” at the top of the page.
    • Select the EC2 instance you created in the step prior and click “Yes, Allocate”.

DNS

Do you have a domain you want to point to this IP? go to your DNS management console where the domain is registered and create an A record pointing the domain to the EIP

Domain    | Type | EIP
<vhost>   | A    | XXX.XXX.XXX.XXX

Now wait for the DNS to propagate

Configure your server

SSH

Run the following commandchmod 600 <your pem filename>.pem && ssh -i <your pem filename>.pem ubuntu@<your EIP> to ssh into your server.

Set ubuntu user’s password

Run sudo passwd ubuntu and follow the steps to change the default user’s password.

Set up the directories to run your applications

  • mkdir ~/web/ web is where the applications (vhosts) will live
  • mkdir ~/web/<vhost>/ name the vhost appropriately (usually for me www.domain.com OR dev.domain.com)
  • mkdir ~/web/<vhost>/app/ location where the project’s git will be initialized
  • mkdir ~/web/<vhost>/logs/ location of logs
  • mkdir ~/web/<vhost>/static/ location of static assets for collectstatic
  • mkdir ~/web/<vhost>/media/ location of user uploaded media

Install Packages

Enabling ubuntu’s Multiverse
  • sudo cp /etc/apt/sources.list /etc/apt/sources.list.backup backup sources list
  • sudo nano /etc/apt/sources.list and uncomment the multiverse lines that look like the following and then save.

    deb http://us-east-1.ec2.archive.ubuntu.com/ubuntu/ precise multiverse
    deb-src http://us-east-1.ec2.archive.ubuntu.com/ubuntu/ precise multiverse deb http://us-east-1.ec2.archive.ubuntu.com/ubuntu/ precise-updates multiverse deb-src http://us-east-1.ec2.archive.ubuntu.com/ubuntu/ precise-updates multiverse

  • sudo apt-get update
Python
  • install python headers sudo aptitude install python2.7-dev
  • sudo aptitude install python-setuptools
  • sudo easy_install pip
  • sudo pip install virtualenv
  • cd /home/ubuntu/web/<vhost.com>/ && virtualenv --no-site-packages venv
Install & configure GIT
  • sudo apt-get install git
  • ssh-keygen -t rsa -C "[email protected]"
  • git config --global user.name "AWS Server"
  • git config --global user.email "[email protected]"
  • git config --global core.autocrlf input
  • initialize your first vhost’s git repo: cd /home/ubuntu/web/<vhost>/app && git init

Note: don’t forget to add your public key as a deploy key on github for the repo. cat ~/.ssh/id_rsa.pub and then copy the output to your clipboard. On github.com, go to the Settings > Deploy Keys page and paste the ssh public key into the Key field and name it appropriately.

Initialize your application.

  • add a remote origin for this repo cd /home/ubuntu/web/<vhost>/app && git remote add origin [email protected]:Y/X.git
  • run the command git pull origin master to bring your remote project local
  • activate the virtualenv source /home/ubuntu/web/<vhost>/venv/bin/activate
  • cd /home/ubuntu/web/<vhost>/app && pip install -r requirements.txt

Note: If your app uses mysql as a database backend, run sudo apt-get install libmysqlclient-dev BEFORE you pip install mysql-python. This is necessary regardless of where the MySQL server resides.

Note: If you use the Python Image Library(PIL) install it’s dependencies so that when PIL is installed support for jpeg, png etc is already there. sudo apt-get build-dep python-imaging && sudo ln -s /usr/lib/`uname -i`-linux-gnu/libfreetype.so /usr/lib/ && sudo ln -s /usr/lib/`uname -i`-linux-gnu/libjpeg.so /usr/lib/ && sudo ln -s /usr/lib/`uname -i`-linux-gnu/libz.so /usr/lib/

UWSGI

I was using gunicorn for a my apps until I got a good look at some of the performance /benefits of uWSGI. Many thanks to this blogpost for the upstart script help.

First, install uwsgi globally: sudo pip install uwsgi.

Next, create a wsgi.py file for this vhost nano /home/ubuntu/web/<vhost>/wsgi.py Add the following:

import os, sys, site, django.core.handlers.wsgi

SITE_DIR = '/home/ubuntu/web/<vhost>/app/'
site.addsitedir(SITE_DIR)
sys.path.append(SITE_DIR)

os.environ['DJANGO_SETTINGS_MODULE'] = '<project>.settings'
application = django.core.handlers.wsgi.WSGIHandler() 

Note: this is also where you would add the newrelic application wrapper

Lastly, create an init script to daemonize this vhost sudo nano /etc/init/<vhostid>uwsgi.conf and add the following:

# file: /etc/init/<vhostid>uwsgi.conf 
description "uWSGI server for <vhost name>" 

start on runlevel [2345]
stop on runlevel [!2345]

respawn
exec /usr/local/bin/uwsgi --home /home/ubuntu/web/<vhost>/app/ --socket /home/ubuntu/web/<vhost>/<vhostid>uwsgi.sock --chmod-socket --module wsgi --pythonpath /home/ubuntu/web/<vhost>/ -H /home/ubuntu/web/<vhost>/venv

Note: what you name this file will effect how you refer to this application sudo service <vhostid>uwsgi restart so maybe you want to name your process production.conf or staging.conf

Lets look at the options we called uwsgi with:

  • home The home directory for uwsgi to use
  • socket The path to a socket file. We’ll give this to Nginx later.
  • chmod-socket Set the permissions on the socket so we can use it.
  • module The wsgi configuration file’s name, without extension. So, myapp_wsgi, not myapp_wsgi.py
  • pythonpath The directory in which the wsgi configuration file resides.
  • H The path to the virtualenv to use with uwsgi

NGINX

Installation

  • Install nginx sudo apt-get install nginx
  • Test to make sure that nginx starts: sudo service nginx start then stop the service sudo service nginx stop
  • Make sure that nginx starts automatically sudo update-rc.d nginx defaults Virtual Hosts

Next, create the vhost’s configuration sudo nano /etc/nginx/sites-available/<vhost.com>. (sample below)

server {
    listen 80;

    server_name <vhost>.com www.<vhost>.com;

    access_log /home/ubuntu/web/www.<vhost>.com/logs/access.log;
    error_log  /home/ubuntu/web/www.<vhost>.com/logs/error.log;

    # no security problem here, since / is alway passed to upstream
    root /home/ubuntu/web/www.<vhost>.com;

    # serve directly - analogous for static/staticfiles
    location /media/ {
        # if asset versioning is used
        if ($query_string) {
            expires max;
        }
    }
    location /static/ {
        # if asset versioning is used
        if ($query_string) {
            expires max;
        }
    }
    location / {
        uwsgi_pass   unix:///home/ubuntu/web/www.<vhost>.com/<vhost>uwsgi.sock;
        include      uwsgi_params;
    }

    # what to serve if upstream is not available or crashes
    error_page 400 /static/400.html;
    error_page 403 /static/403.html;
    error_page 404 /static/404.html;
    error_page 500 502 503 504 /static/500.html;

    # Compression
    gzip on;
    gzip_http_version 1.0;
    gzip_comp_level 5;
    gzip_proxied any;
    gzip_min_length  1100;
    gzip_buffers 16 8k;
    gzip_types text/plain text/html text/css application/x-javascript text/xml application/xml application/xml+rss text/javascript;
    # Some version of IE 6 don't handle compression well on some mime-types, 
    # so just disable for them
    gzip_disable "MSIE [1-6].(?!.*SV1)";
    # Set a vary header so downstream proxies don't send cached gzipped 
    # content to IE6
    gzip_vary on;
}
  • Enable this vhost: sudo ln -s /etc/nginx/sites-available/<vhost.com> /etc/nginx/sites-enabled/<vhost.com>
  • Restart Nginx for changes to take effect sudo service nginx restart

MySQL

This step guides you through setting up a locally hosted mysql database server if you aren’t using a dedicated database solution such as Amazon’s RDS.

  • install mysql server: sudo apt-get install mysql-server libmysqlclient-dev and set password for the root user
  • restart the mysql process: sudo service mysql restart
Written on January 14, 2013