Zero-Downtime Application Deployment Using Blue-Green Architecture (Django App deploy)

Continuous Integration and Continuous Deployment (CI/CD) have become the backbone of DevOps, allowing faster delivery of applications. However, deployment failures can introduce downtime and affect user experience. To combat this, Blue-Green Deployment offers a mechanism to deploy new versions with zero downtime by running two environments simultaneously — one active (Green) and one standby (Blue). This project implements an automated CI/CD pipeline with integrated security and quality tools, along with containerization and Kubernetes-based orchestration to achieve safe and efficient deployments. Literature Review CI/CD Pipelines: CI/CD automates the process of software delivery and deployment. Jenkins is a widely adopted open-source automation tool for CI/CD. Code Quality Tools: Tools like SonarQube evaluate code quality by checking for bugs, vulnerabilities, and code smells. Security Scanning: OWASP Dependency Check and Trivy help identify security flaws in dependencies and container images respectively. Blue-Green Deployment: This deployment technique provides a safe way to roll out changes without affecting live traffic. Amazon EKS: A managed Kubernetes service that simplifies deploying, managing, and scaling containerized applications. Methodology The project methodology is based on an agile and iterative approach where each phase of the CI/CD pipeline is independently developed, tested, and integrated. Key tools and technologies used include: Jenkins for pipeline orchestration. SonarQube for static code analysis. OWASP Dependency Check for third-party vulnerability scanning. Trivy for Docker image vulnerability scanning. Docker Hub for image storage. Amazon EKS for orchestrating Blue-Green deployments. Setting Up the Foundation Spinning up EC2 instances (t2.large for Jenkins, t2.small for EKS management). Download Following tools in Jenkins Server (t2.large) Jenkins : Install Jenkins https://dev.to/bhaktraj/how-to-install-jenkins-in-ubuntu-2735 Docker : Install Docker https://dev.to/bhaktraj/docker-install-in-ubuntu-or-on-cloud-aws-provisioning-mfi SonarQube : After the docker installation, we create a sonarqube container (Remember to add 9000 ports in the security group). docker run -d --name sonarqube \ --restart always \ -p 9000:9000 \ -v sonarqube_conf:/opt/sonarqube/conf \ -v sonarqube_data:/opt/sonarqube/data \ -v sonarqube_extensions:/opt/sonarqube/extensions \ -v sonarqube_logs:/opt/sonarqube/logs \ sonarqube:lts-community Now our sonarqube is up and running on 9000 port Enter username and password, click on login and change password username admin password admin Update New password, This is Sonar Dashboard. Trivy : Install Trivy vim trivy.sh sudo apt-get install wget apt-transport-https gnupg lsb-release -y wget -qO - https://aquasecurity.github.io/trivy-repo/deb/public.key | gpg --dearmor | sudo tee /usr/share/keyrings/trivy.gpg > /dev/null echo "deb [signed-by=/usr/share/keyrings/trivy.gpg] https://aquasecurity.github.io/trivy-repo/deb $(lsb_release -sc) main" | sudo tee -a /etc/apt/sources.list.d/trivy.list sudo apt-get update sudo apt-get install trivy -y AWS CLI : Configure AWS With the help of IAM user Create a IAM User and and access with the command line IAM _Create a user “eks-admin” with AdministratorAccess Create Security Credentials Access Key and Secret access key _ Install AWS CLI v2 curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" sudo apt install unzip unzip awscliv2.zip sudo ./aws/install -i /usr/local/aws-cli -b /usr/local/bin --update Setup your access by aws configure kubectl : Install curl -o kubectl https://amazon-eks.s3.us-west-2.amazonaws.com/1.19.6/2021-01-05/bin/linux/amd64/kubectl chmod +x ./kubectl sudo mv ./kubectl /usr/local/bin kubectl version --short --client Next, we will log in to Jenkins and start to configure our Pipeline in Jenkins its running on 8080 ports http://ip_address:8080 Password is available on sudo cat sudo cat /var/lib/jenkins/secrets/initialAdminPassword copy it and paste to jenkins login and then configure it First some basic configure you have to configue and then after Install Plugins like Goto Manage Jenkins →Plugins → Available Plugins search these plugin and install it Sonarqube Scanner, OWASP Dependency Check, Docker, Docker Commons, Docker Pipeline, Docker API, docker-build-step Slack Notification Kubernetes CLI Plugin, Kubernetes Client API Plugin, Kubernetes Credentials Plugin, Kubernetes plugin Configure Sonar Server in Manage Jenkins: Grab the Public IP Address of your EC2 Instance, Sonarqube works on Port 9000, so :9000. Goto your Sonarqube Server. Click on Administration → Security → Users → Click on Tokens and Update Token → Give it a name → and click on Generate Token copy Token Goto Jenkins Dashboard → Manage Jenkins → Credentials → Add Secret Text.

