Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/mfkimbell/end-to-end-devops

End-to-End CI/CD Pipeline. Jenkins polls for changes to GitHub source code for a Tomcat server hosted web application and then executes jobs with Ansible scripts. These scripts create new docker images with the changes, upload those images to DockerHub, and then deploy the new image into multiple pods on Kubernetes with a loadbalancer.
https://github.com/mfkimbell/end-to-end-devops

ansible aws docker ec2 eks eksctl jenkins kubectl kubernetes maven tomcat

Last synced: about 1 month ago
JSON representation

End-to-End CI/CD Pipeline. Jenkins polls for changes to GitHub source code for a Tomcat server hosted web application and then executes jobs with Ansible scripts. These scripts create new docker images with the changes, upload those images to DockerHub, and then deploy the new image into multiple pods on Kubernetes with a loadbalancer.

Awesome Lists containing this project

README

        

## End-to-End DevOps Pipeline

workflow devops

### **Tools Used:**
* `AWS` Hosting of all CI/CD resources and permissions to those resources
* `EC2` Manage instances that run various servers for the application architecture
* `Maven` Managing dependencies and compiling Java code
* `Jenkins` Build and monitor automation of the application deployment process
* `Ansible` Scripts for uploading new Docker Images and Kubernetes pulling of Images
* `Tomcat` Manage server for Java application
* `Docker` Containerize the application and server
* `Kubernetes` Docker container management and fault tolerance with load balancer
* `EKS` AWS management of Kubernetes
* `eksctl` Command line management of EKS clusters on AWS CLI
* `kubectl` Command line management of Kubernetes clusters

### Purpose

Purpose was to make an end-to-end CICD pipeline for an application stored on Github through AWS. Changes are made to the application on Github. Jenkins polls every minute to see if changes occur, so, in under a minute, Jenkins will execute a job via Ansible to build and upload a new version of the Docker container onto DockerHub. After this is complete, another Jenkins CD job will use Ansible to tell the Kubernetes Bootstrap EC2 to pull the new image from DockerHub and deploy it on multiple pods (for high availability) and create a Load balancer users can access to use the application.

Here are all of the EC2 Instances running:

Screenshot 2023-11-13 at 6 33 45 PM

Here are all of the Jenkins Jobs working:

Screenshot 2023-11-13 at 6 42 04 PM

(For the working version of the app, only `RegApp_CI_Job` and `RegApp_CD_Job` are used)

## Documentation from start to end:

## **As I will be deleting all of these AWS resources to prevent charges on my account, I have documented thoroughly this entire process.**

