EC2 Deployment Part 2:





Overview

In the previous tutorial, Part 1, we created an EC2 instance on AWS, then we logged into our instance over ssh and installed the nginx server. In this installment we will install the rest of the software that we need on our instance to host our site. I am still following another, very similar, tutorial that was written a few years ago. There are a few differences in our approach, particularly at this stage and, of course, a lot has changed even in just a few years so I wrote this tutorial to provide an updated guide.

We saw last time how to log into our instance remotely and run nginx to display the default splash page on port 80. The first thing we will do now is install virtualenvwrapper and Python3, with pip, which will enable us to easily setup the rest of our software, e.g. uWSGI and Django.

Install Python3, virtualenvwrapper and pip3

When you log into your EC2 instance we should first take a look at which versions of Python are currently available.

$ python --version
$ python3 --version

When I run those commands I am told that Python 2.7.14 is installed and there is no version 3 of Python available. To install Python3 you could use the Amazon Linux Extras, like we did for nginx. I ran into problems with this approach as it would install Python 3.6 but does not include the python-devel development tools package that we will need later for installing uWSGI. So instead just use yum to install the Python 3.7.0rc1, the corresponding python-devel package and pip system wide (apparently the rc1 at the end of the Python version indicates that it is still in development but is considered to be "almost done").

$ sudo yum install python3-devel.x86_64

We can now use pip to install virtualenvwrapper. As the name suggests this is just a program that wraps virtualenv. virtualenv provides a way to create virtual environments for Python packages. This way you can create an environment for running Django apps with a specific version of whatever packages you want to use, e.g. Django version 2.0. This is then isolated from the system wide Python package installations. I will assume from this point on that the reader is familiar with virtualenv and virtualenvwrapper and their use. If you are not there a many very useful tutorials out there on how and why to use them, for virtualenvwrapper use see this

To install virtualenvwrapper with pip run

$ sudo pip3 install virtualenvwrapper

