Zero Downtime Deployment: Blue-Green Strategy for Java MySQL Applications on EKS
In today's fast-paced development environment, ensuring application availability and minimizing downtime during deployment is crucial. One of the most effective deployment strategies to achieve this is blue-green deployment, which allows for seamless updates and faster rollbacks in case of issues.
In this blog, I will guide you through the process of deploying a Java-based MySQL application using blue-green deployment to achieve zero downtime and rapid rollback. We'll leverage a CI/CD pipeline integrated with tools like Terraform for provisioning an Amazon EKS cluster, Git, Maven, SonarQube, Trivy, Nexus, Docker, and Kubernetes to automate the entire deployment process.
What is Blue-Green Deployment?
Blue-green deployment is a deployment strategy designed to reduce downtime and minimize the risks involved in releasing new software versions. In this approach, two identical environments are maintained: Blue (the currently live environment) and Green (the environment with the new application version).
Here’s how it works:
Blue Environment (Current Production): The "blue" environment runs the live version of the application.
Green Environment (New Version): The new version of the application is deployed to the "green" environment, which is identical to the blue environment.
Switching Traffic: Once the green environment is fully tested and validated, traffic is seamlessly switched from the blue to the green environment. This ensures that users experience no downtime.
Rollback: In case of any issues with the green environment, traffic can easily be switched back to the blue environment, providing a fast and reliable rollback option.
By the end of this tutorial, you'll have a fully automated pipeline that ensures smooth application updates with zero downtime, high availability, and robust security checks.
Prerequisites:
- AWS Account (with access key and secret access key)
1. Provision and Configure EKS :
Create EC2 ubuntu and ssh into the instance:
Update the instance:
sudo apt update
Install AWS CLI:
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" unzip awscliv2.zip sudo ./aws/install
Configure:
aws configure
Provide the access key and secret access key for the aws user and enter.
Install terraform:
sudo snap install terraform --classic
Clone the git repository for the EKS cluster code which you can get on:
Provision Infrastructure using terraform go into the folder /cluster:
#Terraform initialization command terraform init #Terrafrom plan for planning resources terraform plan #Terraform apply command to apply changes to AWS terraform apply --auto-approve
Connecting to the cluster:
aws eks --region ap-south-1 update-kubeconfig --name cluster-name
To access the EKS cluster using the Jenkins pipeline we need Jenkins to have access to EKS, for that we will be creating the Service Account in webapps namespace which jenkins will use to access the EKS resources.
Creating namespace webapps in EKS:
kubectl create ns webapps
Creating service account first create service_acc.yml with content as:
apiVersion: v1 kind: ServiceAccount metadata: name: jenkins namespace: webapps
To create service account run command:
kubectl apply -f service_acc.yml
Now we will be creating role to attach with the service account we will create a file role.yml with content:
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 - secrets - componentstatuses - configmaps - daemonsets - deployments - events - endpoints - horizontalpodautoscalers - ingress - jobs - limitranges - namespaces - nodes - pods - persistentvolumes - persistentvolumeclaims - resourcequotas - replicasets - replicationcontrollers - serviceaccounts - services verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
To create role run command:
kubectl apply -f role.yml
Next step is to assign the created role to the service account i.e. binding the role to service account for that create file bind_role.yml with content:
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
To bind role with service account run command:
kubectl apply -f bind_role.yml
Next create secret token for the jenkins service account, will create file jenkins_token.yml with content:
apiVersion: v1 kind: Secret type: kubernetes.io/service-account-token metadata: name: mysecretname annotations: kubernetes.io/service-account.name: jenkins
To create secret in webapps namespace run command:
kubectl apply -f jenkins_token.yml -n webapps
We will be using this secret for jenkins to EKS communication, to get the token run command:
kubectl describe secret mysecretname -n webapps
You will get the secret copy that secret and paste it to the Jenkins Secret.
2. Jenkins, SonarQube and Nexus:
Create 3 t2.medium ubuntu EC2 instances with 25 GB Storage.Name them as Jenkins, SonarQube and Nexus.
Configure Jenkins Instance:
SSH into the jenkins instance.
Install Java for Jenkins:
# Install OpenJDK 17 JRE Headless sudo apt install openjdk-17-jre-headless -y
Install Jenkins:
# Download Jenkins GPG key sudo wget -O /usr/share/keyrings/jenkins-keyring.asc \ https://pkg.jenkins.io/debian-stable/jenkins.io-2023.key # Add Jenkins repository to package manager sources echo deb [signed-by=/usr/share/keyrings/jenkins-keyring.asc] \ https://pkg.jenkins.io/debian-stable binary/ | sudo tee \ /etc/apt/sources.list.d/jenkins.list > /dev/null # Update package manager repositories sudo apt-get update # Install Jenkins sudo apt-get install jenkins -y #Enable jenkins sudo systemctl enable jenkins
Access the jenkins server on <ec2-public-ip>:8080 and provide username as admin and password we will get using the command:
sudo cat /var/lib/jenkins/secrets/initialAdminPassword
Install plugins in jenkins by clicking Dashboard > Manage Jenkins > Plugins:
- SonarQube scanner
- Config File Provider
- Maven Integration
- Pipeline Maven Integration
- Kubernetes
- Kubernetes Credentials
- Kubernetes CLI
- Kubernetes Client API
- Docker
- Docker Pipeline
- Pipeline stage view
Installing Tools:
In Jenkins > Tools
Maven: name= maven3; Install from apache; version=3.9.8
SonarQube Scanner: name=sonar-scanner; Install from maven central; version=6.1.0.4477
Install docker on Jenkins server using:
sudo apt-get update sudo apt-get install ca-certificates curl sudo install -m 0755 -d /etc/apt/keyrings sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc sudo chmod a+r /etc/apt/keyrings/docker.asc # Add the repository to Apt sources: echo \ "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \ $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \ sudo tee /etc/apt/sources.list.d/docker.list > /dev/null sudo apt-get update sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin sudo chmod 666 /var/run/docker.sock
Install Trivy:
sudo apt-get install wget apt-transport-https gnupg lsb-release wget -qO - https://aquasecurity.github.io/trivy-repo/deb/public.key | sudo apt-key add - echo deb 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
Install kubectl on Jenkins instance:
sudo snap install kubectl --classic
Configure SonarQube Server:
SSH into the sonarqube instance.
Install docker on the instance:
sudo apt install docker.io
Run the sonarqube container:
sudo docker run -d --name sonar -p 9000:9000 sonarqube:lts-community
You can access the sonarqube using the <sonar-ec2-public-ip>:9000.
Next to sign in into sonarqube enter username: admin and password: admin.
Create token by clicking Administration > Security > Users > Generate token > copy token.
Configure Nexus Server:
SSH into the nexus instance.
Install docker on the instance:
sudo apt install docker.io
Run Nexus container:
deploymentReposudo docker run -d --name nexus -p 8081:8081 sonatype/nexus3:latest
You can access the nexus using the <nexus-ec2-public-ip>:8081.
Next to you need to sign in into nexus using the username: admin and password we need to check for the file /nexus-data/admin.password inside the nexus container for that follow below commands:
sudo docker exec -it containerid /bin/bash cd sonatype-work cd nexus3 cat admin.password
3. Adding Credentials in Jenkins:
To store credentials inside Jenkins, go to Jenkins > Manage Jenkins > Manage Credentials > System > Global Credentials > Add credentials >
Git Credentials: Username & Password (github username:token)
DockerHub: Username with password ( dockerhub username:password ).
SonarQube: Secret Text ( Copied SonarQube token).
Kubernetes: Secret Text ( jenkins service account token).
4. Adding Sonar Scanner in Jenkins:
To configure sonarqube server go to Jenkins server > Dashboard > Manage Jenkins > System
Under SonarQube server click on add SonarQube
Add name= sonar-server
url= <sonarserver-ip>:9000
Select created credential as token. Click Apply.
5. Configure Nexus:
Add URL to pom.xml
To configure nexus first go to nexus > browse > copy maven releases url and paste it in the <maven-release> url section inside pom.xml of source code.
For maven-snapshot copy the url from nexus > browse > copy maven-snapshot url and paste in the <maven-snapshots> url section inside pom.xml of source code.
<distributionManagement> <repository> <id>maven-releases</id> <url>http://3.110.172.144:8081/repository/maven-releases/</url> </repository> <snapshotRepository> <id>maven-snapshots</id> <url>http://3.110.172.144:8081/repository/maven-snapshots/</url> </snapshotRepository> </distributionManagement>
Nexus Credentials:
Go to Jenkins > Dashboard > Manage Jenkins > Managed Files > Click Add new config > Id= maven-settings > Next
You will get the maven settings file, inside file find the <server> </server> section and uncomment it. And make 2 copies of it.
<server> <id>maven-releases</id> <username>nexus_username</username> <password>nexus_password</password> </server> <server> <id>maven-snapshots</id> <username>nexus_username</username> <password>nexus_password</password> </server>
Click Save.
6. Creating Jenkins Pipeline:
Click Dashboard > New Item > Project name > select pipeline > OK
Click Discard old builds > Max build =2
Next create pipeline:
pipeline { agent any tools{ maven 'maven3' } parameters { choice(name: 'DEPLOY_ENV', choices: ['blue', 'green'], description: 'Choose which environment to deploy: Blue or Green') choice(name: 'DOCKER_TAG', choices: ['blue', 'green'], description: 'Choose the Docker image tag for the deployment') booleanParam(name: 'SWITCH_TRAFFIC', defaultValue: false, description: 'Switch traffic between Blue and Green') } environment { IMAGE_NAME = "shubzz/bankapp" TAG = "${params.DOCKER_TAG}" // The image tag now comes from the parameter KUBE_NAMESPACE = 'webapps' SCANNER_HOME= tool 'sonar-scanner' } stages { stage('Git Checkout') { steps { git branch: 'main', credentialsId: 'git-cred', url: 'https://github.com/shubzz-t/Blue-Green-Deployment-CICD.git' } } stage('Compile'){ steps{ sh "mvn compile" } } stage('Tests'){ steps{ sh "mvn test -DskipTests=true" } } stage("Trivy FS scan"){ steps{ sh "trivy fs --format table -o fs.html ." } } stage('SonarQube Analysis') { steps { withSonarQubeEnv('sonar') { sh "$SCANNER_HOME/bin/sonar-scanner -Dsonar.projectKey=multitier -Dsonar.projectName=multitier -Dsonar.java.binaries=target" } } } stage("Sonar Quality Gate Check"){ steps{ timeout(time: 1, unit: 'HOURS') { waitForQualityGate abortPipeline: false } } } stage("Build"){ steps{ sh "mvn package -DskipTests=true" } } stage('Trivy FS Scan') { steps { sh "trivy fs --format table -o fs.html ." } } stage("Pulish to Nexus"){ steps{ withMaven(globalMavenSettingsConfig: 'mvn-global', jdk: '', maven: 'maven3', mavenSettingsConfig: '', traceability: true) { sh 'mvn deploy -DskipTests=true' } } } stage('Docker build') { steps { script { withDockerRegistry(credentialsId: 'docker-cred') { sh "docker build -t ${IMAGE_NAME}:${TAG} ." } } } } stage('Trivy Image Scan') { steps { sh "trivy image --format table -o image.html ${IMAGE_NAME}:${TAG}" } } stage('Docker Push Image') { steps { script { withDockerRegistry(credentialsId: 'docker-cred') { sh "docker push ${IMAGE_NAME}:${TAG}" } } } } stage('Deploy MySQL Deployment and Service') { steps { script { withKubeConfig(caCertificate: '', clusterName: 'devopsshack-cluster', contextName: '', credentialsId: 'k8-token', namespace: 'webapps', restrictKubeConfigAccess: false, serverUrl: 'https://CAFA65A9A51DA473D77884FE6006404A.gr7.ap-south-1.eks.amazonaws.com') { sh "kubectl apply -f mysql-ds.yml -n ${KUBE_NAMESPACE}" // Ensure you have the MySQL deployment YAML ready } } } } stage('Deploy SVC-APP') { steps { script { withKubeConfig(caCertificate: '', clusterName: 'devopsshack-cluster', contextName: '', credentialsId: 'k8-token', namespace: 'webapps', restrictKubeConfigAccess: false, serverUrl: 'https://CAFA65A9A51DA473D77884FE6006404A.gr7.ap-south-1.eks.amazonaws.com') { sh """ if ! kubectl get svc bankapp-service -n ${KUBE_NAMESPACE}; then kubectl apply -f bankapp-service.yml -n ${KUBE_NAMESPACE} fi """ } } } } stage('Deploy to Kubernetes') { steps { script { def deploymentFile = "" if (params.DEPLOY_ENV == 'blue') { deploymentFile = 'app-deployment-blue.yml' } else { deploymentFile = 'app-deployment-green.yml' } withKubeConfig(caCertificate: '', clusterName: 'devopsshack-cluster', contextName: '', credentialsId: 'k8-token', namespace: 'webapps', restrictKubeConfigAccess: false, serverUrl: 'https://CAFA65A9A51DA473D77884FE6006404A.gr7.ap-south-1.eks.amazonaws.com') { sh "kubectl apply -f ${deploymentFile} -n ${KUBE_NAMESPACE}" } } } } 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: 'devopsshack-cluster', contextName: '', credentialsId: 'k8-token', namespace: 'webapps', restrictKubeConfigAccess: false, serverUrl: 'https://CAFA65A9A51DA473D77884FE6006404A.gr7.ap-south-1.eks.amazonaws.com') { sh ''' kubectl patch service bankapp-service -p "{\\"spec\\": {\\"selector\\": {\\"app\\": \\"bankapp\\", \\"version\\": \\"''' + newEnv + '''\\"}}}" -n ${KUBE_NAMESPACE} ''' } echo "Traffic has been switched to the ${newEnv} environment." } } } stage('Verify Deployment') { steps { script { def verifyEnv = params.DEPLOY_ENV withKubeConfig(caCertificate: '', clusterName: 'devopsshack-cluster', contextName: '', credentialsId: 'k8-token', namespace: 'webapps', restrictKubeConfigAccess: false, serverUrl: 'https://CAFA65A9A51DA473D77884FE6006404A.gr7.ap-south-1.eks.amazonaws.com') { sh """ kubectl get pods -l version=${verifyEnv} -n ${KUBE_NAMESPACE} kubectl get svc bankapp-service -n ${KUBE_NAMESPACE} """ } } } } } }
7. Getting Domain Ready
This step is optional step if you don't have any domain name registered you can skip this step.
Go to your domain provider in my case it is GoDaddy.com.
Click on DNS and you will get all the records in that click on the CNAME record.
Add your load balancer dns so that all request coming to your domain will be routed to load balancer dns.
Click Save and it will take time to reflect.
Blue Deployment with Bank name as Goldencat.
New green deployment with traffic switch to green deployment and changed bank name.
Conclusion:
By implementing the blue-green deployment strategy, we’ve achieved a robust and flexible approach for deploying Java MySQL applications on AWS EKS with zero downtime and faster rollback capabilities. Leveraging tools like Terraform for infrastructure, and integrating Git, Maven, SonarQube, Trivy, Nexus, and Docker in the CI/CD pipeline ensures that each release is not only automated but also secure and reliable. This setup empowers teams to continuously deploy with confidence, reducing risks while maintaining seamless application performance.
For more insightful content on technology, AWS, and DevOps, make sure to follow me for the latest updates and tips. If you have any questions or need further assistance, feel free to reach out—I’m here to help!
Streamline, Deploy, Succeed-- Devops Made Simple!☺️
Subscribe to my newsletter
Read articles from Shubham Taware directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Shubham Taware
Shubham Taware
👨💻 Hi, I'm Shubham Taware, a Systems Engineer at Cognizant with a passion for all things DevOps. While my current role involves managing systems, I'm on an exciting journey to transition into a career in DevOps by honing my skills and expertise in this dynamic field. 🚀 I believe in the power of DevOps to streamline software development and operations, making the deployment process faster, more reliable, and efficient. Through my blog, I'm here to share my hands-on experiences, insights, and best practices in the DevOps realm as I work towards my career transition. 🔧 In my day-to-day work, I'm actively involved in implementing DevOps solutions, tackling real-world challenges, and automating processes to enhance software delivery. Whether it's CI/CD pipelines, containerization, infrastructure as code, or any other DevOps topic, I'm here to break it down, step by step. 📚 As a student, I'm continuously learning and experimenting, and I'm excited to document my progress and share the valuable lessons I gather along the way. I hope to inspire others who, like me, are looking to transition into the DevOps field and build a successful career in this exciting domain. 🌟 Join me on this journey as we explore the world of DevOps, one blog post at a time. Together, we can build a stronger foundation for successful software delivery and propel our careers forward in the exciting world of DevOps. 📧 If you have any questions, feedback, or topics you'd like me to cover, feel free to get in touch at shubhamtaware15@gmail.com. Let's learn, grow, and DevOps together!