SSH into an ec2 instance, and download jenkins and java (we create security groups that allow access from port 8080 for when we need to access our webapps

"service jenkins start" to start the jenkins server

access jenkins with public ip and port


Screenshot 2023-11-11 at 5 52 00 PM

Next, need to integrate github with jenkins

Install git on jenkins instance

Install github plugin on jenkins GUI

**Roadblock: jenkins uses "master" not "main" by default**

Screenshot 2023-11-11 at 9 56 04 PM

Confiure git on jenkins GUI

Java JDK on Maven from our EC2 server



Integrate maven with jenkins

Here are the three Jenkins Jobs I've created

![Screenshot 2023-11-11 at 4 49 34 PM](https://github.com/mfkimbell/end-to-end-DevOps/assets/107063397/c28fcad5-c0bb-4188-bafd-bc8e78068a27)

Here we can see our build artifacts from the maven webapp job

Now we setup the Tomcat server similarly

Had to remove default permissions, and update it so we can have admin access. Also created some shortcuts for starting and stoppping the server

Screenshot 2023-11-11 at 6 10 35 PM

And then we login to the tomcat manager console

Screenshot 2023-11-11 at 6 14 07 PM

Then we set up jenkins with some secrets to access the tomcat server

Screenshot 2023-11-11 at 9 52 54 PM

Next we create a jenkins job "buildAndDeployServer" that builds the webapp on the tomcat server. After run, we see "webapp" now appears on the tomcat server.

Screenshot 2023-11-11 at 10 13 44 PM

And now we can see our webapp running on Tomcat:

Screenshot 2023-11-11 at 10 31 34 PM

But I ran this job MANUALLY. I want it to run every time I make changes to my code. So we set a build trigger for code changes in Jenkins. We can poll the software configuration management, which is a cron job that checks to see if the there are changes to the code, and only runs if there are changes. The current configuration I have set up uses a wildcard syntax to check the server every minute:

Screenshot 2023-11-11 at 10 38 07 PM

And here is a job running automatically after a code change:

Screenshot 2023-11-11 at 10 52 11 PM

->

Screenshot 2023-11-11 at 10 43 09 PM

I want to deploy Tomcat into a docker container

So I make a new EC2 instance and install docker on it.

We have the interal port as 8080, and the external port as 8081. So we need to update the security group of the docker-host.

Screenshot 2023-11-12 at 10 20 13 AM

Here we open ports 8081-9000:

Screenshot 2023-11-12 at 10 22 27 AM

I write a dockerfile to install java and tomcat on centos and then start the Tomcat server. This was a nightmare to do for some reason. Normally you would just pull a Official Docker Tomcat image, but there was a well-known issue with that apparently:

Screenshot 2023-11-12 at 11 01 05 AM

Changed my docker container to be accessed via password so I can connect to it via jenkins. SSH is best practice, but this was easier.

Screenshot 2023-11-12 at 11 47 47 AM

Now we need to build a Jenkins job to deploy artifacts on our docker container. Specifically, we are sending the .war webapp file over SSH:

Screenshot 2023-11-12 at 12 11 12 PM

Spent a couple hours experimenting here since I again ran into some issues:

Screenshot 2023-11-12 at 1 07 29 PM

Finally managed to get the webapp.war file on my docker instance

Screenshot 2023-11-12 at 1 23 42 PM

Now we create a new folder to copy the artifacts to in the EC2 instance:

Screenshot 2023-11-12 at 1 39 47 PM

We also update our Jenkins job to reflect that:

Screenshot 2023-11-12 at 1 41 58 PM

Now that the file is with our dockerfile, we can update it to send the .war file to the tomcat webapp folder.

Screenshot 2023-11-12 at 1 45 20 PM

Create a new container with these updates:

Screenshot 2023-11-12 at 1 48 32 PM

Now if we go to the URL, (publicIP):8086/webapp we can see our webapp:

Screenshot 2023-11-12 at 1 49 39 PM

However, I built this container manually, and I'd like jenkins to build it for me. So we set it to check for updates every minute, and remove and rebuild if there are changes to the code:

Screenshot 2023-11-12 at 2 12 29 PM

Now we are going to integrate Ansible as a deployment tool. We set up access from our Ansible EC2 to the Docker EC2 with it's private IP since we are in a VPC, so that's allowed:

Screenshot 2023-11-12 at 3 25 39 PM

We can see that Ansible was successfully able to acces the private IP:

Screenshot 2023-11-12 at 3 58 41 PM

Now we need to make it so Jenkins can integrate with Ansible. We need to make a new Jenkins job that this time excludes the "exec" jobs, since we will be handling the deployment on Ansible.

Screenshot 2023-11-12 at 4 27 27 PM

We give our Anisble EC2 it's own dockerfile with the same instructions as before on building the Tomcat server, build that image, and then build that container.

Screenshot 2023-11-12 at 4 48 32 PM

And the webapp sucessfully runs on the docker container in the Ansible EC2:

Screenshot 2023-11-12 at 4 50 43 PM

Now we need to create an Ansible Playbook to execute these commands automatically. We need to instruct Ansible to instruct the Docker container to pull a container from DockerHub. First off, we need to change the host IPs on the Ansible server so we can access a playbook file on the Ansible server, altering vi/ansible/hosts. We create groups based on where we want the execution to occur:

Screenshot 2023-11-12 at 5 01 53 PM

This is my basic playbook. I create a task to create an image and specify that if this playbook is available it can be executed in the docker directory.

Screenshot 2023-11-12 at 5 17 45 PM

After running the task, we can see the image has been added:

Screenshot 2023-11-12 at 5 20 24 PM

Now, we want to upload our image onto DockerHub. So I login to DockerHub on my Ansible EC2, and then manually push the image of interest.

Screenshot 2023-11-12 at 5 35 11 PM

And now we see it on DockerHub:

Screenshot 2023-11-12 at 5 35 48 PM

Now I'd like to incorporate these steps into Ansible Playbook. I create two new tasks on the playbook to tag and upload the image:

Screenshot 2023-11-12 at 5 40 45 PM

Now I alter my jenkins job to execute the Ansible Playbook, as well as adding a "Poll ACM: * * * * *" so it's run automatically with code changes:

Screenshot 2023-11-12 at 5 51 07 PM

Next, I create another playbook, deploy_regapp.yml, that graps the container from DockerHub.

Screenshot 2023-11-12 at 6 24 21 PM

And once again, the webapp is up and running, this time through Ansible Playbook.

Screenshot 2023-11-12 at 6 34 38 PM

Next, we need to update our playbook to remove current versions so that the container actually gets updated when there are changes.

Screenshot 2023-11-12 at 6 43 28 PM

And we add that playbook to the Jenkins job execs:

Screenshot 2023-11-12 at 6 49 04 PM

Finally, our webapp is up and running again, all done automatically through Jenkins and Ansible whenever a change is made.

Screenshot 2023-11-12 at 6 53 08 PM

Here are all of the Jenkins jobs so far, Copy_Artifacts_onto_Ansible is the recent one I was referring to:

Screenshot 2023-11-12 at 6 58 13 PM

Now, I want to incorporate Kubernetes for recovery/high-availability in case something happens to the Docker container. I make a new EC2 instance, and download awscli and eksctl.

Screenshot 2023-11-13 at 11 52 26 AM

Then I make an IAM role "eksctl" with EC2, Cloudformation, and IAM Full access, as well as administrator access for now.

Screenshot 2023-11-13 at 11 50 07 AM

I run "eksctl create cluster" along with other specifications, and we can see the cluster up and running in Cloudformation:

Screenshot 2023-11-13 at 12 49 45 PM

I run "kubectl run webapp --image=http" to create a webapp pod, and I run "kubectl create deployment demo-nginx --image=nginx --port=80 --replicas=2" to create an "nginx" deployment of two replica pods.

Screenshot 2023-11-13 at 1 03 08 PM

Then, I expose the application with a load balancer on port 80:

Screenshot 2023-11-13 at 1 06 11 PM

Screenshot 2023-11-13 at 1 07 08 PM

Through the external address, we can see the Nginx page:

Screenshot 2023-11-13 at 1 08 44 PM

It's better practice to use Manifest Files, so we'll implement that now. First we create a yml file with pod creation instructions:

Screenshot 2023-11-13 at 1 32 36 PM

->

Screenshot 2023-11-13 at 1 25 17 PM

As well as for a load balancer:

Screenshot 2023-11-13 at 1 38 11 PM

->

Screenshot 2023-11-13 at 1 34 38 PM

Now we want to make a deployment from the Docker container on DockerHub. Previously, we were using a known Docker Container (nginx), however, I want to use MY docker container.

Screenshot 2023-11-13 at 4 27 48 PM

Accessing our webapp from our new load balancer (produced by aws):

Screenshot 2023-11-13 at 4 40 00 PM

Now, because of the Rolling deployment, even if one pod goes down, a new pod will be deployed to take it's place:

Screenshot 2023-11-13 at 4 41 43 PM

Next, we want Ansible to run these commands instead of running them manually. First we want to create groups so Ansible can connect to the private IPs of the EC2 instances:

Screenshot 2023-11-13 at 4 56 51 PM

And now we can see Ansible is able to access all of these servers (by checking uptime):

Screenshot 2023-11-13 at 5 00 51 PM

I make an ansible playbook to execute on kubernetes server:

Screenshot 2023-11-13 at 5 20 06 PM

We also need a playbook for the service/loadbalancer:

Screenshot 2023-11-13 at 5 21 50 PM

Now that it's working, I want to execute these playbooks from Jenkins like before:

Screenshot 2023-11-13 at 5 30 00 PM

Screenshot 2023-11-13 at 5 32 41 PM

We can see before I had no pods up and running, and after running the job they are up and running again:

Screenshot 2023-11-13 at 5 33 28 PM

In hindsight, these should be merged, so now we only need to run kube_deploy.yml:

Screenshot 2023-11-13 at 5 35 36 PM

Screenshot 2023-11-13 at 5 36 16 PM

Now, on Jenkins, we need to update our (previously) deployment job and change it to instead use ansible to create and upload a new image onto DockerHub. The highlighted section is what will be deleted that was previously from the "Copy_Artifacts_Onto_Ansible" job:

Screenshot 2023-11-13 at 5 49 10 PM

We rename our "Deploy on Kubernetes" to "RegApp_CD_Job" and we call our altered "Copy_Artifacts_Onto_Ansible" job "RegApp_CI_Job" for clairty:

Screenshot 2023-11-13 at 5 53 05 PM

I ran these jobs manually, I would like them to run automatically and sequentially:

Screenshot 2023-11-13 at 5 59 29 PM

Screenshot 2023-11-13 at 6 00 34 PM

Additionally, I update my kube_deploy.yml to create new pods if the DockerHub Image has changed:

Screenshot 2023-11-13 at 6 07 20 PM

Finally, we can test the full pipeline. We access the webapp via our loadbalancer on AWS:

Screenshot 2023-11-13 at 6 19 39 PM

Here is the webapp loaded. I'm going to make a change in VS code and commit a change to the title.

Screenshot 2023-11-13 at 6 20 48 PM

Screenshot 2023-11-13 at 6 22 38 PM

We can see the DockerHub Image get updated:

Screenshot 2023-11-13 at 6 24 41 PM

We see the CI and CD jobs get executed:

Screenshot 2023-11-13 at 6 28 43 PM

And we can see the new deployment of the pods:

Screenshot 2023-11-13 at 6 41 30 PM

Finally, we can see the updated WebApp:

Screenshot 2023-11-13 at 6 30 21 PM