From the virtualenvwrapper installation documentation we learn that we need to add the following lines of code to our ~/.bashrc file in order to activate the program. The ~.bashrc file is "sourced" every time a user logs in. This provides a way for the OS to initialize some user specific environment variables that will allow us to utilize virtualenvwrapper. Add the following lines (below # Added for virtualenvwrapper) to your ~/.bashrc file:

# .bashrc

# Stuff already here by default
...

# User specific aliases and functions

# Added for virtualenvwrapper
export WORKON_HOME=$HOME/.virtualenvs
export PROJECT_HOME=$HOME/Devel
# change to version of Python to the Python3 we just installed
export VIRTUALENVWRAPPER_PYTHON=/usr/bin/python3
source /usr/local/bin/virtualenvwrapper.sh

This is a slightly modified version of the lines suggested by the virtualenvwrapper installation documentation. We have modified the line export VIRTUALENVWRAPPER_PYTHON=/usr/bin/python3 because we want to use the new Python3 we installed (3.7) and not the default system version at /usr/bin/python, which is the original Python2.7 version.

With these additions made to the .bashrc file, either log out and back in or just run the following command to source the file and make the updates.

$ source ~/.bashrc

This should enable virtualenvwrapper. Test the installation by creating your first virtual environment and activating it.

$ mkvirtualenv djangoEnv

Install and test Django

Once you have activated your virtualenv you should see (djangoEnv) prepended to your terminal user name, correspondingly I will indicate terminal commands that should take place within the virtualenv by starting code snippets with a (djangoEnv)$ from now on. Though I named the new virtual environment "djangoEnv" it currently doesn't have any packages installed; this can be verified with (djangoEnv)$ pip freeze listing nothing. Lets now install Django version 2.0 into our djangoEnv environment.

(djangoEnv)$ pip install django==2.0

Running (djangoEnv)$ pip freeze should now show that Django==2.0 and a dependency, pytz, are installed.

To test Django we will create a simple project and use the Django development server to check if we can access the "website" with an outside browser. First create the project "testProj", I made mine in my home directory ~/ aka /home/ec2-user/, so my main Django project directory is /home/ec2-user/test_project.

(djangoEnv)$ cd ~
(djangoEnv)$ django-admin startproject testProj
(djangoEnv)$ cd testProj

Now run the development server on port 8000.

(djangoEnv)$ python manage.py runserver 0.0.0.0:8000

Now lets perform a simple test to make sure that we can connect succesfully to our EC2 instance from the outside world. We can do that by using our local web browser to send a request to our EC2 instance on port 8000. There are two issues standing in the way or our test though.

First we need to add the EC2 instance to the list of allowed hosts for our Django project. From our main Django project directory open the testProj/settings.py file in a text editor of your choice. Then modify the line ALLOWED_HOSTS list:

# settings.py

ALLOWED_HOSTS = [
      '<instance IP>',
]

Note that the IP address is a string.

Next we need to revisit our AWS console and adjust the security settings to allow incoming connections on port 8000. Log into your console, go to the EC2 section and then the instance information page (where we got the Public DNS/IP in the previous tutorial). On this page follow the link for your security group to the right of the "Security Groups" line, mine is named "launch-wizard-1". Then use the drop-down "Actions" menu to select "Edit inbound rules". Finally add a "Custom TCP Rule" and specify port 8000, I also limited the source to my local IP.

With the allowed hosts and the instance security setting changed you should now be able to access that port by using your web browser to go to

<instance IP>:8000

Once there you should see the Django splash page.


Django development server splash page indicates successful installation and function of Django. ___

Install and test uWSGI

nginx is capable of serving static content but it isn't capable of directly interacting with our Django project. The purpose of uWSGI or any WSGI (web server gateway interface), at least in this context, is to act as an intermediary between the nginx web server and our Django website. A much more thorough explanation of the interactions of web servers, applications servers and web applications is available on the uWSGI site and here. As suggested by the deployment tutorial we have been following so far we will now install uWSGI following the official uWSGI installation and testing tutorial, which conveniently features Django and nginx, very closely.

Though uWSGI is a Python package we will be installing it system wide, rather than in the djagoEnv environment. Before we can install uWSGI we must have a C compiler installed, here we will use the gcc compiler (which I thought was already installed on the Amazon Linux AMI, and may already be on yours).

$ sudo yum install gcc

Then deactivate the djangoEnv virtual environment, with (djangoEnv)$ deactivate, and perform the system wide installation of uWSGI (at the time of this writing uWSGI version 2.0.17.1 was installed) with pip3:

$ sudo pip3 install uwsgi

Now we can copy the test script provided by the uWSGI tutorial to temporarily use uWSGI as our webserver and verify that it is functioning properly. First create a file ~/test.py and copy this code into it

# test.py

def application(env, start_response):
    start_response('200 OK', [('Content-Type','text/html')])
    return [b"Hello World"]

Then we can run uWSGI, again on port 8000, to verify that it is working properly with the following command.

$ uwsgi --http :8000 --wsgi-file test.py

Then when you visit your site at port 8000, as we have before to ensure that Django was working properly, you should see only the text "Hello World". As the tutorial mentions this implies that our browser is successfully interacting with the EC2 instance at port 8000 and that uWSGI is receiving an HTTP request and responding appropriately.

Finally we can test that uWSGI and Django are able to successfully interact, i.e. use uWSGI as a server for your testProj Django application. Run the following commands from the main directory of the testProj.

$ cd ~/testProj
$ workon djangoEnv
(djangoEnv)$ cd ~/testProj
(djangoEnv)$ uwsgi --http :8000 --module mysite.wsgi --venv $VIRTUAL_ENV

With the command --module mysite.wsgi we are using the setting/wsgi.py file within the Django project to direct uWSGI rather than our test.py file from earlier. Importantly wsgi.py includes some modules from Django. Since uWSGI is installed system wide and Django is installed in a virtualenv we need to show uWSGI where the packages that we want to use (i.e. Django) are within the virtualenv, this is what the --venv $VIRTUAL_ENV argument does.

If you visit the site with your browser at port 8000 you should see the same Django splash page that we saw earlier when testing Django alone.

Conclusion

In this tutorial we installed virtualenvwrapper, Python, Django and uWSGI on our EC2 instance. We also confirmed that, at least in isolation, Django and uWSGI were performing as expected and returned HTTP reponses on port 8000. In the next installment of this tutorial we will use our web server, nginx, in concert with uWSGI and Django to fully deploy our test project.