DEV Community

Cover image for Django Continuous Deployment to PythonAnywhere
Tomas Fernandez for Semaphore

Posted on • Updated on • Originally published at semaphoreci.com

Django Continuous Deployment to PythonAnywhere

This is a streamlined version of my original Python Django tutorial. Here, I want to focus on the continuous deployment stage, so you may get your website online quicker. If, after reading this, you are curious about multi-branch deployments and continuous integration, be sure to check out the original article. I hope you enjoy it. 🍭

In this hands-on guide, we're going to learn how to deploy a Django website to PythonAnywhere using Semaphore Continuous Integration and Delivery (CI/CD). No matter how reliable the code is, we still need to implement CI/CD to detect and remedy errors quickly. When we have confidence in the accuracy of our code, you can ship updates faster and with fewer mistakes.

Things You’ll Need

You’ll need GitHub and Semaphore accounts. I’d recommend installing Semaphore’s commad line tool for an easy setup.

To get started, fork, clone the Django demo project and add it to Semaphore:



$ cd semaphore-demo-python-django
$ sem init


Enter fullscreen mode Exit fullscreen mode

The application you just forked is a simple task manager; we can create and edit tasks, we also have a separate admin site to manage users and permissions. The website is built with Python and Django. The data will be stored in MySQL.

A Note About Continuous Integration

Continuous Integration is a programming discipline in which the application is built and tested each time there is a push. By making multiple small changes instead of a big one, problems are detected earlier and corrected faster. Such a paradigm, clearly, calls for an automated system to carry out all the steps. In such systems, code travels over a path—a pipeline—and it must pass an ever-growing number of tests before it can reach the users.

The project includes a complete CI pipeline with a complete suite of tests. No setup required:

The in-depth explanation about the CI stage can be found on the original full-length tutorial

Deploy to PythonAnywhere

Websites are meant to run on the internet; let see how we can publish our site the world to enjoy. PythonAnywhere is a hosting provider that, as the name suggests, specializes in Python. In this section, we'll learn how to use it.

Create the Website

Head to PythonAnywhere (PA) and create an account. In order to do an automated deployment, we need SSH access, and that requires a paid account, so sign up for the entry-level tier (hacker 🎩).

Once you have your account, create the services for the application:

  1. Create a MySQL database called pydjango_production with a good password.
  2. Generate an API Token.
  3. Add your public SSH key to PA servers:


$ ssh-copy-id YOUR_PA_USERNAME@ssh.pythonanywhere.com


Enter fullscreen mode Exit fullscreen mode

Create an environment file for your application: .env


 bash
# This value is found on PythonAnywhere Accounts->API Token.
export API_TOKEN=YOUR_API_TOKEN

# Django Secret Key - Use a long random string for security.
export SECRET_KEY=A_RANDOM_STRING_FOR_DJANGO

# These values can be located on PythonAnywhere Databases tab.
export DB_HOST=YOUR_PA_USERNAME.mysql.pythonanywhere-services.com
export DB_USER=YOUR_PA_USERNAME
export DB_PASSWORD=YOU_DB_PASSWORD
# The name of the DB is prefixed with USERNAME$
export DB_NAME='YOUR_PA_USERNAME$pydjango_production'
export DB_PORT=3306


Enter fullscreen mode Exit fullscreen mode

Scp the file to the server:



$ scp .env YOUR_PA_USERNAME@ssh.pythonanywhere.com:~


Enter fullscreen mode Exit fullscreen mode

Now we're ready to create the website. Luckily for us, there is an official helper script. If you wish to use a custom domain instead of the default one (USERNAME.pythonanywhere.com), add a --domain= option.


 bash
$ ssh YOUR_PA_USERNAME@ssh.pythonanywhere.com
$ source ~/.env
$ pa_autoconfigure_django.py --python=3.7 YOUR_GITHUB_REPO_URL


Enter fullscreen mode Exit fullscreen mode

The script should take a few minutes to complete. Take a cup of coffee, and don't forget to stretch 🧘🏽.

Once done, there’s only one thing left to do. We need the app to have access to our environment file. Edit the WSGI file for your new website, by default it’s located at /var/www. Add three lines to that file, as shown below:


 python
# This file contains the WSGI configuration required to serve up your
# Django app
import os
import sys

# Add your project directory to the sys.path
settings_path = '/home/YOUR_PA_USERNAME/YOUR_PA_USERNAME.pythonanywhere.com'
sys.path.insert(0, settings_path)

# Set environment variable to tell django where your settings.py is
os.environ['DJANGO_SETTINGS_MODULE'] = 'pydjango_ci_integration.settings'

