Deployment with Dynamic Docker Cloud of Jenkins on Kubernetes - Zero Downtime

Deployment with Dynamic Docker Cloud of Jenkins on Kubernetes - Zero Downtime

Hello !! In this article I am going to document the complete process to deploy a website with Jenkins Dynamic Docker Cloud running on Kubernetes.

?? Task Description ??

1. Create container image that’s has Linux and other basic configuration required to run Slave for Jenkins. ( example here we require kubectl to be configured )

2. When we launch the job it should automatically starts job on slave based on the label provided for dynamic approach.

3. Create a job chain of job 1 & job 2 using build pipeline plugin in Jenkins 

4. Job 1 : Pull the GitHub repository automatically when some developers push repository to GitHub and perform the following operations as:

  •  Create the new image dynamically for the application and copy the application code into that corresponding docker image
  •    Push that image to the docker hub (Public repository) 

 ( GitHub code contain the application code and Dockerfile to create a new image )

5. Job 2 ( Should be run on the dynamic friend of Jenkins configured with Kubernetes kubectl command): Launch the application on the top of Kubernetes cluster performing following operations:

  •  If launching first time then create a deployment of the pod using the image created in the previous job. Else if deployment already exists then do rollout of the existing pod making zero downtime for the user.
  •   If Application created first time, then Expose the application. Else don’t expose it.

Pre Configuration Required :

? Linux Operating System

No alt text provided for this image

? Git

No alt text provided for this image

? Docker

No alt text provided for this image

? Kubernetes

No alt text provided for this image

Kubernetes Cluster : Minikube in mycase

This may Help to install Minikube in AWS

Dockerfile For Jenkins Docker Cloud Image :

Docker can build images automatically by reading the instructions from a Dockerfile. A Dockerfile is a text document that contains all the commands a user could call on the command line to assemble an image. Using docker build users can create an automated build that executes several command-line instructions in succession.

#The base code has been picked from https://docs.docker.com/engine/examples/running_ssh_service/ and edit further to setup kubectl. ## 


FROM ubuntu:18.04 
RUN apt-get update && apt-get install -y openssh-server && apt-get install openjdk-8-jre -y 
RUN mkdir /var/run/sshd 
RUN echo 'root:redhat' | chpasswd 
RUN sed -i 's/PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config 
# SSH login fix. Otherwise user is kicked off after login 
RUN sed 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/sshd 
#ENV NOTVISIBLE "in users profile" 
#RUN echo "export VISIBLE-now" ? /etc/profile 
#Kubectl Setup 
RUN apt-get install curl -y 
RUN curl -LO https://storage.googleapis.com/kubernetes-release/release/`curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt`/bin/linux/amd64/kubectl 
RUN chmod +x ./kubectl 
RUN mv ./kubectl /usr/local/bin/kubectl 
EXPOSE 22 
CMD ["/usr/sbin/sshd","-D"]

This Dockerfile have commands to install open-sshserver, openjdk-8-jre and kubectl command to run Kubernetes commands.

To build Image from Dockerfile :

docker build -t image_name  .  // we should be in the directory of Dockerfile
No alt text provided for this image
docker pull 202011501/jenkins_friend_kube:latest

In my case, Kubernetes is running in AWS Cloud.I am going to use the single node Minikube cluster. Deployed jenkins as a pod on top of Kubernetes. I used a yaml file to do the complete comfiguration.

Kubernetes :

? Jenkins on Kubernetes

Created two Persistent Volumes Claims:

jenkins-pvc to make jenkins persistent

deploy-pvc to make the code of deployment persistent

apiVersion: v1
	kind: PersistentVolumeClaim
	metadata:
	  name: jenkins-pvc
	spec:
	  resources: 
	    requests:
	      storage: 5Gi
	  accessModes:
	  - ReadWriteMany
	  storageClassName: standard
	---
	apiVersion: v1
	kind: PersistentVolumeClaim
	metadata:
	  name: deploy-pvc
	spec:
	  resources:
	    requests:
	      storage: 1Gi
	  accessModes:
	  - ReadWriteMany
	  storageClassName: standard

The Persistent Volume Claims use the standard security group.

Created Service NodePort :

	apiVersion: v1
	kind: Service
	metadata:
	  name: jenkins-deploy
	  labels:
	    app: jenkins-app
	spec:
	  ports:
	    - port: 8080
	  selector:
	    app: jenkins-app
	    tier: frontend
	  type: NodePort

This service expose the port 8080 of Pod as NodePort .

