Photo by Vishnu R Nair on Unsplash

Simple CI/CD with CRON

Matt Gosden
4 min readMar 8, 2019

--

What is CI/CD?

Continuous Integration / Continuous Deployment (CI/CD) is a great timesaver when building apps

  • The tests are run automatically when new code is committed to the git repository
  • If the tests pass then it builds the latest version and pushes it out to update the live app

Simples! This saves time and is great for teams so we can have a latest ‘alpha’ version always up to play with.

CI/CD may be overkill for small projects

CI/CD can save us time on small projects. Currently I have to hop onto the server each time I push up a change, pull down the latest updates, run migrations, restart the server, and so on. It’s only a 5-10 minute job each time. But it is a hassle and requires a bunch of passwords and codes.

However full CI/CD is overkill for small projects in my view because

  1. CI/CD tools often work best using Docker, and I may not have my project Dockerised yet
  2. You want a good decent test suite that is passing — and for development projects I might not have that in place yet
  3. Configuration takes quite a few hours to get it set up for people like myself that don’t do it very often

So is there a simpler way of doing this?

How CRON can help

This is a simple workaround using cron which saves me having to hop onto the server and will continuously deploy my code. It takes about 15 mins to set up opposed to half a day faffing around with Docker and CI tools for full CI/CD.

The idea is simple

  • We use git to deploy the app on the cloud server
  • The version of code we want deployed is the master branch — I can work on development on my own machine, and when I want that deployed I merge it into master so that it will be automatically deployed
  • Testing is my responsibility — as I am the only developer I commit to myself that I will make sure the tests pass before I merge into master!
  • On the server the cron scheduler regularly pulls down the latest master branch, does the migrations and restarts the server

How to set up

Example project

This is a trading bot that is built around the django web framework. It is deployed on an Ubuntu server in the cloud (DigitalOcean), and uses gunicorn and nginx.

This process applies to other types of web apps also.

Deploy the app

Clone my project using git on the server

git clone address_of_remote_repository

Set up the server and project to get the app up and running as per normal.

Configure git

On the cloud server, create SSH keys and install them onto my GitHub repository. This GitHub guide gives detailed steps for different operating systems.

Now when I run git pull on the server, the username and password are not requested.

Create deploy scripts

The deployment has been split into two shell scripts: one that the normal server user can run; and one that needs root to run.

A shell script (called refresh.sh in this example) activates the virtual environment, collects the latest master branch, runs all the migrations. This does all the updates needed to the app codebase and database.

#!/bin/bashsource ~/.virtualenvs/my_app/bin/activate
cd my_app
git pullpip install -r my_app/requirements.txtpython3 manage.py migrate
python3 manage.py collectstatic --noinput

We use the--noinput option in django so that these scripts do not ask for confirmation from the user.

You can add other steps as required to fully update your codebase for your specific project.

A shell script (called reload.sh in this example) that will be run as root that will restart the two servers so that the changes will then be live.

#!/bin/bash
systemctl restart gunicorn
systemctl reload nginx

This script is for a django example using these two server configurations, but this can be easily adapted to other server setups.

Both shell scripts need to be made executable — chmod a+x reload.sh

Test that they do indeed work.

Setting up crontab

We will set up the app to refresh each night at 2am whilst we are asleep.

Using: crontab -e add the following lines to the crontab file for the main app user:

00 02 * * * /bin/bash /home/my_user/refresh.sh

This will run the refresh.sh script at 2am every morning.

We then need to reboot the servers, and we need to be a root user to do this as these commands require sudo access. So we add a line to the root user’s crontab using sudo crontab -e :

05 02 * * * /bin/bash /home/my_user/reload.sh

This will run the reload.sh script 5 minutes later.

That’s it.

If you want to deploy at different times or multiple times a day then here is a good introduction to how to use cron settings.

Debugging issues

If it does not run as intended, here are some debugging tasks to try:

  1. Do the two shell scripts run cleanly in the terminal as the main user and root user respectively?
  2. Are there errors in the log files explaining why cron is not running your scripts cleanly? On DigitalOcean Ubuntu machines, the cron logs can be read using sudo cat /var/log/syslog | grep cron
  3. Try changing the scheduled timing of the cron job to 2 minutes in the future and watching if the process does fire up then using ps -aux | grep python

Final thoughts

This is clearly very quick to set up and perhaps a bit of a hack. For proper projects where uptime of the app is important, or there are multiple developers, then full CI/CD methods and tools would be much more reliable. But for a quick continuous deployment of an app in development, this is a nice timesaver rather than hopping onto the server continuously.

--

--