"Mastering DevOps with a Real-World Netflix Clone: CI/CD, Kubernetes, Docker, Monitoring & DevSecOps in Action"

Search for a command to run...

No comments yet. Be the first to comment.
In this series, I will walk you through building a complete DevOps Mega Project using tools like Git, Docker, Jenkins, AWS, Kubernetes, and Terraform β from development to production deployment.
π Introduction In this blog, Iβll walk you through how I deployed a two-tier Flask web application with a MySQL database using Docker containers on an AWS EC2 instance. This setup ensures container isolation, environment consistency, and real-time c...
Master GitLab CI/CD Variables, Secrets Management, Artifacts & Best Practices π Introduction In the previous article, we learned how GitLab Runners execute CI/CD pipelines and how to configure Self-H

Learn GitLab Runners, Hosted vs Self-Hosted Runners, Runner Registration, Tags, Pipeline Editor & Parallel Jobs π Introduction In the previous article, we created our first GitLab CI/CD pipeline and

Learn Continuous Integration, Continuous Delivery, Pipelines, Stages, Jobs & .gitlab-ci.yml π Introduction In the previous articles, we explored GitLab fundamentals, repository management, and collab

Learn Docker Scout, Multi-Stage Builds, Docker Hardened Images (DHI), SBOM, Docker Model Runner, Ask Gordon AI, and production-ready container security through practical, real-world examples. Introduc

Import Repositories, Mirroring & Repository Management Best Practices π Introduction In the previous articles, we learned the fundamentals of GitLab, created projects, and configured secure authentic

Hello DevOps enthusiasts! π
In this project, we'll walk through the deployment of a fully functional Netflix Clone using modern DevOps practices. Our CI/CD pipeline will be built using Jenkins, which will automate the build, test, and deployment processes. The application itself will run inside Docker containers, orchestrated by a Kubernetes cluster for high availability and scalability.
To ensure observability and maintain system health, weβll integrate Prometheus, Grafana, and Node Exporter to monitor both Jenkins pipelines and Kubernetes metrics in real time.
This blog is a complete, hands-on guide that combines infrastructure automation, containerization, orchestration, and monitoring β everything you'd expect in a real-world DevOps workflow. I hope you find it insightful and valuable!
π» Deploy a T2 Large Ubuntu 22.04 instance on AWS to serve as the base environment.
βοΈ Set up Jenkins, Docker, and Trivy; then run SonarQube in a Docker container.
π Generate an API key from TMDB to fetch movie data for the application.
π Set up Prometheus for monitoring and Grafana for visualizing system metrics.
π Install the Prometheus plugin in Jenkins and connect it with the Prometheus server.
βοΈ Set up SMTP email integration in Jenkins for real-time build notifications.
π§ Add plugins for JDK, Node.js, SonarQube Scanner, and OWASP Dependency Check.
π Define a declarative pipeline script in Jenkins for CI/CD automation.
π Enable OWASP Dependency Check Plugin for vulnerability scanning of dependencies.
π€ Build the Docker image of your app and push it to Docker Hub or another registry.
π¦ Run the Docker container and expose it to make the app accessible.
π‘ Configure Kubernetes master and worker nodes using Ubuntu 20.04 instances.
π₯οΈ Open the app in your browser and verify its functionality and UI.
π Shut down all EC2 instances to avoid unnecessary charges.
ποΈ Implementation Steps
Below is the step-by-step process to implement the entire DevOps pipeline:-
Launch an AWS T2 Large Instance. Use Ubuntu 22.04 as the image. You can create a new key pair or use an existing one. Enable HTTP and HTTPS in the Security Group and open all ports (not recommended for production, but okay for learning purposes).

#!/bin/bash
sudo apt update -y
#sudo apt upgrade -y
wget -O - https://packages.adoptium.net/artifactory/api/gpg/key/public | tee /etc/apt/keyrings/adoptium.asc
echo "deb [signed-by=/etc/apt/keyrings/adoptium.asc] https://packages.adoptium.net/artifactory/deb $(awk -F= '/^VERSION_CODENAME/{print$2}' /etc/os-release) main" | tee /etc/apt/sources.list.d/adoptium.list
sudo apt update -y
sudo apt install temurin-17-jdk -y
/usr/bin/java --version
curl -fsSL https://pkg.jenkins.io/debian-stable/jenkins.io-2023.key | sudo tee \
/usr/share/keyrings/jenkins-keyring.asc > /dev/null
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 -y
sudo apt-get install jenkins -y
sudo systemctl start jenkins
sudo systemctl status jenkins
vi jenkins.sh #make sure run in Root (or) add at userdata while ec2 launch
sudo chmod 777 jenkins.sh
./jenkins.sh # this will installl jenkins
Once Jenkins is installed, go to your AWS EC2 Security Group and open Inbound Port 8080, since Jenkins runs on that port.
Now, grab your Public IP Address of the EC2 instance.
You can access Jenkins in your browser using:
<EC2 Public IP Address:8080>
sudo cat /var/lib/jenkins/secrets/initialAdminPassword