Deployment of Jenkins :

apiVersion: apps/v1
	kind: Deployment
	metadata:
	  name: jenkins-deploy
	spec:
	  replicas: 1
	  selector:
	    matchLabels:
	      app: jenkins-app
	    matchExpressions:
	     - {key: tier, operator: In, values: [frontend]}
	  template:
	    metadata: 
	      name: jenkins-pod
	      labels:
	        app: jenkins-app
	        tier: frontend
	    spec:
	      containers:
	      - name: jenkins-con
	        image: 202011501/kube_jenkins:latest
	        ports:
	        - containerPort: 8080
	        volumeMounts:
	        - name: jenkins-pvc
	          mountPath: /var/lib/jenkins
	        - name: deploy-pvc
	          mountPath: /root
	      volumes:
	      - name: jenkins-pvc
	        persistentVolumeClaim:
	          claimName: jenkins-pvc
	      - name: deploy-pvc
	        persistentVolumeClaim:
	        
            claimName: deploy-pvc

In this deployment file the PVC "jenkins-pvc" mounted to /var/lib/jenkins . and "deploy-pvc" to /root. Where i am going to copy the deployment code to directory /root .

Complete jenkins manifest file :

apiVersion: v1
	kind: PersistentVolumeClaim
	metadata:
	  name: jenkins-pvc
	spec:
	  resources: 
	    requests:
	      storage: 5Gi
	  accessModes:
	  - ReadWriteMany
	  storageClassName: standard
	---
	apiVersion: v1
	kind: PersistentVolumeClaim
	metadata:
	  name: deploy-pvc
	spec:
	  resources:
	    requests:
	      storage: 1Gi
	  accessModes:
	  - ReadWriteMany
	  storageClassName: standard
	---
	apiVersion: v1
	kind: Service
	metadata:
	  name: jenkins-deploy
	  labels:
	    app: jenkins-app
	spec:
	  ports:
	    - port: 8080
	  selector:
	    app: jenkins-app
	    tier: frontend
	  type: NodePort
	---
	apiVersion: apps/v1
	kind: Deployment
	metadata:
	  name: jenkins-deploy
	spec:
	  replicas: 1
	  selector:
	    matchLabels:
	      app: jenkins-app
	    matchExpressions:
	     - {key: tier, operator: In, values: [frontend]}
	  template:
	    metadata: 
	      name: jenkins-pod
	      labels:
	        app: jenkins-app
	        tier: frontend
	    spec:
	      containers:
	      - name: jenkins-con
	        image: 202011501/kube_jenkins:latest
	        ports:
	        - containerPort: 8080
	        volumeMounts:
	        - name: jenkins-pvc
	          mountPath: /var/lib/jenkins
	        - name: deploy-pvc
	          mountPath: /root
	      volumes:
	      - name: jenkins-pvc
	        persistentVolumeClaim:
	          claimName: jenkins-pvc
	      - name: deploy-pvc
	        persistentVolumeClaim:
              claimName: deploy-pvc

To run manifest file :

jenkins.yml is the file name.

kubectl apply -f jenkns.yml
No alt text provided for this image
No alt text provided for this image

? Configure Kubectl in Jenkins Pod to run the Kubernetes commads from Jenkins Pod.

  • copied the configuration file from Host on which Kubernetes is running to jenkins pod
kubectl cp /root/.kube/config <namespace>/<pod_name>:/root/.kube

replace <namespace> with the name of your namespace and <pod_name> with the name of the pod.

No alt text provided for this image
  • we need these certificate to be authorize to run kubelet command.

? ca.crt - /root/.minikube

No alt text provided for this image

? client.crt - /root/.minikube/profile/minikube/client.crt

? client.key - /root/.minikube/profile/minikube/client.crt

No alt text provided for this image

Everything is configured. Now, we are good to go to our deployment part.kubectl command working from jenkins pod

No alt text provided for this image

? Jenkins is running great !!

No alt text provided for this image

Let's go to web UI of jenkins.

? To get the login password for container we have to go inside the jenkins pod and retrive the password of the jenkins from /root/.jenkins/secretes/initialAdminPasword

kubectl exec -it <name_of_pod> bash // To execute the interactive terminal of jenkins pod

cat /root/.jenkins/secrets/initialAdminPassword //print the password on terminal
No alt text provided for this image

Git :

When Developer commit the code to GitHub.The post-commit Hook of Git automatically push the code to GitHub. Jenkins Poll SCM check the GitHub for code every minute. When Jenkins find the updated code in GitHub then Jenkins triggers the Job 1 due to job chaining Every Job triggers.

