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
? Git
? Docker
? Kubernetes
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
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
? 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.
- we need these certificate to be authorize to run kubelet command.
? ca.crt - /root/.minikube
? client.crt - /root/.minikube/profile/minikube/client.crt
? client.key - /root/.minikube/profile/minikube/client.crt
Everything is configured. Now, we are good to go to our deployment part.kubectl command working from jenkins pod
? Jenkins is running great !!
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
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
Docker:
We have to expose the docker socket of node to a port to use docker from jenkins.
This image is the status of docker service before we expose the socket of docker
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
- 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
- When we run status after editing the file
systemctl status docker
- We can see node is listening to port 4243 wit netstat command
netstat -tnlp
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
- Checks for code update in GitHub for every minute
- 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....
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.
- 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.
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
- This job will run after Job 1 is build Stable
- 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.
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:
Job 2:
Build Pipeline :
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.
DevOps Engineer | MLOps | DataOps | Founding Engineer
4 年https://github.com/kethavathsivanaik/task4