Jenkins will now get installed and install all the libraries.

Create a user click on save and continue.
Jenkins Getting Started Screen.

sudo apt-get update
sudo apt-get install docker.io -y
sudo usermod -aG docker $USER #my case is ubuntu
newgrp docker
sudo chmod 777 /var/run/docker.sock
Once Docker is installed, launch a SonarQube container.
Make sure to open port 9000 in the Security Group, as SonarQube runs on that port.
docker run -d --name sonar -p 9000:9000 sonarqube:lts-community

SonarQube is now successfully up and running.

Log in to SonarQube using the default credentials:
Username: admin
Password: admin
Set your new password.
You will now be redirected to the SonarQube Dashboard.

You will now see the SonarQube Dashboard appear below.

vi trivy.sh
sudo apt-get install wget apt-transport-https gnupg lsb-release -y
wget -qO - https://aquasecurity.github.io/trivy-repo/deb/public.key | gpg --dearmor | sudo tee /usr/share/keyrings/trivy.gpg > /dev/null
echo "deb [signed-by=/usr/share/keyrings/trivy.gpg] 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 -y
Now, let's create an API key from The Movie Database (TMDB).
Open a new browser tab and search for βTMDBβ to get started.

As soon as you access the TMDB site, youβll see a page similar to the one shown below.

Click on βLoginβ at the top-right corner. Youβll be redirected to the login page.
If you donβt have an account, click on βClick hereβ to sign up.
(Since I already had an account, I simply entered my credentials.)

once you create an account you will see this page.

Letβs create an API key, By clicking on your profile and clicking settings.


Once you navigate to the API section, click on βCreate APIβ.
Fill in the required details, and your API key will be generated β just like shown in the image below.


Begin by creating a dedicated system user for Prometheus.
Using separate users for services improves security and simplifies management.
Run the following command to add the Prometheus user:
sudo useradd \
--system \
--no-create-home \
--shell /bin/false prometheus

--system β Creates a system account.
--no-create-home β Skips creation of a home directory for Prometheus.
--shell /bin/false β Disables shell access for the Prometheus user.
prometheus β Adds a user and group named prometheus.
Letβs check the latest version of Prometheus from the download page.
You can use the curl or wget command to download Prometheus.
wget https://github.com/prometheus/prometheus/releases/download/v2.47.1/prometheus-2.47.1.linux-amd64.tar.gz

Next, extract all Prometheus files from the downloaded archive.
tar -xvf prometheus-2.47.1.linux-amd64.tar.gz

In production, the data directory is usually mounted on a separate disk.
But for this tutorial, we'll simply create a /data directory and a separate folder for Prometheus configuration.
sudo mkdir -p /data /etc/prometheus
Now, letβs change the directory to Prometheus and move some files.
cd prometheus-2.47.1.linux-amd64/

First, move the prometheus binary and promtool to /usr/local/bin/.promtool is useful for validating configuration files and Prometheus rules.
sudo mv prometheus promtool /usr/local/bin/
Optionally, move the console libraries to the Prometheus configuration directory.
These templates help create custom dashboards using Go templating, but theyβre not essential if you're just starting out.
sudo mv consoles/ console_libraries/ /etc/prometheus/
Finally, move the example Prometheus configuration file to the designated config directory.
sudo mv prometheus.yml /etc/prometheus/prometheus.yml
To avoid permission issues, you need to set the correct ownership for the /etc/prometheus/ and data directory.
sudo chown -R prometheus:prometheus /etc/prometheus/ /data/
You can delete the archive and a Prometheus folder when you are done.
cd
rm -rf prometheus-2.47.1.linux-amd64.tar.gz

Verify that the Prometheus binary is executable by running the command below:
prometheus --version