Apr 15, 2025 - 11:03
 0
Zero-Downtime Application Deployment Using Blue-Green Architecture (Django App deploy)

Continuous Integration and Continuous Deployment (CI/CD) have become the backbone of DevOps, allowing faster delivery of applications. However, deployment failures can introduce downtime and affect user experience. To combat this, Blue-Green Deployment offers a mechanism to deploy new versions with zero downtime by running two environments simultaneously — one active (Green) and one standby (Blue). This project implements an automated CI/CD pipeline with integrated security and quality tools, along with containerization and Kubernetes-based orchestration to achieve safe and efficient deployments.

Image description

Literature Review
CI/CD Pipelines: CI/CD automates the process of software delivery and deployment. Jenkins is a widely adopted open-source automation tool for CI/CD.

Code Quality Tools: Tools like SonarQube evaluate code quality by checking for bugs, vulnerabilities, and code smells.

Security Scanning: OWASP Dependency Check and Trivy help identify security flaws in dependencies and container images respectively.

Blue-Green Deployment: This deployment technique provides a safe way to roll out changes without affecting live traffic.

Amazon EKS: A managed Kubernetes service that simplifies deploying, managing, and scaling containerized applications.

Methodology

The project methodology is based on an agile and iterative approach where each phase of the CI/CD pipeline is independently developed, tested, and integrated. Key tools and technologies used include:

Jenkins for pipeline orchestration.

SonarQube for static code analysis.

OWASP Dependency Check for third-party vulnerability scanning.

Trivy for Docker image vulnerability scanning.

Docker Hub for image storage.

Amazon EKS for orchestrating Blue-Green deployments.

Setting Up the Foundation

Spinning up EC2 instances (t2.large for Jenkins, t2.small for EKS management).

Download Following tools in Jenkins Server (t2.large)

docker run -d --name sonarqube \
  --restart always \
  -p 9000:9000 \
  -v sonarqube_conf:/opt/sonarqube/conf \
  -v sonarqube_data:/opt/sonarqube/data \
  -v sonarqube_extensions:/opt/sonarqube/extensions \
  -v sonarqube_logs:/opt/sonarqube/logs \
  sonarqube:lts-community

Now our sonarqube is up and running on 9000 port
Enter username and password, click on login and change password

username admin
password admin

Image description

Image description

Update New password, This is Sonar Dashboard.

  • Trivy : Install Trivy
vim trivy.sh

sudo apt-get install wget apt-transport-https gnupg lsb-release -y
wget -qO - https://aquasecurity.github.io/trivy-repo/deb/public.key | gpg --dearmor | sudo tee /usr/share/keyrings/trivy.gpg > /dev/null
echo "deb [signed-by=/usr/share/keyrings/trivy.gpg] https://aquasecurity.github.io/trivy-repo/deb $(lsb_release -sc) main" | sudo tee -a /etc/apt/sources.list.d/trivy.list
sudo apt-get update
sudo apt-get install trivy -y

  • AWS CLI : Configure AWS With the help of IAM user Create a IAM User and and access with the command line

IAM
_Create a user “eks-admin” with AdministratorAccess
Create Security Credentials Access Key and Secret access key _

Install AWS CLI v2

curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
sudo apt install unzip
unzip awscliv2.zip
sudo ./aws/install -i /usr/local/aws-cli -b /usr/local/bin --update

Setup your access by

aws configure
  • kubectl : Install
curl -o kubectl https://amazon-eks.s3.us-west-2.amazonaws.com/1.19.6/2021-01-05/bin/linux/amd64/kubectl
chmod +x ./kubectl
sudo mv ./kubectl /usr/local/bin
kubectl version --short --client

Next, we will log in to Jenkins and start to configure our Pipeline in Jenkins its running on 8080 ports

http://ip_address:8080

Password is available on

sudo cat sudo cat /var/lib/jenkins/secrets/initialAdminPassword

copy it and paste to jenkins login

and then configure it
First some basic configure you have to configue

and then after
Install Plugins like

Goto Manage Jenkins →Plugins → Available Plugins
search these plugin and install it

  • Sonarqube Scanner,
  • OWASP Dependency Check,
  • Docker, Docker Commons, Docker Pipeline, Docker API, docker-build-step
  • Slack Notification
  • Kubernetes CLI Plugin, Kubernetes Client API Plugin, Kubernetes Credentials Plugin, Kubernetes plugin

