CI/CD: Increase your Development Pace
To automate and ultimately speed up your deployment chores, once and for all
This article is written as a part of individual review criterion of PPL CS UI 2020
Overview
I assume by the time you read this blog, you know that an app cannot be sitting on your computer and running locally only. Most of them need to be published so others can use it.
And if you are aware of that, you must know that publishing or deploying an application is one hell of a chore to do, especially when you have to do it over and over every time you update the application.
Sit back and relax, with some easy steps you can be just like me and my team, where we do not worry about the hustle of deployment anymore. Now let's dive into CI/CD.
Continuous Integration (CI)
Simple to say, CI is the name given to the processes of preparing the code to be released. These processes include:
- Building the app
- Running tests
- Report and distribute the results
Continuous Delivery (CD)
Meanwhile, CD includes some processes that execute the deployment/release process. Basically, this process is the one moving your code from your repository to the server in which the application resides and running.
How we do it on our team at PPL
I am developing the backend server for our project. And we decided to have our application to be served on Heroku. Since we have two environments: one for developments (staging) and another one for the real deal (production). Therefore:
1. Created two Heroku apps and their associated database
We created 2 Heroku apps, and we added a Postgres Add-On to each of them. Go to their dashboard and you can achieve this with some simple clicks. Psst, it’s free :)
Say you have 2 apps called app
and app-staging
, then your apps will be accessible through :
app.herokuapp.com
app-staging.herokuapp.com
2. Create the application
We use Django-REST as our application framework. We find it easy to work with, flexible, and we are already quite familiar with some of its features. So we created one (just one)
3. Create a git repo for the application
We are provided with a GitLab enterprise account, so we push our application to this repo. For the two environments, we have 2 branches to differentiate them: master
and staging
.
4. Setup CI/CD files
Since we are using Gitlab and it’s registered pipeline which is Gitlab-CI, we provide it with a .gitlab-ci.yml
as a configurational script for our CI.
This file will instruct the Gitlab Runner to executes some commands. Moreover, we can divide them into stages like test
, deploy
, etc. In our case, we have code analytic tools called SonarQube, which we also plugged in here.
stages:
- test
- sonar-scanner
- deploy
test:
image: python:3.6.5
stage: test
before_script:
- pip install -r requirements.txt
- python manage.py makemigrations
- python manage.py migrate
- python manage.py collectstatic --no-input
- python manage.py runserver 8000 &
when: on_success
script:
- echo "Starting linter"
- sh lint.sh
- echo "Starting tests"
- coverage erase
- coverage run --include="./*/*" --omit="./env/*","./project/*","./manage.py" manage.py test apps
- coverage xml -i
- coverage report -m
artifacts:
paths:
- coverage.xml
SonarScanner:
image:
name: sonarsource/sonar-scanner-cli:latest
entrypoint: [""]
stage: sonar-scanner
script:
- sonar-scanner
-Dsonar.host.url=$SONARQUBE_URL
-Dsonar.login=$SONARQUBE_TOKEN
-Dsonar.branch.name=$CI_COMMIT_REF_NAME
-Dsonar.projectKey=$SONARQUBE_PROJECT_KEY
deploy:
image: ruby:2.4
stage: deploy
before_script:
- gem install dpl
- wget -qO- https://cli-assets.heroku.com/install-ubuntu.sh | sh
- >
if [[ "${CI_COMMIT_REF_NAME}" == "staging" ]];
then
export HEROKU_APP_NAME=$HEROKU_STAGING_APP
HEROKU_APP_HOST=http://tuberculosis-ppti-staging.herokuapp.com/
ENVIRONMENT_NAME=staging
DATABASE_URL=$DATABASE_URL_STAGING
export DATABASE_NAME=$DATABASE_NAME_STAGING
export DATABASE_USER=$DATABASE_USER_STAGING
export DATABASE_PASSWORD=$DATABASE_PASSWORD_STAGING
export DATABASE_HOST=$DATABASE_HOST_STAGING
elif [[ "${CI_COMMIT_REF_NAME}" == "master" ]];
then
export HEROKU_APP_NAME=$HEROKU_PRODUCTION_APP
HEROKU_APP_HOST=http://tuberculosis-ppti.herokuapp.com/
ENVIRONMENT_NAME=production
DATABASE_URL=$DATABASE_URL_PRODUCTION
export DATABASE_NAME=$DATABASE_NAME_PROD
export DATABASE_USER=$DATABASE_USER_PROD
export DATABASE_PASSWORD=$DATABASE_PASSWORD_PROD
export DATABASE_HOST=$DATABASE_HOST_PROD
fi
- export DATABASE_PORT=5432
- export DATABASE_IS_PSQL=true
script:
- dpl --provider=heroku --app=$HEROKU_APP_NAME --api-key=$HEROKU_API_KEY
- export HEROKU_API_KEY=$HEROKU_API_KEY
only:
- staging
- master
environment:
name: $ENVIRONMENT_NAME
url: $HEROKU_APP_HOST
You can see our customization on the production
and staging
is set on the conditionals on the
if [[ “${CI_COMMIT_REF_NAME}” == “staging” ]];
...
elif [[ "${CI_COMMIT_REF_NAME}" == "master" ]];
...
fi
There you have it. As you can see, there are some variables noted in $VARIABLE
. You can set their value in secret through the settings. Why? So it does not available in public, of course, it is a secret anyway.
Now, since we intend to deploy our apps to Heroku, it demands us to have Procfile to set up the webserver that is going to run our application. The contents of this file Procfile
can be as simple as :
web: gunicorn app.wsgi
Where your app name should substitute the above app
.
5. Push code to this repo = deploy
Just push it with git push [remote-name] [branch]
(now your branch has to be either staging or master as we described in the script), and it will deploy your code to the corresponding application!