For more details and available configuration options, run the Prometheus help command:
prometheus --help
We'll use some of these options while defining the Prometheus service.
To manage Prometheus as a service, weβll create a Systemd unit file, since Systemd is the default service manager on most modern Linux distributions.
sudo vim /etc/systemd/system/prometheus.service

Prometheus.service
[Unit]
Description=Prometheus
Wants=network-online.target
After=network-online.target
StartLimitIntervalSec=500
StartLimitBurst=5
[Service]
User=prometheus
Group=prometheus
Type=simple
Restart=on-failure
RestartSec=5s
ExecStart=/usr/local/bin/prometheus \
--config.file=/etc/prometheus/prometheus.yml \
--storage.tsdb.path=/data \
--web.console.templates=/etc/prometheus/consoles \
--web.console.libraries=/etc/prometheus/console_libraries \
--web.listen-address=0.0.0.0:9090 \
--web.enable-lifecycle
[Install]
WantedBy=multi-user.target

Letβs review key Systemd and Prometheus options:
Restart β Defines if the service should restart on failure, timeout, or exit.
RestartSec β Time delay before the service restarts.
User & Group β Specifies which Linux user/group runs the Prometheus process.
--config.file=/etc/prometheus/prometheus.yml β Path to Prometheus's main config file.
--storage.tsdb.path=/data β Directory where Prometheus stores time-series data.
--web.listen-address=0.0.0.0:9090 β Listens on all network interfaces. Use localhost if reverse proxy (e.g., NGINX) is used.
--web.enable-lifecycle β Enables config reload without restarting the Prometheus process.
To ensure Prometheus starts automatically after reboot, run:
Now, simply start the Prometheus service using the command below:
sudo systemctl enable prometheus
sudo systemctl start prometheus
To check the status of Prometheus run the following command:
sudo systemctl status prometheus

If Prometheus fails to start or runs into issues, use the following command to view logs and identify errors:
journalctl -u prometheus -f --no-pager
Now, open your browser and access Prometheus using your serverβs IP address.
Make sure to append port 9090, like this:
<public-ip:9090>

If you navigate to the Targets tab in Prometheus, youβll see a single target β Prometheus itself.
By default, it scrapes its own metrics every 15 seconds.
To visualize system metrics effectively, we can use Grafana a powerful and flexible visualization tool. It supports various data sources, including the widely-used Prometheus.
Before proceeding, let's ensure that all necessary dependencies are installed.
sudo apt-get install -y apt-transport-https software-properties-common
Open your browser and navigate to http://<your-ip>:3000.
Log in to Grafana using the default credentials:
Username: admin
Password: admin

To start visualizing metrics:
Click on βAdd your first data sourceβ
Choose Prometheus as the data source
In the configuration screen:
Set the URL to http://localhost:9090 (or your Prometheus serverβs IP if itβs remote)
Click Save & Test to validate the connection
Once the data source is connected:
Go to the Dashboards section
Click on Import to load a pre-built dashboard
Enter dashboard ID 1860 and click Load
Select your Prometheus data source from the dropdown
Click Import to load the dashboard
β Youβll now see a full dashboard with CPU, Memory, Disk, and other system metrics in real time!

Ensure Jenkins is up and running on your system.
Go to:
Manage Jenkins β Plugins β Available Plugins
In the search bar, type βPrometheusβ
Look for and install the plugin named:
Prometheus Metrics Plugin
This plugin exposes Jenkins metrics at a /prometheus endpoint, which Prometheus can scrape.

No changes needed in the plugin config just click Apply and Save.
To configure Prometheus for Jenkins, create a static target by adding a job_name with static_configs.
Head over to your Prometheus server to update the config.
sudo vim /etc/prometheus/prometheus.yml
- job_name: 'jenkins'
metrics_path: '/prometheus'
static_configs:
- targets: ['<jenkins-ip>:8080']
Prometheus will scrape metrics from the /prometheus endpoint of your Jenkins server.
Replace <jenkins-ip> with your Jenkins hostβs IP or DNS name.
Make sure port 8080 is the correct port Jenkins is running on.

Before restarting Prometheus, make sure the configuration file is valid.
If everything is correct, youβll see:Checking /etc/prometheus/prometheus.yml β SUCCESS
promtool check config /etc/prometheus/prometheus.yml
Once the configuration is validated, you can reload it without restarting Prometheus by sending a POST request:
curl -X POST http://localhost:9090/-/reload

To ensure Prometheus is scraping Jenkins metrics correctly, check the Targets section:
Open Prometheus in your browser:
http://<your-prometheus-ip>:9090/targets
Under the βTargetsβ tab, look for the job named jenkins.
If the status shows βUPβ, you're all set! π

