Deploy Django to Openshift Using Jenkins Pipeline(CI/CD)

Using Jenkins pipeline, you can easily automate your deployments in openshift. In this post, we are going to use this powerful tool to deploy a Django project. But before we start, one thing I need to mention is that, this project has been tested on in Minishift 1.2.0, Openshift v3.9.0 and
Kubernetes v1.9.1
. So without further adou, let us begin.

Source Code

You can find the source code for a this project here: https://github.com/ruddra/openshift-django

Pipeline Excecusion Steps

On every pipeline execution, the code goes through the following steps:

  1. First, jenkins will check if build configuration for https://github.com/ruddra/openshift-django exists in dev project.
  2. If not, then it will create new app using the templates provided in code.
  3. Then, it will build and test the code
  4. After successful build, it will deploy the image to dev project.
  5. Then it will prompt if it will deploy the code to stage. If yes, then it will proceed to next step, else it will abort.
  6. It will push the dev image to stage and tag it as promoteToQA.
  7. This image will be deployed to stage project and will be scaled to 3.

Automated Deploy on Minishift

Go to scripts directory and run ./setup.sh

Manual Deploy on Minishift

First, run minishift using minishift start memory=4000.
Then, run the following commands to create projects in Openshift.

# Create Projects
oc new-project dev --display-name="Tasks - Dev"
oc new-project stage --display-name="Tasks - Stage"
oc new-project cicd --display-name="CI/CD"

Install Jenkins in cicd by running

oc project cicd
oc new-app openshift/jenkins-2-centos7

Now, give permission to Jenkins to modify in dev and stage project.

# Grant Jenkins Access to Projects
oc policy add-role-to-user edit system:serviceaccount:cicd:jenkins -n dev
oc policy add-role-to-user edit system:serviceaccount:cicd:jenkins -n stage

Deploy the MySQL in cicd project by loading MySQL template like this:

oc new-app -f .openshift/templates/mysql-template.yaml

It will create the mysql server. Please keep in mind that, this MySQL will be available in 172.30.0.30. This configurations in defined in Service configuration section of the template

So, we are almost ready. Now please load the pipeline using:

oc new-app -f .openshift/pipelines/openshift-django-pipeline.yaml

Now you will be able to see the pipeline in Console > CI/CD > Build > Pipelines. You can run it from there or from CLI like:

oc start-build djangopipeline

To implement webhooks, you can look into this project.

Screenshots

Pipeline execustion looks like this in Blue Ocean:
Screen-Shot-2018-07-24-at-8.17.19-PM

Stage View in Jenkins looks like this:
Screen-Shot-2018-07-24-at-8.20.50-PM

In CI/CD project of Openshift:
Screen-Shot-2018-07-24-at-8.22.46-PM

In Dev project of Openshift:
Screen-Shot-2018-07-24-at-8.23.06-PM

In Stage project of Openshift:
Screen-Shot-2018-07-24-at-8.23.25-PM

After Pipeline excecustion, it should look like this:
Screen-Shot-2018-07-24-at-8.37.52-PM

How to Customize the Pipeline to Deploy your Project

Pre-requisite

  1. Have a Django Project with Docker to deploy it.
  2. Have minishift installed in your local machine.
  3. Give it 4GB memory.
  4. If you can pull python, jenkins-2-centos7, mysql-55-centos7 images beforehand using docker pull <image name>, it would make the deployment much more faster.

Modify Pipeline

You can use this Pipeline to deploy your own project with this minimal changes:

  1. Go to Pipeline template at here:
    OR
    Take this pipeline:
def openshiftDjangoRepo="https://raw.githubusercontent.com/ruddra/openshift-django/develop/.openshift/templates/openshift-django-template.yaml"
def stageTag="promoteToQA"
def DEV_PROJECT="dev"
def STAGE_PROJECT="stage"
def templateName="openshift-django"

pipeline{
  agent { label ""}
  stages{
    stage('Create in DEV') {
      when {
        expression {
          openshift.withCluster() {
            openshift.withProject(DEV_PROJECT) {
              echo "checking openshift django exists in DEV"
              return !openshift.selector("bc", "${templateName}").exists();
            }
          }
        }
      }
      steps {
        script {
          openshift.withCluster() {
            openshift.withProject(DEV_PROJECT) {
              openshift.newApp(openshiftDjangoRepo).narrow("svc").expose();
            }
          }
        }
      }
    }
    stage('Build and Test in DEV'){
      steps {
        script {
          openshiftBuild(namespace: "${DEV_PROJECT}", buildConfig: "${templateName}", showBuildLogs: 'true',  waitTime: "600000")
        }
      }
    }
    stage('Rollout to DEV') {
      steps {
        script {
          openshiftDeploy(namespace: "${DEV_PROJECT}", deploymentConfig: "${templateName}", waitTime: "600000")
        }
      }
    }
    stage('Scale in DEV') {
      steps {
        script {
          openshiftScale(namespace: "${DEV_PROJECT}", deploymentConfig: "${templateName}", replicaCount: '1')
        }
      }
    }
    stage('Promote to STAGE?') {
      steps {
        timeout(time:15, unit:'MINUTES') {
          input message: "Promote to STAGE?", ok: "Promote"
        }

        script {
          openshift.withCluster() {
            openshift.tag("${DEV_PROJECT}/${templateName}:latest", "${STAGE_PROJECT}/${templateName}:${stageTag}")
          }
        }
      }
    }
    stage('Rollout to STAGE') {
      steps {
        script {
          openshift.withCluster() {
            openshift.withProject(STAGE_PROJECT) {
              if (openshift.selector('dc', "${templateName}").exists()) {
                openshift.selector('dc', "${templateName}").delete()
                openshift.selector('svc', "${templateName}").delete()
                openshift.selector('route', "${templateName}").delete()
              }
            openshift.newApp("${templateName}:${stageTag}").narrow("svc").expose()
          }
        }
      }
    }
  }
  stage('Scale in STAGE') {
      steps {
        script {
          openshiftScale(namespace: "${STAGE_PROJECT}", deploymentConfig: "${templateName}", replicaCount: '3')
        }
      }
    }
  }
}
  1. Edit this line in the template:
    def openshiftDjangoRepo="https://raw.githubusercontent.com/ruddra/openshift-django/master/.openshift/templates/openshift-django.yaml"
    
    Change the url to either your repository link like https://github.com/ruddra/openshift-django
  2. Change the template name in there as well:
    def templateName="openshift-django"
    

Advanced Implementation

In this post, we have deployed our application by using only Openshift APIs in Jenkins Plugin. If you want more advanced implementation, like having Jenkins do build, testing, store test results etc with more advanced pipeline, then please look into these posts:

  1. Modern CI/CD Using Python+Gunicorn+NGINX+Jenkins Pipeline to Openshift (Part One)
  2. Modern CI/CD Using Python+Gunicorn+NGINX+Jenkins Pipeline to Openshift (Part Two)
  3. Modern CI/CD Using Python+Gunicorn+NGINX+Jenkins Pipeline to Openshift (Part Three)

Thanks for reading.
Cheers!!


Arnab Kumar Shil

Love coding, traveling, anime, cycling, ping pong, motorbiking, and humor.

Dhaka, Bangladesh
https://ruddra.com

Comments

comments powered by Disqus