This repo will discuss automating code deployment based on changes in the repository, using a Django Notes App and a NodeJS Todo Application as examples.
Developers can manually run the code on the server.
Automation involves the following stages:
- Build: Compile and prepare the code for deployment.
- Test: Execute tests to ensure code quality.
- Deploy: Push the code changes to the server.
When a developer writes code to be deployed on AWS, the choice of tools and deployment process is crucial. The solution in this repo involves leveraging DevOps tools like GitHub, Docker, Docker Hub, Jenkins, and AWS cloud.
Here's my proposed methodology:
Let's use Jenkins to create a pipeline using Groovy. There are three options:
- Create a FreeStyle Item
- Develop a pipeline triggered by a script
- Establish a declarative pipeline using the pipeline from source control (GitHub). This method checks for correct syntax usage.
To start with, let's create an AWS EC2 Instance. I used Ubuntu Server 22.04 LTS as an image and allow SSH traffic from anywhere to connect to this instance. Refer to the following 4 images for further guidance:
Now, connect to EC2 Instance via local terminal.
Use to following commands to install Jenkins.
First, update application packaging tool.
sudo apt update
Then install Java.
sudo apt install openjdk-11-jre
Check if it is installed or not
java -version
Now, use the following commands to install Jenkins and get it started as a service.
sudo wget -O /usr/share/keyrings/jenkins-keyring.asc \
https://pkg.jenkins.io/debian-stable/jenkins.io-2023.key
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
sudo apt-get update
sudo apt-get install jenkins
Check is Jenkins is running.
systemctl status jenkins
Go to EC2 Instance
-> Security Group
. Then add an inbound rule allowing port 8080, on which Jenkins is running, to be accessible anywhere using the public IP.
Now, go to <public ip of EC2 instance>:<Port on which Jenkins is running>
In our case, the url of Jenkins is: http://44.204.28.208:8080/
Type the following command to get the initial password.
sudo cat /var/lib/jenkins/secrets/initialAdminPassword
Create the first admin user after the prompt.
Create an item on Jenkins from the menu having the following specification. Select Pipeline and choose the relevant name for it.
After that, set the specification of the pipeline as shown:
Now, create a dummy Groovy to check if pipeline is working or not.
- To push the image to Docker Hub, login via Shell. For that, we have to put both username and password. Password being a sensitive information can't be put in Groovy so I had to find a workaround.
Go to Manage Jenkins
-> Security
-> Configure Credential
and add in the credential in Global Credentials
.
To catch those environment variables in the code, use the below code snippet.
steps {
echo "Pushing the image to Docker Hub"
withCredentials([usernamePassword(credentialsId:"dockerHub", passwordVariable:"dockerHubPass", usernameVariable:"dockerHubUser")]) {
sh "docker tag my-notes-app ${env.dockerHubUser}/my-notes-app:latest"
sh "docker login -u ${env.dockerHubUser} -p ${env.dockerHubPass}"
sh "docker push ${env.dockerHubUser}/my-notes-app:latest"
}
}
Assigning a unique ID to each credential from which later we can extract Username and Password.
- While using the below command to start the deployment of the app, I encountered a second problem.
steps {
echo "Deploying the container" sh "docker run -d -p 8000:8000
}
After rebuilding, the port 8000 shows its already being used. This creates a problem as the goal is to auto deploy the pushed changes. So, I came up with a workaround which is docker-compose.yaml
file.
version : "3.3"
services :
web :
image : priyansh2120/my-notes-app:latest
ports :
- "8000:8000"
By using the following command, the container can be started.
docker-compose up
If this can preceded by the following command, we can easily stop the existing running app, if it is running
docker-compose down && docker-compose up -d
The final JenkinsFile should look like this:
pipeline {
agent any
stages {
stage("Clone code") {
steps {
echo "Cloning the code"
git url:"https://github.com/priyansh2120/django-notes-app-for-deployment.git", branch: "main"
}
}
stage("build") {
steps {
echo "Building the image"
sh "docker build -t my-notes-app ."
}
}
stage("Push to Docker Hub") {
steps {
echo "Pushing the image to Docker Hub"
withCredentials([usernamePassword(credentialsId:"dockerHub", passwordVariable:"dockerHubPass", usernameVariable:"dockerHubUser")]){
sh "docker tag my-notes-app ${env.dockerHubUser}/my-notes-app:latest"
sh "docker login -u ${env.dockerHubUser} -p ${env.dockerHubPass}"
sh "docker push ${env.dockerHubUser}/my-notes-app:latest"
}
}
}
stage("Deploy") {
steps {
echo "Deploying the container"
sh "docker-compose down && docker-compose up -d"
}
}
}
}
- The final problem encountered is, if changes need to be made in JenkinsFile itself, it has to be done in Jenkins. We can do it from GitHub itself by following the below option which can be accessed by going to configure the item.
The last step is to trigger automatic deployment whenever developer pushes to the main branch which can be don through the concept of Webhooks.
Add the webhook as follows:
Check is webhooks are added or not
Now, whenever the developer will push to the main branch, the deployment will be automatic.