For a better visualization of Jenkins metrics, follow these steps to import a pre-built Grafana dashboard:
Go to Grafana Web UI β Click on the β+β (Plus) icon in the left sidebar β Select Import Dashboard
In the dashboard import page:
Enter the dashboard ID: 9964
Click Load
Once loaded:
Select your Prometheus data source from the dropdown
Click Import to load the dashboard
β Youβll now see a detailed and real-time Jenkins Monitoring Dashboard, showing performance, system usage, and health metrics.

To enable advanced email notifications in Jenkins:
Go to Manage Jenkins β Plugins β Available Plugins
Search for Email Extension Plugin
Select it and click on Install without restart
This plugin allows Jenkins to send customizable email alerts for build status and job events.

Open Gmail β Click on profile β Manage your Google Account
Go to Security tab β Enable 2-Step Verification
Scroll down β Click on App Passwords
Verify your password when prompted
Select Other (Custom name) β Enter Jenkins β Click Generate
Copy the 16-character app password shown (e.g., xxxx xxxx xxxx xxxx)
Use this password in Jenkins instead of your Gmail password


Go to Manage Jenkins β Configure System
Scroll down to E-mail Notification section
Enter the following details:
SMTP Server: smtp.gmail.com
Default user e-mail suffix: @gmail.com (optional)
Click on Advanced to expand more options
Choose one of the following setups (depending on your preference):
β Use SMTP Authentication
User Name: Your Gmail address
Password: Paste the App Password
β Use TLS
SMTP Port: 587
β Use SMTP Authentication
User Name: Your Gmail address
Password: Paste the App Password
β Use SSL
SMTP Port: 465
(Optional) Set Reply-To Address if needed
Make sure Charset is set to UTF-8
β Click on βTest configuration by sending test e-mailβ to verify setup
Click Apply and then Save
Go to Manage Jenkins β Credentials
Select the appropriate domain ((global) or your specific folder)
Click Add Credentials:
Username: Your Gmail address
Password: Gmail App Password
ID / Description: e.g. gmail-creds
Click OK
Expand the Advanced section under E-mail Notification
Set Default Content Type to:
HTML (text/html)
β This ensures formatted emails with HTML structure
Leave List ID and Precedence Header empty unless needed
Go to Manage Jenkins β Configure System
Scroll down to the Email Extension section
Configure the following:
Optional:
β¬ Enable Debug Mode (for troubleshooting)
β¬ Require Administrator for Template Testing
β¬ Enable watching for jobs
β¬ Allow sending to unregistered users (only if you allow public triggers)
Under Default Triggers, you can select when Jenkins should send emails:
Suggested triggers:
β Failure - 2nd
β Failure - Any (or as per your use case)
β¬ Always, Fixed, Aborted, etc. (enable as per your need)
π‘ Note: The following script is a part of a Jenkins Declarative Pipeline and should be placed inside your
Jenkinsfile.
post {
always {
emailext attachLog: true,
subject: "'${currentBuild.result}'",
body: "Project: ${env.JOB_NAME}<br/>" +
"Build Number: ${env.BUILD_NUMBER}<br/>" +
"URL: ${env.BUILD_URL}<br/>",
to: 'postbox.aj99@gmail.com', #change Your mail
attachmentsPattern: 'trivyfs.txt,trivyimage.txt'
}
}
Before configuring the pipeline, we need to install a few essential plugins in Jenkins to support tools like JDK, SonarQube, Node.js, and Dependency Check.
Follow these steps to install the necessary plugins:
Go to Manage Jenkins β Plugins β Available Plugins
Search and install the following plugins (β Install without restart):
Eclipse Temurin Installer
β For managing and installing JDK versions in Jenkins
SonarQube Scanner
β Required to run code quality analysis with SonarQube
NodeJS Plugin
β Enables managing and installing Node.js versions inside Jenkins builds
β Once installed, these plugins will allow you to integrate code scanning, JS tools, and JDK inside your pipeline.
Once the required plugins are installed, configure the tools globally so they can be used in your Jenkins pipelines.
Go to Manage Jenkins β Global Tool Configuration
Under the JDK section:
Click on βAdd JDKβ
Uncheck β Install automatically (if not needed manually)
Provide a name like: jdk17
OR enable auto-install and select Temurin JDK 17
Under the NodeJS section:
Click on βAdd NodeJSβ
Provide a name like: node16
Check β Install automatically
Select version: 16.x
Scroll down and click Apply then Save