Configure Sonar Server in Manage Jenkins:

Grab the Public IP Address of your EC2 Instance, Sonarqube works on Port 9000,
so :9000.
Goto your Sonarqube Server. Click on Administration → Security → Users → Click on Tokens and Update Token → Give it a name → and click on Generate Token
Token Generate in Sonarqube

copy Token

Goto Jenkins Dashboard → Manage Jenkins → Credentials → Add Secret Text. It should look like this

Upload Credentials

You will this page once you click on create

Now, go to Dashboard → Manage Jenkins → System and Add like the below image.

you can leave the server url because sonarqube is running in local host or you can paste the public ip
like
http://ip_address:9000

Sonarqube system setting

Click on Apply and Save

The Configure System option is used in Jenkins to configure different server

Global Tool Configuration is used to configure different tools that we install using Plugins

Sonar Tool

We will install a sonar scanner in the tools.

Configure OWASP Dependency Check Tool:

Plugin is install now we had to configure the Tool
Goto Dashboard → Manage Jenkins → Tools →

Owasp Tools

Click on Apply and Save here.

Configure Docker Tools:

Docker Tools

Add DockerHub Username and Password under Global Credentials

Goto Jenkins Dashboard → Manage Jenkins → Credentials → set username and password It should look like this

Docker password

For more Security
Create a token on docker hub and paste it on password

Goto DockerHub Dashboard → Account Setting → Personal access tokens → It should look like this

Docker Hub

Password Prompt paste it to password section of jenkins credential

Now configure Slack For notification

goto https://slack.com/marketplace/A0F7VRFKN-jenkins-ci

Slack Marketplace

Click on add to slack

Select the channel and add jenkins ci integration
jenkins ci integration
Follow the steps that are mention or simply copy the token and paste to jenkins Credential

jenkins Slack Credential

and update the slack setting in jenkins
by
go to Dashboard → Manage Jenkins → System and Add like the below image.

and details are taken by slack dashboard of that channel check it is it wokking properly or not if success message show that means slack is connected

Slacknotification setting

Now Setting a EKS Management server

Launch a EC2 Instance with t2small instance type

Configure the following things

IAM
_Create a user “eks-admin” with AdministratorAccess
Create Security Credentials Access Key and Secret access key _

Install AWS CLI v2

curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
sudo apt install unzip
unzip awscliv2.zip
sudo ./aws/install -i /usr/local/aws-cli -b /usr/local/bin --update

Setup your access by

aws configure
curl -o kubectl https://amazon-eks.s3.us-west-2.amazonaws.com/1.19.6/2021-01-05/bin/linux/amd64/kubectl
chmod +x ./kubectl
sudo mv ./kubectl /usr/local/bin
kubectl version --short --client

Install eksctl

curl --silent --location "https://github.com/weaveworks/eksctl/releases/latest/download/eksctl_$(uname -s)_amd64.tar.gz" | tar xz -C /tmp
sudo mv /tmp/eksctl /usr/local/bin
eksctl version

Setup EKS Cluster

eksctl create cluster --name blue-green-deployment --region us-east-1 --node-type t2.medium --nodes-min 2 --nodes-max 2 --node-volume-size 10
aws eks update-kubeconfig --region us-east-1 --name blue-green-deployment
kubectl get nodes

Kubernetes Configuration for Jenkins Deployment Access

Run Kubernetes manifest files to:

  • Create a Service Account for Jenkins.
vi serviceaccount.yml
apiVersion: v1 
kind: ServiceAccount 
metadata: 
  name: jenkins 
  namespace: webapps

note: edit the intent

  • Create a Role.
vi role.yml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: app-role
  namespace: webapps
rules:
  - apiGroups:
        - ""
        - apps
        - autoscaling
        - batch
        - extensions
        - policy
        - rbac.authorization.k8s.io
    resources:
      - pods
      - componentstatuses
      - configmaps
      - daemonsets
      - deployments
      - events
      - endpoints
      - horizontalpodautoscalers
      - ingress
      - jobs
      - limitranges
      - namespaces
      - nodes
      - secrets
      - pods
      - persistentvolumes
      - persistentvolumeclaims
      - resourcequotas
      - replicasets
      - replicationcontrollers
      - serviceaccounts
      - services
    verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
  • Create a Role and bind it to the service account.
