Simple Web Applications Deployment via Git

Git is not only great version control tool,  but can be easily used for web application deployment to testing or production environmenst.  For more complex projects some continuous integration (CI), tools/services can be more appropriate  (like Jenkins), but for smaller project we can do just fine with Git , SSH and simple script installed as git hook. Below is the scenario I’m using for one Python Flask web application.

Flask application is deployed on Debian server (running in VM), in uwsgi container behind nginx frontend (nginx is also serving static files).   We have two VMs –  one production and one testing. If necessary production instance can be easily scaled horizontally  by adding more uwsgi instances behind nginx frontend.

Git Flow

We have two long running branches in Git:
devel – for latest development version
master – for production version

Developers work on feature branches, which they can run locally (with Flask embedded WSGI server),  when  features are ready, they are merged to devel branch and  devel branch is then pushed to test instance, where all new features are tested by users.  When we are fine with new functionality,  devel branch is merged to master branch and master branch is pushed to production instance.

Server Setup

This is setup for testing server,  same is for production server,  only that branch for production is master.

1. Install Git

apt-get update
apt-get install git

2. Create bare repo

mkdir -p /var/repo/app.git
cd /var/repo/app.git
git init --bare

3. Add remote

git remote add origin https://our.repo.server.com/repo/app.git

4. Fetch branch and check it out

git fetch origin devel:devel
mkdir /opt/app
git --work-tree=/opt/app --git-dir=/var/repo/app.git checkout -f devel

Now we are ready to run the Flask application, just need an usual setup for Flask:

Server setup – specific for Flask and Python

1. Install base dependencies

apt-get install -y build-essential python python-dev python-pip uwsgi uwsgi-plugin-python nginx-full python-nose 
# optionally you might like to get newer uwsgi - compile it from source

2. Create  uwsgi configuration for your application /etc/uwsgi/apps-available/app.ini

[uwsgi]
buffer-size=32768
socket = /tmp/app.uwsgi
plugin = python
chdir = /opt/app/src #whatever is your main Flask module
master=true
workers=1
threads=2
module = server # whatever is main module name
callable = app
enable-threads=true

And symlink it to /etc/uwsgi/apps-enabled.  Indeed for production configuration you might need other parameters, especially workers and threads.

3. Create site configuration for nginx – /etc/nginx/sites-available/app-nginx

server {
	listen   80 default_server; ## listen for ipv4; this line is default and implied
	listen   [::]:80 default_server ipv6only=on; ## listen for ipv6

	root /opt/app/src;
	index index.html index.htm;

	# Optionally server name
	# server_name some.server.com;

	location /static/ {   # assuming this is location of static files
		# static app files
		try_files $uri $uri/ 404;
	}

	location / {
		include uwsgi_params;
		uwsgi_pass unix:/tmp/app.uwsgi;
	}
}

Remove default nginx site and symlink this to /etc/nginx/sites-enabled.

4. Install necessary python dependencies for you application

# assuming that your project has all dependencies in requirements.txt file
# which is the best practice
pip install -r /opt/app/requirements.txt

5. Restart servers

service uwsgi restart
service nginx restart

 Server Setup – Git hook

Create following file in /var/repo/app.git/hooks/post-receive

#!/bin/bash
set -x -e

BRANCH=devel
WD=/opt/app

REFS=`cat`

if [[  $REFS =~ "refs/heads/"$BRANCH ]] ; then
	git --work-tree=$WD --git-dir=/var/repo/app.git checkout -f $BRANCH
	pip install -r $WD/requirements.txt
	cd $WD
	nosetests
	uwsgi --reload /var/run/uwsgi/app/app/pid
	service nginx  reload
	echo "Succesfully deployed $BRANCH branch"
else
	echo "This is not our branch: $REFS"
        exit 0
fi

This script checks if correct branch is pushed. Eventually it can be easily extended to deploy different branches to different locations.

Client setup

Assuming you already have cloned devel branch to your computer and worked on it.

1. Assure that your SSH public key is set on server

cat .ssh/id_dsa.pub | ssh root@test.server.com "cat >> .ssh/authorized_keys"

2. Add remote repository to your cloned repo

git remote add test-server ssh://root@test.server.com/var/repo/app.git

3. When you finished you can just push changes to test server

git push test-server devel

and they will be automatically deployed.

Leave a Reply

Your email address will not be published. Required fields are marked *