Go to the Jenkins dashboard
Click on βNew Itemβ
Enter the item name as: Netflix
Select Pipeline as the project type
Click OK to create the job
To integrate SonarQube with Jenkins, follow these steps:
Grab the Public IP of your EC2 instance where SonarQube is installed
β SonarQube runs on port 9000, so open http://<your-public-ip>:9000
Go to your SonarQube Web UI:
Click on Administration β Security β Users
Click on Tokens next to your username
Enter a token name (e.g., jenkins-token)
Click Generate
Copy the generated token and save it somewhere safe β you'll need it in Jenkins

In SonarQube, go to Administration β Security β Users β Tokens, enter a name, and click Generate to create your token.

Copy the token generated from SonarQube
Go to Jenkins Dashboard β Manage Jenkins β Credentials
Choose the correct domain (usually (global)), then click Add Credentials
Select Secret Text as the kind
Paste the token into the Secret field
Add an ID like sonar-token and a description (e.g., SonarQube Auth Token)
Click OK to save

Once you click on Create, you will be redirected to the token details page where your token is displayed.

Go to Manage Jenkins β Configure System, scroll to SonarQube servers, click Add, enter a name, SonarQube URL, select the token (by ID), check the injection box, and click Save.

Click on Apply and Save after setting up SonarQube server details.
π§ Configure System: Used to connect and configure external servers like SonarQube.
π§° Global Tool Configuration: Used to configure tools installed via plugins (like JDK, NodeJS, Sonar Scanner).
Next, weβll install the SonarQube Scanner in Global Tool Configuration.

In the SonarQube dashboard, go to Administration β Configuration β Quality Gates
β Create or select a Quality Gate to enforce code standards.
Then navigate to Administration β Configuration β Webhooks
β Add a new Webhook to notify Jenkins after analysis is complete.

Click on Create to add the new webhook in SonarQube.

Add a name (e.g., jenkins) and paste the Jenkins webhook URL in the URL field, then click Create.
Go to your Jenkins pipeline job and paste the script inside the Pipeline Script section.
pipeline {
agent any
tools {
jdk 'jdk17'
nodejs 'node16'
}
environment {
SCANNER_HOME = tool 'sonar-scanner'
}
stages {
stage('clean workspace') {
steps {
cleanWs()
}
}
stage('Checkout from Git') {
steps {
git branch: 'main', url: 'https://github.com/ApurvGujjar07/Netflix-clone.git'
}
}
stage("Sonarqube Analysis") {
steps {
withSonarQubeEnv('sonar-server') {
sh ''' $SCANNER_HOME/bin/sonar-scanner -Dsonar.projectName=Netflix \
-Dsonar.projectKey=Netflix '''
}
}
}
stage("quality gate") {
steps {
script {
waitForQualityGate abortPipeline: false, credentialsId: 'Sonar-token'
}
}
}
stage('Install Dependencies') {
steps {
sh "npm install"
}
}
}
post {
always {
emailext attachLog: true,
subject: "'${currentBuild.result}'",
body: "Project: ${env.JOB_NAME}<br/>" +
"Build Number: ${env.BUILD_NUMBER}<br/>" +
"URL: ${env.BUILD_URL}<br/>",
to: 'gujjarapurv181@gmail.com',
attachmentsPattern: 'trivyfs.txt,trivyimage.txt'
}
}
}

To view the analysis report, go to your SonarQube Server β Projects, and select your project.

The report is now generated with status "Passed", showing that around 3.2k lines were scanned.
For detailed insights, go to the Issues section in SonarQube.

First, we installed the required plugins. Next, go to Dashboard β Manage Jenkins β Global Tool Configuration to configure the tools like JDK, NodeJS, and Sonar Scanner.

Click on Apply and Save to store the tool configuration.
Now go to your job β Configure β Pipeline, add the updated script, and click Build Now to run it.
stage('OWASP FS SCAN') {
steps {
dependencyCheck additionalArguments: '--scan ./ --disableYarnAudit --disableNodeAudit', odcInstallation: 'DP-Check'
dependencyCheckPublisher pattern: '**/dependency-check-report.xml'
}
}
stage('TRIVY FS SCAN') {
steps {
sh "trivy fs . > trivyfs.txt"
}
}
The stage view would look like this

Once the build completes, youβll see a status graph along with any detected vulnerabilities in the SonarQube dashboard.