vi rolebindservice.yml

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: app-rolebinding
  namespace: webapps 
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: app-role 
subjects:
- namespace: webapps 
  kind: ServiceAccount
  name: jenkins 
  • Create a ClusterRole and bind it as well.
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: jenkins-cluster-role
rules:
- apiGroups: [""]
  resources: ["persistentvolumes"]
  verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]

---

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: jenkins-cluster-role-binding
subjects:
- kind: ServiceAccount
  name: jenkins
  namespace: webapps
roleRef:
  kind: ClusterRole
  name: jenkins-cluster-role
  apiGroup: rbac.authorization.k8s.io

  • Generate token using service account in the namespace

create token

vi token.yml
apiVersion: v1
kind: Secret
type: kubernetes.io/service-account-token
metadata:
  name: mysecretname
  annotations:
    kubernetes.io/service-account.name: jenkins

Run Manifests

kubectl create namespace webapps
kubectl apply -f . -n webapps

Copy the token and add it to Jenkins credentials to allow Jenkins to deploy applications onto EKS securely.

kubectl describe secret mysecretname -n webapps

kube token

Paste this token to jenkns credential

Goto Jenkins Dashboard → Manage Jenkins → Credentials → set Secret key
It should look like this

kube credential

Create a Job on Jenkins
Dashboard and then New Item name the job "Blue-green-Deployment"(What every you want) and then click on ok

Jenkin job

Upload the Script And Click on OK