# -–-> ADD THESE NEXT THREE LINES TO YOUR WSGI.PY <-------
from dotenv import load_dotenv
env_file = os.path.expanduser('~/.env')
load_dotenv(env_file)
# --------------------------------------------------------

# Set the 'application' variable to the Django wsgi app
from django.core.wsgi import get_wsgi_application
application = get_wsgi_application()


Enter fullscreen mode Exit fullscreen mode

That’s it, after reloading the website, it should be online 🥇.

Benefits of Continuous Deployment

Deployment is a complex process with a lot of moving parts. It would be a shame if, after painstakingly testing everything, the application crashes due to a faulty deployment.

Continuous Deployment (CD) is an extension of the CI concept; in fact, most integration tools don't make a great distinction between CI and CD. A CD pipeline performs all the deployment steps as a repeatable, battle-hardened process.

Deployment with Semaphore

We’re going to create a new pipeline to deploy the updates automatically on each update.

The deployment process needs some secret data, for example, the SSH key to connect to PythonAnywhere. The environment file also has sensitive information, so we need to protect it.

Storing secrets in Semaphore is easy as pie:



$ sem create secret ssh-key \
    -f $HOME/.ssh/id_rsa:/home/semaphore/.ssh/id_rsa_pa


Enter fullscreen mode Exit fullscreen mode

Now do the same for the environment file:



$ sem create secret env \
    -f ./.env:/home/semaphore/.env


Enter fullscreen mode Exit fullscreen mode

Create a new pipeline file at .semaphore/deploy.yml with the next three code boxes:


 yaml
version: v1.0
name: Deploy Django to PythonAnywhere
agent:
  machine:
    type: e1-standard-2
    os_image: ubuntu1804


Enter fullscreen mode Exit fullscreen mode

The pipeline has only one “Deploy” block. It begins by invoking the secrets we just created: env and ssh key. We can also define environment variables at this point, adjust the values for your user:


 yaml
blocks:
  - name: Deploy
    task:
      secrets:
        - name: env
        - name: ssh-key
      env_vars:
        - name: SSH_USER
          value: YOUR_PA_USERNAME


Enter fullscreen mode Exit fullscreen mode

The job uses checkout to clone the repository, then envsubst expands the values of deployment.sh with the corresponding environment values. Finally, the files are copied to PA and the deployment script executed remotely:


 yaml
      jobs:
        - name: Deploy to PythonAnywhere
          commands:
            - checkout
            - cat deployment.sh | envsubst > ~/deploy.sh
            - chmod 0600 ~/.ssh/id_rsa_pa
            - ssh-keyscan -H ssh.pythonanywhere.com >> ~/.ssh/known_hosts
            - ssh-add ~/.ssh/id_rsa_pa
            - scp ~/.env ~/deploy.sh ${SSH_USER}@ssh.pythonanywhere.com:~
            - ssh ${SSH_USER}@ssh.pythonanywhere.com bash deploy.sh


Enter fullscreen mode Exit fullscreen mode

I mentioned deployment.sh, but haven't shown it yet. Here it is:


 bash
# pull updated version of branch from repo
cd ${SSH_USER}.pythonanywhere.com
git fetch --all
git reset --hard origin/$SEMAPHORE_GIT_BRANCH

# perform django migration task
source ~/.env
source ~/.virtualenvs/${SSH_USER}.pythonanywhere.com/bin/activate
python manage.py migrate

# restart web application
touch /var/www/${SSH_USER}_pythonanywhere_com_wsgi.py


Enter fullscreen mode Exit fullscreen mode

In short, the script does 3 things:

  1. Updates the app code from the repository.
  2. Executes manage.py migrate, in case there new code has additional tables.
  3. Restarts the web application.

Now all that remains is to link the pipelines. This is achieved adding a promotion to the end of .semaphore/semaphore.yml:


 yaml
promotions:
  - name: Deploy
    pipeline_file: deploy.yml


Enter fullscreen mode Exit fullscreen mode

Push all the updated files to your repository:


 bash
$ git add .semaphore/*
$ git add deployment.sh
$ git commit -m "add deployment"
$ git push origin master


Enter fullscreen mode Exit fullscreen mode

Semaphore will start working immediately. Wait until the CI pipeline is done:

Alt Text

And hit the Promote button to start the deployment:

Alt Text

Enjoy your new website.

Alt Text

Conclusion

We've discovered the incredible potential of a CI/CD platform. I hope that the tools and practices discussed here can add value to your projects, improve your team effectiveness, and make your life easier.

For the next steps, I suggest browsing Semaphore Blog for more examples and tutorials, and, of course, writing a pipeline for your own application. Good luck!

Did you find the post useful? Let me know by ❤️-ing or 🦄-ing below! Do you have any questions or suggestions for other tutorials? Let me know in the comments. Thank you for reading!

Top comments (0)