To build and push Docker images using Jenkins, you need to install a few essential Docker-related plugins.
Go to Dashboard β Manage Jenkins β Plugin Manager β Available Plugins
Search and install the following (β Install without restart):
Docker
Docker Commons
Docker Pipeline
Docker API
docker-build-step
Now, go to Dashboard β Manage Jenkins β Tools β

Go to Dashboard β Manage Jenkins β Credentials
Click on the appropriate domain (usually (global)), then click Add Credentials
Choose Username with password as the kind
Fill in:
Username: Your DockerHub username
Password: Your DockerHub password
ID: e.g. dockerhub-creds (youβll use this in the pipeline)
Click OK to save

stage("Docker Build & Push") {
steps {
script {
withDockerRegistry(credentialsId: 'docker', toolName: 'docker') {
sh "docker build --build-arg TMDB_V3_API_KEY=Aj7ay86fe14eca3e76869b92 -t netflix ."
sh "docker tag netflix adgujjar/netflix:latest"
sh "docker push adgujjar/netflix:latest"
}
}
}
}
stage("TRIVY") {
steps {
sh "trivy image adgujjar/netflix:latest > trivyimage.txt"
}
}
After the build, you'll see the output along with a Dependency-Check trend graph showing vulnerability stats over time.

When you log in to DockerHub, you'll see that a new image has been successfully created and pushed to your repository.

Now, add the following stage to your pipeline to run the container and check if the application is working:
stage("Run Container") {
steps {
sh "docker run -d -p 3000:3000 --name netflix-container adgujjar/netflix:latest"
}
}

Open http://<your-jenkins-public-ip>:8081 in your browser you should see the application running successfully.
π Note:
1).Kubernetes cluster setup and pod creation can vary based on your environment, cloud provider, and specific project needs.
You can follow the steps as shown above, or adjust them according to your infrastructure.
2).Below, Iβm sharing a reference GitHub repo with ready-to-use Kubernetes YAMLs and setup scripts β feel free to use the commands from there as well to complete your setup.https://github.com/LondheShubham153/kubernetes-in-one-shot(itβs not a promotion link just for your reference .
To deploy your application on Kubernetes, letβs set up a basic 2-node cluster and prepare Jenkins to interact with it.
Launch two Ubuntu 20.04 EC2 instances:
Master Node β Name it: master
Worker Node β Name it: worker
Connect to both instances using PuTTY or MobaXterm ,CMD
kubectl on Jenkins Serverkubectl to apply Kubernetes manifestssudo apt update
sudo apt install curl
curl -LO https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl
sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl
kubectl version --client
master)sudo hostnamectl set-hostname K8s-Master
πΉ Install Docker and Setup Permissions
sudo apt-get update
sudo apt-get install -y docker.io
sudo usermod -aG docker ubuntu
newgrp docker
sudo chmod 777 /var/run/docker.sock
πΉ Install Kubernetes Components
sudo apt-get install -y apt-transport-https ca-certificates curl gpg
curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.29/deb/Release.key \
| sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
echo 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.29/deb/ /' \
| sudo tee /etc/apt/sources.list.d/kubernetes.list
sudo apt-get update
sudo apt-get install -y kubelet kubeadm kubectl
sudo apt-mark hold kubelet kubeadm kubectl
πΉ Initialize the Cluster
sudo kubeadm init --pod-network-cidr=10.244.0.0/16
πΉ Configure kubectl for Current User
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
πΉ Apply Flannel CNI
kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
π Copy the kubeconfig file (/home/ubuntu/.kube/config) to your Jenkins machine, so Jenkins can connect to the cluster.
worker)sudo hostnamectl set-hostname K8s-Worker
πΉ Install Kubernetes Components
sudo apt-get install -y apt-transport-https ca-certificates curl gpg
curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.29/deb/Release.key \
| sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
echo 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.29/deb/ /' \
| sudo tee /etc/apt/sources.list.d/kubernetes.list
sudo apt-get update
sudo apt-get install -y kubelet kubeadm kubectl
sudo apt-mark hold kubelet kubeadm kubectl
After you run kubeadm init on the master, it will show you a command like:
kubeadm join <master-ip>:6443 --token <token> --discovery-token-ca-cert-hash sha256:<hash>
π Copy that full command and run it on the worker node to join the cluster.
Copy the config file to Jenkins master or the local file manager and save it