def COLOR_MAP = [
    'success': 'good',
    'FAILURE': 'danger',
]
pipeline {

    agent any


    parameters {
        choice(name: 'DEPLOY_ENV', choices: ['blue', 'green'], description: 'Choose which environment to deploy: Blue or Green')
        booleanParam(name: 'SWITCH_TRAFFIC', defaultValue: false, description: 'Switch traffic between Blue and Green')
    }

    stages{
        stage('clean workspace'){
            steps{
                cleanWs()
            }
        }

        stage('Fetch the code'){
            steps{
                git url: 'https://github.com/bhaktraj/vkonsec.git', branch: 'kubernetes'

            }    
        }

        stage('TRIVY FS SCAN') {
            steps {
                sh "trivy fs . > trivyfs.txt"
            }
        }

        stage('sonarqube scan'){
            environment {
             scannerHome = tool 'Sonarscanner'
          }

          steps {
            withSonarQubeEnv('Sonarscanner') {
               sh '''${scannerHome}/bin/sonar-scanner -Dsonar.projectKey=vkonsec \
                   -Dsonar.projectName=vkonsec \
                   -Dsonar.projectVersion=1.0 \
                   -Dsonar.sources=. \
                   '''
            }
        }
        }
        stage('OWASP Dependency Check') {
            steps {
                dependencyCheck additionalArguments: '--scan ./ --format XML --out dependency-check-report --project vkonsec-django --enableExperimental', odcInstallation: 'Owasp'
                dependencyCheckPublisher pattern: '**/dependency-check-report.xml'

            }
        }
        stage('Build docker images'){
            steps{
                script{
                    dockerimage = docker.build('vkonsec' + ":$BUILD_NUMBER", ".")
                    dockerimage = docker.build('nginx' + ":$BUILD_NUMBER", "./nginx")

                }

            }
        }
        stage("TRIVY"){
            steps{
                sh "trivy image vkonsec:$BUILD_NUMBER > trivy.txt" 
            }
        }
        stage("Docker Push"){
            steps{
                script{
                   withDockerRegistry(credentialsId: 'dockercred', toolName: 'docker'){   
                       sh "docker tag vkonsec:$BUILD_NUMBER bhaktraj/vkonsec:$BUILD_NUMBER "
                       sh "docker tag nginx:$BUILD_NUMBER bhaktraj/nginx:$BUILD_NUMBER "
                       sh "docker push bhaktraj/vkonsec:$BUILD_NUMBER "
                       sh "docker push bhaktraj/nginx:$BUILD_NUMBER "
                    }
                }
            }
        }
        stage('Persistent Volume Claim for MySQL') {
            steps {
                script {
                    withKubeConfig(caCertificate: '', clusterName: 'blue-green-deployment', contextName: '', credentialsId: 'kubecred', namespace: 'webapps', restrictKubeConfigAccess: false, serverUrl: 'https://3CA6E485D43C0E5E4EB7B7108ECACEA2.gr7.us-east-1.eks.amazonaws.com') {
                        sh """ if ! kubectl get pvc mysql-pvc -n webapps; then
                                kubectl apply -f k8/mysql-pvc.yaml -n webapps
                            fi
                        """  
                    }
                }
            }
        }
        stage('Deploying MySQL') {
            steps {
                script {
                    withKubeConfig(caCertificate: '', clusterName: 'blue-green-deployment', contextName: '', credentialsId: 'kubecred', namespace: 'webapps', restrictKubeConfigAccess: false, serverUrl: 'https://3CA6E485D43C0E5E4EB7B7108ECACEA2.gr7.us-east-1.eks.amazonaws.com') {
                        sh """ 
                        kubectl apply -f k8/mysql-deployment.yaml -n webapps
                        kubectl apply -f k8/mysql-service.yaml -n webapps
                        """  
                    }
                }
            }
        }

        stage('Update K8s Manifest') {
            steps {
                sh " sed -i 's/buildid/$BUILD_NUMBER/g' k8/django-blue-deployment.yaml "
                sh " sed -i 's/buildid/$BUILD_NUMBER/g' k8/django-green-deployment.yaml "
                sh " sed -i 's/buildid/$BUILD_NUMBER/g' k8/nginx-deployment.yaml "
            }
        }

        stage('Application deployment') {
            steps {
                script {
                    def deploymentFile = ""
                    if (params.DEPLOY_ENV == 'blue') {
                        deploymentFile = 'k8/django-blue-deployment.yaml'
                    } else {
                        deploymentFile = 'k8/django-green-deployment.yaml'
                    }

                    withKubeConfig(caCertificate: '', clusterName: 'blue-green-deployment', contextName: '', credentialsId: 'kubecred', namespace: 'webapps', restrictKubeConfigAccess: false, serverUrl: 'https://3CA6E485D43C0E5E4EB7B7108ECACEA2.gr7.us-east-1.eks.amazonaws.com') 
                    {
                        sh "kubectl apply -f ${deploymentFile} -n webapps"
                        sh "kubectl apply -f k8/django-service.yaml -n webapps"
                    }
                }
            }
        }

        stage('Switch Traffic Between Blue & Green Environment') {
            when {
                expression { return params.SWITCH_TRAFFIC }
            }
            steps {
                script {
                    def newEnv = params.DEPLOY_ENV

                    // Always switch traffic based on DEPLOY_ENV
                    withKubeConfig(caCertificate: '', clusterName: 'blue-green-deployment', contextName: '', credentialsId: 'kubecred', namespace: 'webapps', restrictKubeConfigAccess: false, serverUrl: 'https://3CA6E485D43C0E5E4EB7B7108ECACEA2.gr7.us-east-1.eks.amazonaws.com') {
                        sh '''
                            kubectl patch service djangoapp -p "{\\"spec\\": {\\"selector\\": {\\"app\\": \\"djangoapp\\", \\"version\\": \\"''' + newEnv + '''\\"}}}" -n webapps
                        '''
                    }
                    echo "Traffic has been switched to the ${newEnv} environment."
                }
            }
        }
        stage('Deploy Nginx') {
            steps {
                script {
                    withKubeConfig(caCertificate: '', clusterName: 'blue-green-deployment', contextName: '', credentialsId: 'kubecred', namespace: 'webapps', restrictKubeConfigAccess: false, serverUrl: 'https://3CA6E485D43C0E5E4EB7B7108ECACEA2.gr7.us-east-1.eks.amazonaws.com') {
                        sh """ 
                        kubectl apply -f k8/nginx-deployment.yaml -n webapps
                        kubectl apply -f k8/nginx-service.yaml -n webapps
                        """  
                    }
                }
            }
        }

        stage('Verify Deployment') {
            steps {
                script {
                    def verifyEnv = params.DEPLOY_ENV
                    withKubeConfig(caCertificate: '', clusterName: 'blue-green-deployment', contextName: '', credentialsId: 'kubecred', namespace: 'webapps', restrictKubeConfigAccess: false, serverUrl: 'https://3CA6E485D43C0E5E4EB7B7108ECACEA2.gr7.us-east-1.eks.amazonaws.com') {
                        sh """
                        kubectl get pods -l version=${verifyEnv} -n webapps
                        kubectl get svc nginx -n webapps
                        """
                    }
                }
            }
        }



    }
    post {
        always {
            echo 'slack Notification'
            slackSend channel: '#jenkins',
                color:  COLOR_MAP[currentBuild.currentResult],
                message:"*${currentBuild.currentResult}:* Job ${env.JOB_NAME} build ${env.BUILD_NUMBER} \n more info at : ${env.BUILD_URL}"
            slackUploadFile channel: '#jenkins',
                filePath: '/var/lib/jenkins/workspace/pipeline/trivy.txt',
                initialComment: "