Continuous Integration and Deployment with Drone, Docker, Django, Gunicorn and Nginx - Part 3
Recently updated on
The Introduction
This is the third and final part of a multi-part tutorial covering a simple(ish) setup of a continuous integration/deployment pipeline using Drone.io:0.5. Since Part 2, Drone.io:0.8 has become available. This new version boasts much better documentation and is comparably much easier to set up than Drone.io:0.5 and even outlines how to set up your server behind NGINX.
In parts 1 and 2 of this series we:
- Set up our Drone server
- Added a
.drone.yml
to our Django application and configured it to tell Drone to run our application’s test suite - Added a condition on our master branch to only allow passed build to be merged
- Added a publish step to our
.drone.yml
to be triggered on merges to publish an updated version of our application’s code to Dockerhub - Created a systemd service to manage our application’s Docker container on our EC2 instance
- Created a
deploy.sh
script on our EC2 instance that stops and removes our app’s Docker container, pulls an updated image for the container from Dockerhub and then restarts the systemd service managing the container. - Lastly, we added a deploy step to our
.drone.yml
to ssh into our EC2 container and run thedeploy.sh
script.
The last remaining piece of infrastructure for a successful deployment is to allow the outside world the ability to communicate with our Django application’s Docker container. This tutorial will cover:
- Setting an Elastic IP to our EC2 instance
- Registering a domain
- Installing and configuring NGINX to forward traffic to our Django application’s Docker container
Step 1: Set an elastic IP to your EC2 instance
By default, your EC2 instance will be assigned an IP address. However, if you restart your EC2 instance, your instance will be assigned a different IP address. This is problematic if you want to tie a domain name to a EC2 instance in a more permanent fashion. Elastic IPs help address this situation.
An Elastic IP address is a static IPv4 address designed to allow you to easily reassign the IP address from one EC2 instance to another, without a DNS change. While setting one to your EC2 instance isn’t necessary, it’s a good idea to set the domain you will be registering in step 2 to point to this address so that if your EC2 instance is restarted or you want to associate that address to a different EC2 instance, you won’t have to update your DNS settings.
To assign an Elastic IP to your instance, in the EC2 Service section of the AWS console, navigate to the Elastic IPs
section under Network & Security
. From there, simply click on the Allocate new address
button at the top of the page and you should see a new Elastic IP address appear in the list of available addresses. Right click on your new IP and click “associate address.” This will open up a screen where you can tie your address to your application’s EC2 instance.
Now that you have tied the address to your instance, we can now register a domain!
Step 2: Registering a domain
In order to set up NGINX properly, we will need a domain name (if you already have a domain registered and have associated it with your EC2 instance, skip to Step 3). For this tutorial, I will be using Amazon Route 53 as the domain registrar and to route traffic to our EC2 instance. Registering a domain with AWS will cost you $12 a year per domain. Feel free to use a different registrar as the steps should roughly be the same.
In your AWS console and in the services
tab, click on Route 53
to go to the Route 53 dashboard. Under the Domains
section, click on Registered domains
and you should see a list of all currently registered domains. Register a domain with the Register Domain
button and follow the displayed steps to register a domain.
After your domain is registered, click on the Hosted zones
tab. For your newly registered domain we want to create a new hosted zone via the Create Hosted Zone
button at the top of the dashboard. Enter in your domain name and make sure the Type
of the hosted zone is set to Public Hosted Zone
. Once your hosted zone is created, we need to create a couple of “A” records that should look something like this:
Name | Type | TTL | Value |
---|---|---|---|
* | A | 1h | 34.237.301.14 |
www | A | 1h | 34.237.301.14 |
Now that we have a DNS record associating a specific domain name with our EC2 instance, we need to install and configure NGINX in order to forward that traffic on to our Django application’s Docker container.
Step 3: Installing and configuring NGINX
Now that you have an elastic IP pointed at your application’s EC2 instance and your domain’s DNS configuration is set up to use that IP, we need to install NGINX on your EC2 instance and configure it to correctly forward HTTPS traffic to your application’s Docker container.
With the exception of having to set up a firewall (which isn’t necessary thanks to AWS’ security groups
), this Digital Ocean tutorial outlining how to install NGINX is very good.
Once NGINX is installed on your instance, open up an editor (such as Vim or Nano) and make a new file inside of /etc/nginx/sites-enabled/your_site
. While I’ve provided both a site configuration that allows for either HTTP or HTTPS traffic, I recommend you use the HTTPS configuration, which provides a secure/encrypted connection between your server and a client’s browser. You can find plenty of good tutorials on how to install SSL certs on the web. I recommend Let’s Encrypt certificates from certbot (Imaginary’s LetsEncrypt installation tutorial , Dgitial Ocean’s tutorial).
HTTP configuration (not recommended)
# Your sites' conf.
upstream web {
server 127.0.0.1:8000;
}
server {
listen 80;
server_name <your_domain>; # example.com www.example.com;
charset utf-8;
#Max upload size; Adjust to your preference
client_max_body_size 75M;
location / {
proxy_pass http://web;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
HTTPS configuration (Recommended)
upstream web {
server 127.0.0.1:8000;
}
server {
listen 80;
server_name <your_domain>;
# Redirect non-https traffic to https
return 301 https://$host$request_uri;
}
server {
# SSL configuration
listen 443 ssl http2;
server_name <your_domain>;
charset utf-8;
client_max_body_size 75M;
location / {
proxy_pass http://web;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location ~ /.well-known {
allow all;
root /var/www/html;
}
ssl_certificate /etc/letsencrypt/live/<your_domain>/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/<your_domain>/privkey.pem; # managed by Certbot
}
The “web” in the upstream web
block can be set to anything you want (ex: upstream app
), just be sure to keep that name consistent with what is passed in the proxy_pass
line (ex: proxy_pass http://app
). This block allows you to specify multiple web applications to handle traffic for a specific domain (see the NGINX documentation for more information).
Save your file and restart NGINX:
nginx restart
If there are any errors in your config, NGINX will complain. If the restart is successful, then simply try to visit your site via your favorite browser!
http(s)://your_domain
If you only see the NGINX splash page, then double check that your site’s configuration is setup correctly ( /etc/nginx/sites-enabled/your_site
).
If you see content related to your site, congratulations! You’ve correctly configured NGINX to serve traffic to your dockerized Django application. With that, you now have a fully deployed dockerized Django application on an EC2 instance that automatically updates itself when new code is merged into your master branch!