Copy the contents of the kubeconfig file (usually located at:
/home/ubuntu/.kube/config on the K8s Master Node)
Open File Explorer on your Jenkins machine and:
Create a new text file named: secret-file.txt
Paste the copied kubeconfig content into it
Save the file in a known location (e.g., Documents folder)
π Note:
This file will be used to configure Kubernetes access in Jenkins using the Kubernetes plugin.

Go to Manage Jenkins β Manage Credentials
Click on (global) under Jenkins
Click on Add Credentials
In the Kind dropdown, select: Kubeconfig
Upload the secret-file.txt you saved earlier
Give it an ID like k8s-config and a description
Click OK to save

To monitor system-level metrics like CPU, memory, and disk, we'll install Node Exporter on both the master and worker nodes.
Run the following command on both Master (master) and Worker (worker) nodes:
sudo useradd \
--system \
--no-create-home \
--shell /bin/false node_exporter
Run the following command on both Master and Worker nodes to download Node Exporter:
wget https://github.com/prometheus/node_exporter/releases/download/v1.8.1/node_exporter-1.8.1.linux-amd64.tar.gz
π¦ This will download the latest stable version (
v1.8.1) of Node Exporter.
tar -xvf node_exporter-1.8.1.linux-amd64.tar.gz
/usr/local/binsudo mv node_exporter-1.8.1.linux-amd64/node_exporter /usr/local/bin/
rm -rf node_exporter*
node_exporter --version
β You should see the Node Exporter version output in the terminal.
Node Exporter supports many plugins (called collectors). You can list all available options using:
node_exporter --help
π§ For demo purposes, weβll enable the
logindcollector using a systemd unit file.
Run this on both Master and Worker nodes:
sudo vim /etc/systemd/system/node_exporter.service
[Unit]
Description=Node Exporter
Wants=network-online.target
After=network-online.target
StartLimitIntervalSec=500
StartLimitBurst=5
[Service]
User=node_exporter
Group=node_exporter
Type=simple
Restart=on-failure
RestartSec=5s
ExecStart=/usr/local/bin/node_exporter \
--collector.logind
[Install]
WantedBy=multi-user.target

After updating the systemd unit file with:
User=node_exporter
Group=node_exporter
ExecStart=/usr/local/bin/node_exporter --collector.logind
Now run the following commands on both Master and Worker nodes:
sudo systemctl enable node_exporter
sudo systemctl start node_exporter
sudo systemctl status node_exporter
journalctl -u node_exporter -f --no-pager
π Tip: Use this log command if Node Exporter fails to start or shows inactive.

At this stage, Prometheus is scraping only a single default target.
Although Prometheus supports dynamic service discovery for platforms like AWS, GCP, and Kubernetes, weβll keep it simple for now.
π§ In future tutorials, weβll explore Prometheus deployments in cloud-native environments and inside Kubernetes clusters.
To add a static target (like your Node Exporters), follow these steps:
Go to your Prometheus server
Open the config file:
sudo vim /etc/prometheus/prometheus.yml
- job_name: node_export_masterk8s
static_configs:
- targets: ["<master-ip>:9100"]
- job_name: node_export_workerk8s
static_configs:
- targets: ["<worker-ip>:9100"]
By default, Node Exporter will be exposed on port 9100.
Whenever it's not clearly mentioned where to run a command, assume it should be executed on the main instance β i.e., the first EC2 or server where the service (like Prometheus, Jenkins, or Kubernetes Master) was initially set up.
Since lifecycle management is enabled, you can reload Prometheus config via API β no need to restart the service or cause downtime.
π Reminder: Run this on the main Prometheus instance only.
promtool check config /etc/prometheus/prometheus.yml
Once the config is valid, reload Prometheus without downtime using:
curl -X POST http://localhost:9090/-/reload
β This tells Prometheus to re-read the updated config file instantly.
To confirm that your new targets are being scraped, open:
plaintextCopyEdithttp://<your-prometheus-ip>:9090/targets
β If the status shows UP, your Node Exporters are working correctly.

Add the following stage at the end of your Jenkinsfile to automatically deploy the application to your Kubernetes cluster:
stage('Deploy to kubernets'){
steps{
script{
dir('Kubernetes') {
withKubeConfig(caCertificate: '', clusterName: '', contextName: '', credentialsId: 'k8s', namespace: '', restrictKubeConfigAccess: false, serverUrl: '') {
sh 'kubectl apply -f deployment.yml'
sh 'kubectl apply -f service.yml'
}
}
}
}
}
Make sure you have a Kubernetes folder in your repo containing deployment.yml and service.yml
The credentialsId: 'k8s' should match the Kubernetes secret you created in Jenkins earlier
You can verify deployment with:
β Once this stage runs, your app will be live on the Kubernetes cluster! π