post-commit hook:

Add the file in /.git/hooks/post-commit

#!/bin/bash
echo "Ready to Push to GitHub !"
git push

Make file Executable

chmod +x /.git/hooks/post-commit
No alt text provided for this image

Docker:

We have to expose the docker socket of node to a port to use docker from jenkins.

No alt text provided for this image

This image is the status of docker service before we expose the socket of docker

No alt text provided for this image

Highlighted is the path of the file we are going to edit to expose the socket of docker service

-H tcp://0.0.0.0:4243

Added the above line to ExecStart line

No alt text provided for this image
  • We have changed the configuration of service that's the we need to reload daemon and docker service
systemctl daemon-reload
systemctl restart docker.service
No alt text provided for this image
  • When we run status after editing the file
systemctl status docker 
No alt text provided for this image
  • We can see node is listening to port 4243 wit netstat command
netstat  -tnlp
No alt text provided for this image

Now we can go to Jenkins to run our Jobs,

Jenkins :

Install the following plugins from Mange Jenkins ? Plugin Manager

  • SSH Agent
  • Docker

Job 1 : Pull the GitHub repository automatically when some developers push repository to GitHub and perform the following operations as:

  •  Create the new image dynamically for the application and copy the application code into that corresponding docker image
  •    Push that image to the docker hub (Public repository) 

 ( GitHub code contain the application code and Dockerfile to create a new image )

  • GitHub repository URL
No alt text provided for this image
  • Checks for code update in GitHub for every minute
No alt text provided for this image
  • The GitHub code have Dockerfile of webserver and code to webpage.

Dockerfile :

FROM centos:latest
RUN yum install httpd -y
COPY index.html /var/www/html/
CMD /usr/sbin/httpd -DFOREGROUND
EXPOSE 80
  • index.html content.
Done!! It's Working....
No alt text provided for this image

The Dockerfile will build as a image named 202011501/webapplication:latest and send to Docker Hub.

Configure Jenkins Dynamic Docker Cloud as a Friend :

Before we run Job 2 we have to configure dynamic docker cloud in jenkins as Jenkins friend.

Manage Jenkins ? Configure Nodes and Cloud ? New Cloud

Here we go,

Provide the Docker Host URI - where your docker service is running.

No alt text provided for this image
  • Create a new Docker Agent Templete - This is labeled as jenkins_friend the friend is started with image we already push in docker hub.The Remote Root directory of the node is /root/jenkins. We are going to connect the Docker Cloud with SSH and we have to provide credentials to login the Docker cloud. For simplicity I am going to pick Non Verification Strategy of Host Key Verification.
No alt text provided for this image
No alt text provided for this image

Job 2 ( Should be run on the dynamic friend of Jenkins configured with Kubernetes kubectl command): Launch the application on the top of Kubernetes cluster performing following operations:

  •  If launching first time then create a deployment of the pod using the image created in the previous job. Else if deployment already exists then do rollout of the existing pod making zero downtime for the user.
  •   If Application created first time, then Expose the application. Else don’t expose it.

Restricted the job to run in cloud with tag jenkins_friend

No alt text provided for this image
  • This job will run after Job 1 is build Stable
No alt text provided for this image
  • If deployment "webserver" equals to 0 then we are going to create deployment with the image we push to docker hub in Job 1.Scaled the deployment and created replica.
No alt text provided for this image
export len=$(kubectl get deployments | grep webserver | wc -l)
if [ $len -eq 0 ]
then
kubectl create deployment webserver --image=202011501/webapplication:latest
kubectl scale deployment webserver --replicas=3
kubectl expose deployment webserver --port 80 --type NodePort
kubectl get svc webserver
else
kubectl rollout restart deployment/webserver
kubectl rollout status deployment/webserver
fi

Console Output :

Job 1:

No alt text provided for this image
No alt text provided for this image

Job 2:

No alt text provided for this image

Build Pipeline :

No alt text provided for this image

GitHub Link:

Done this task under guidance of Vimal Daga Sir.In training of  DevOps Assembly Lines by Linux World Informatics Pvt Ltd.

Thank you Mr. Vimal Daga sir for your guidance to do this real use case as a Task with right approach and right education.


Siva Naik Kethavath

DevOps Engineer | MLOps | DataOps | Founding Engineer

4 年
回复

要查看或添加评论,请登录

Siva Naik Kethavath的更多文章

社区洞察

其他会员也浏览了