Open the following URL in your browser to view the deployed app:
http://<public-ip-of-slave>:<service-port>
β Output: You should see your Netflix Clone application running in the browser β served directly from your Kubernetes cluster!

Access the Netflix


pipeline {
agent any
tools {
jdk 'jdk17'
nodejs 'node16'
}
environment {
SCANNER_HOME = tool 'sonar-scanner'
}
stages {
stage('Clean Workspace') {
steps {
cleanWs()
}
}
stage('Checkout from Git') {
steps {
git branch: 'main', url: 'https://github.com/ApurvGujjar07/Netflix-clone.git'
}
}
stage('SonarQube Analysis') {
steps {
withSonarQubeEnv('sonar-server') {
sh '''$SCANNER_HOME/bin/sonar-scanner \
-Dsonar.projectName=Netflix \
-Dsonar.projectKey=Netflix'''
}
}
}
stage('Quality Gate') {
steps {
script {
waitForQualityGate abortPipeline: false, credentialsId: 'Sonar-token'
}
}
}
stage('Install Dependencies') {
steps {
sh "npm install"
}
}
stage('OWASP FS Scan') {
steps {
dependencyCheck additionalArguments: '--scan ./ --disableYarnAudit --disableNodeAudit', odcInstallation: 'DP-Check'
dependencyCheckPublisher pattern: '**/dependency-check-report.xml'
}
}
stage('Trivy FS Scan') {
steps {
sh "trivy fs . > trivyfs.txt"
}
}
stage('Docker Build & Push') {
steps {
script {
withDockerRegistry(credentialsId: 'docker', toolName: 'docker') {
sh "docker build --build-arg TMDB_V3_API_KEY=AJ7AYe14eca3e76864yah319b92 -t netflix ."
sh "docker tag netflix adgujjar/netflix:latest"
sh "docker push adgujjar/netflix:latest"
}
}
}
}
stage('Trivy Image Scan') {
steps {
sh "trivy image adgujjar/netflix:latest > trivyimage.txt"
}
}
stage('Deploy to Container') {
steps {
sh "docker run -d --name netflix -p 8081:80 adgujjar/netflix:latest"
}
}
stage('Deploy to Kubernetes') {
steps {
script {
dir('Kubernetes') {
withKubeConfig(
credentialsId: 'k8s',
caCertificate: '',
clusterName: '',
contextName: '',
namespace: '',
serverUrl: '',
restrictKubeConfigAccess: false
) {
sh 'kubectl apply -f deployment.yml'
sh 'kubectl apply -f service.yml'
}
}
}
}
}
}
post {
always {
emailext attachLog: true,
subject: "'${currentBuild.result}'",
body: "Project: ${env.JOB_NAME}<br/>" +
"Build Number: ${env.BUILD_NUMBER}<br/>" +
"URL: ${env.BUILD_URL}<br/>",
to: 'gujjarapurv181@gmail.com',
attachmentsPattern: 'trivyfs.txt,trivyimage.txt'
}
}
}
Once you're done testing and deploying, make sure to terminate all unused EC2 instances (Jenkins, Kubernetes Master, Worker, SonarQube, etc.) to avoid unnecessary charges.
If you feel Iβve made any mistake in this project or something wasnβt clear, please feel free to leave a comment.
Iβll make sure to reply to your message as soon as possible.
Thank you so much for reading! π

Hi, I'm Apurv Gujjar, a passionate and aspiring DevOps Engineer.
This project represents the beginning of my professional journey into the world of DevOps, cloud-native technologies, and CI/CD automation.
From writing pipelines and configuring SonarQube to deploying Dockerized applications on Kubernetes β this was more than a technical challenge, it was a learning milestone.
If you're reading this β thank you for being a part of my journey. π
On the right, youβll see a small glimpse of who's behind the terminal. π (Photo goes here)
π§ Email: gujjarapurv181@gmail.com
π GitHub: github.com/ApurvGujjar07
πΌ LinkedIn: linkedin.com/in/apurv-gujjar
π‘ If you found this project useful, or have any suggestions or feedback, feel free to reach out or drop a comment Iβd love to connect and improve.
This is just the beginning many more builds, deployments, and learnings ahead.