Skip to content

Latest commit

 

History

43 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

image

TASK APP

A full-stack task management application built incrementally over 12 weeks,
demonstrating real-world DevOps and Cloud Engineering workflows.


📌 Project Overview

This project demonstrates how to implement DevOps and Cloud Engineering workflows starting from basic to advanced tools. The project spans 12 weeks, with each week introducing new tools and updates to improve the workflow and showcase real DevOps practices.


🔋 Technologies Used

Core

Git GitHub Node.js HTML5 CSS3 JavaScript

CI/CD

GitHub Actions Jenkins

📦 Containers & Infrastructure

Docker Kubernetes Terraform

☁️ Cloud

Microsoft Azure


Week 1 — Project Files and Dependencies

  1. Install Node.js and restart your computer

  2. Verify installation: node -v && npm -v

  3. Create Frontend and Backend folders

  4. Create html, css, and js files inside the frontend folder

  5. Create server.js and package.json inside the backend

  6. Install dependencies:

    cd backend
    npm install

    This creates: node_modules/ and package-lock.json

  7. Start the server:

    node server.js

    You should see: Server running on http://localhost:3000

  8. Test the API — open in your browser:

    http://localhost:3000/tasks
    

    You should see an empty array []

Outcome:

  • ✅ Frontend working locally
  • ✅ Backend working locally
  • ✅ API endpoint returning empty array

Week 2 — Git & GitHub

  1. Initialize git from the main project folder:

    git init
    git status
  2. Create a GitHub repo named Task App (leave all defaults) and create

  3. Connect your local project to the repo:

    git remote add origin https://github.com/iampryce/Task-App.git
  4. Push local code to GitHub:

    git branch -M main
    git add .
    git commit -m "Task App frontend + backend"
    git push -u origin main

Week 3 — CI Pipeline with GitHub Actions

Objectives

Create a YAML workflow file that automatically runs when code is pushed to GitHub:

  • Checks out the repo
  • Sets up Node.js
  • Installs backend dependencies
  • Simulates a test
  • Shows logs
  • ✅ Pipeline green if everything works — ❌ fails if something breaks

Steps

  1. Create the workflow folder:

    mkdir -p .github/workflows
  2. Create the CI file:

    touch .github/workflows/ci.yml

    The ci.yml file tells GitHub what to do automatically when code is pushed.

  3. Write or copy the YAML config into ci.yml

  4. Push to GitHub:

    git add .github/workflows/ci.yml
    git commit -m "Week 2: Add CI pipeline"
    git push
  5. Watch it run:

    • Go to your GitHub repo → Actions tab
    • Click Week 2 CI Pipeline
    • You should see: Checkout ✅ Setup Node.js ✅ Install dependencies ✅ Simulate test ✅

Week 4 — Jenkins (Enterprise-style CI/CD)

What is Jenkins?

GitHub Actions Jenkins
Managed by GitHub servers Self-managed server

Objectives

  • Launch a Linux server
  • Install Jenkins
  • Access Jenkins via browser
  • Connect Jenkins to GitHub repo
  • Run first Jenkins build automatically

Steps

1. Launch a VM

Allow inbound traffic: SSH (22), HTTP (80), Custom TCP: 8080 (Jenkins)

2. Connect to Server and Install Java

sudo apt update
sudo apt install fontconfig openjdk-21-jre
java -version

3. Install Node.js

sudo apt update
sudo apt install nodejs npm -y

4. Install Jenkins

Use the official docs for Ubuntu/Debian: https://www.jenkins.io/download/

# Add Jenkins key
sudo wget -O /etc/apt/keyrings/jenkins-keyring.asc \
  https://pkg.jenkins.io/debian-stable/jenkins.io-2026.key

echo "deb [signed-by=/etc/apt/keyrings/jenkins-keyring.asc]" \
  https://pkg.jenkins.io/debian-stable binary/ | sudo tee \
  /etc/apt/sources.list.d/jenkins.list > /dev/null

# Install
sudo apt update
sudo apt install jenkins

# Enable and start
sudo systemctl enable jenkins
sudo systemctl start jenkins
sudo systemctl status jenkins

5. Access Jenkins

Open in browser: http://your-IP:8080

Get the default password:

sudo cat /var/lib/jenkins/secrets/initialAdminPassword

Complete setup → Install suggested plugins → Create admin user → Save

6. Build Jenkins Pipeline Job

  1. Create new item → name it task-app-jenkins-ci → select Pipeline
  2. Configure: Scroll to Pipeline → choose Pipeline script from SCM → SCM: Git → add repo URL → change branch to main → set script path to Jenkinsfile → Save
  3. Create Jenkinsfile with pipeline script and push:
    git add Jenkinsfile
    git commit -m "Add Jenkins pipeline"
    git push
  4. Go to Jenkins → click Build Now to verify

7. Enable GitHub Webhook Trigger

In Jenkins job → ConfigureBuild Triggers → ✅ GitHub hook trigger for GITScm polling → Save

In GitHub → SettingsWebhooks → Add Webhook:

  • URL: http://yourIP:8080/github-webhook/
  • Content Type: application/json
  • Events: Just the push event

Test it:

git add .
git commit -m "Test Jenkins webhook"
git push

Jenkins will now build automatically on every push.


Week 5 — Docker

What is Docker?

Docker packages your application and all its dependencies into a single standardized container that runs consistently across any environment — from a developer's laptop to the cloud.

Steps

1. Create the Dockerfile in the root of your project

2. Build Docker Image locally

docker build -t task-app .

3. Run the Container

docker run -p 3000:3000 task-app

Open: http://localhost:3000

4. Push Dockerfile to GitHub

git add Dockerfile
git commit -m "Add Dockerfile for containerization"
git push

5. Install Docker on Jenkins Server

sudo apt update
sudo apt install docker.io -y
sudo systemctl start docker
sudo systemctl enable docker

6. Allow Jenkins to Use Docker

sudo usermod -aG docker jenkins
sudo systemctl restart jenkins

7. Update Jenkinsfile to add Docker build stage

Jenkins now: Pull repo → Install dependencies → Verify Node → Build Docker image

8. Verify the Docker build in Jenkins console output

9. Confirm the image exists on the server:

docker images

Week 5B — Container Registry (Docker Hub)

Objectives

Push Docker images to Docker Hub so they can be accessed from anywhere.

Steps

1. Create a repository on Docker Hub

2. Login to Docker Hub on Jenkins Server

sudo docker login

Use the secure browser login: https://login.docker.com/activate

3. Tag your Docker image

sudo docker images
sudo docker tag [IMAGE_ID] [dockerusername]/[reponame]:task-appv1

4. Push the image

docker push [your-username]/[your-repo-name]:task-appv1

5. Verify on Docker Hub

Go to hub.docker.com → your repository → Tags tab

6. Pull and Run from anywhere

docker pull YOUR_DOCKER_USERNAME/your-repo-name:v1
docker run -p 8080:3000 YOUR_DOCKER_USERNAME/your-repo-name:v1

7. Automate in Jenkins

Update Jenkinsfile to add:

  • Tag stage → prepares image for Docker Hub
  • Push stage → uploads image automatically
# On Jenkins server
sudo su - jenkins
docker login
git add .
git commit -m "Automate Docker push in Jenkins"
git push

Week 6 — Infrastructure as Code (Terraform + Azure)

What is Terraform?

Terraform lets you define and create cloud infrastructure using code instead of manual clicks in the console.

Objectives

  1. Install Terraform
  2. Connect Terraform to Azure
  3. Create a VM using code
  4. SSH into the server

Steps

1. Install Terraform and Azure CLI

Verify:

terraform -v
az version

Login and configure Azure:

az login --use-device-code
az account show
az account list --output table
az account set --subscription "SUBSCRIPTION_ID"

2. Create Terraform Files

mkdir terraform-ec2
cd terraform-ec2
touch main.tf

Copy the Terraform configuration into main.tf.

3. Generate SSH Key Pair

ssh-keygen -t rsa -b 4096 -m PEM -f ~/.ssh/azure-devops-key.pem
ls ~/.ssh
  • ~/.ssh/azure-devops-key.pem → private key (keep secure)
  • ~/.ssh/azure-devops-key.pem.pub → public key (shared with server)

4. Run Terraform

terraform init    # Downloads provider plugins
terraform plan    # Preview changes
terraform apply   # Create infrastructure

SSH into your server:

ssh -i ~/.ssh/azure-devops-key.pem azureuser@YOUR_PUBLIC_IP

5. Verify on Azure Portal

Open the portal and confirm all resources were created.

6. Clean Up

terraform destroy

Create a .gitignore file to exclude Terraform state files from being pushed to GitHub.


Week 7 / 8 — Kubernetes + Full App (Frontend + Backend)

Overview

At this stage, we move from a single containerized application to a realistic production-ready structure with separate Frontend and Backend services.

Service Responsibility
Frontend User interface (HTML, CSS, JavaScript)
Backend App logic and APIs

What is Kubernetes?

Kubernetes is a system that automatically runs and manages containerized applications. Instead of manually starting containers and restarting them when they fail, Kubernetes takes over these responsibilities and ensures the application is always running as expected.

Key benefits:

  • Automatically restarts failed containers
  • Scales up or down based on demand
  • Manages communication between services using stable internal names (e.g. backend-service)
  • Shifts container management responsibility from the developer to the system

Build Docker Images — Frontend & Backend

  1. Create a Dockerfile inside both frontend/ and backend/ folders (delete the old root Dockerfile)

  2. Build images:

    docker build -t [username]/[backend-repo]:latest ./backend
    docker build -t [username]/[frontend-repo]:latest ./frontend
  3. Verify images:

    docker images
  4. Push to Docker Hub:

    docker push [backend-image]
    docker push [frontend-image]

Kubernetes Steps

1. Install Kubernetes (K3s) on VM

curl -sfL https://get.k3s.io | sh -
sudo kubectl get nodes

2. Backend Deployment

mkdir k8s

Create k8s/backend-deployment.yaml with your backend image, then apply:

sudo kubectl apply -f backend-deployment.yaml
sudo kubectl get pods

3. Create Backend Service

Create k8s/backend-service.yaml, push to GitHub, pull to VM, then apply:

sudo kubectl apply -f backend-service.yaml
sudo kubectl get svc

You should see a ClusterIP and port 3000 listed.

4. Frontend Deployment

Create k8s/frontend-deployment.yaml and k8s/frontend-service.yaml, push and pull to VM, then apply:

sudo kubectl apply -f frontend-deployment.yaml
sudo kubectl apply -f frontend-service.yaml
sudo kubectl get pods
sudo kubectl get svc

5. Verify

curl http://localhost:30007

If you get HTML back — Kubernetes is working and the frontend is running ✅

6. Open firewall port

Update main.tf NSG rules:

security_rule {
  name                       = "allow-frontend"
  priority                   = 1004
  direction                  = "Inbound"
  access                     = "Allow"
  protocol                   = "Tcp"
  source_port_range          = "*"
  destination_port_range     = "30007"
  source_address_prefix      = "*"
  destination_address_prefix = "*"
}
terraform apply

Test in browser: http://YOUR-VM-IP:30007


Jenkins → Kubernetes Integration

Every push triggers a full automated pipeline:

Push Code → Jenkins builds images → Jenkins pushes to Docker Hub → Jenkins updates Kubernetes → Kubernetes pulls new image → Rolling update

Setup Steps

1. Update your Jenkinsfile to build both services, push to Docker Hub, and deploy to Kubernetes

2. Generate a Docker Hub Access Token

  • hub.docker.com → Profile → Account SettingsSecurityNew Access Token
  • Description: jenkins-ci | Access: Read & Write
  • Copy the token immediately — you won't see it again

3. Add credentials to Jenkins

Manage Jenkins → Credentials → System → Global → Add Credentials

Field Value
Kind Username with password
Username your Docker Hub username
Password paste the access token
ID dockerhub-creds
Description Docker Hub Access Token

4. Give Jenkins access to Kubernetes

SSH into your VM:

sudo chmod 644 /etc/rancher/k3s/k3s.yaml
sudo usermod -aG sudo jenkins

5. Add Jenkins to sudoers

sudo visudo

Add at the bottom:

jenkins ALL=(ALL) NOPASSWD: /usr/local/bin/kubectl

6. Restart Jenkins

sudo systemctl restart jenkins

7. Test the full pipeline

git add .
git commit -m "add jenkins cicd"
git push

8. Verify deployment

kubectl get pods

You should see newly created pods with a recent AGE — confirming Jenkins built, pushed, and Kubernetes deployed the update automatically ✅

9. End-to-End Final Test

  1. Make a small change in the HTML file
  2. Commit and push
  3. Verify pods: kubectl get pods
  4. Open the app in the browser and confirm changes are live

Week 9 — Monitoring (Prometheus & Grafana)

Overview

This phase introduces full observability into the system by deploying a monitoring stack inside Kubernetes. Prometheus collects metrics from the cluster, and Grafana provides a visual dashboard to explore them in real time.

Steps

1. Install Helm on your server

curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
helm version

Helm installs complex applications into Kubernetes using pre-configured packages. Instead of writing many YAML files manually, Helm handles it for you.

2. Add Prometheus Repository

helm repo add prometheus-community https://prometheus-community.github.io/helm-charts

Connects Helm to the public repository that contains ready-made configurations for Prometheus and Grafana.

3. Update Helm Repositories

helm repo update

Fetches the latest versions of all packages, ensuring you install the most up-to-date monitoring stack.

4. Configure Kubernetes Access

sudo chmod 644 /etc/rancher/k3s/k3s.yaml

Gives your user permission to read the Kubernetes config file. Without this, Helm and kubectl cannot connect to the cluster.

5. Export KUBECONFIG

export KUBECONFIG=/etc/rancher/k3s/k3s.yaml

Tells your system which config file to use to connect to Kubernetes.

Test the connection:

kubectl get nodes

6. Install Monitoring Stack

helm install monitoring prometheus-community/kube-prometheus-stack

Deploys a complete monitoring system into your Kubernetes cluster in one command — Prometheus, Grafana, and Alertmanager all included.

7. Verify Installation

kubectl get pods

You should see new pods for Prometheus, Grafana, and Alertmanager — confirming the monitoring stack is running ✅

8. Configure Grafana Service Ports

Check the current Grafana service:

kubectl get svc monitoring-grafana

You will see TYPE: ClusterIP — meaning it's internal only and not accessible from outside the cluster.

Create k8s/grafana-service.yaml with the following:

apiVersion: v1
kind: Service
metadata:
  name: monitoring-grafana
  namespace: default

spec:
  type: NodePort

  selector:
    app.kubernetes.io/instance: monitoring
    app.kubernetes.io/name: grafana

  ports:
    - name: http-web
      port: 80
      protocol: TCP
      targetPort: 3000
      nodePort: 32000

Push to GitHub:

git add .
git commit -m "Expose Grafana via NodePort"
git push

Pull on your VM:

cd Task-App
git pull

Delete the old service and apply the new one:

kubectl delete svc monitoring-grafana
kubectl apply -f k8s/grafana-service.yaml
kubectl get svc monitoring-grafana

You should now see: NodePort 80:32000/TCP

Update terraform-ec2/main.tf to open port 32000 and apply:

security_rule {
  name                       = "allow-grafana-nodeport"
  priority                   = 1009
  direction                  = "Inbound"
  access                     = "Allow"
  protocol                   = "Tcp"
  source_port_range          = "*"
  destination_port_range     = "32000"
  source_address_prefix      = "*"
  destination_address_prefix = "*"
}
terraform apply

9. Login to Grafana

Open in your browser: http://YOUR_SERVER_IP:32000

Get the admin password:

kubectl get secret monitoring-grafana -o jsonpath="{.data.admin-password}" | base64 -d ; echo
Field Value
Username admin
Password your output

10. Open Kubernetes Dashboards

In Grafana you should see pre-built folders:

  • Kubernetes / Compute Resources / Node
  • Kubernetes / Compute Resources / Pod
  • Kubernetes / Networking

Launch your app, perform some tasks, then return to Grafana and watch the metrics spike in real time.

Full Pipeline Flow

GitHub → Jenkins → Docker → Kubernetes → App runs → Prometheus collects data → Grafana shows it

Conclusion

This phase introduced the feedback layer into the DevOps workflow. Without monitoring, systems operate blindly — making it difficult to detect issues or understand performance. With Prometheus and Grafana in place, the cluster can now observe itself, tracking CPU usage, memory, pod health, and application resource usage in real time.

The pipeline has evolved beyond just building and deploying. It now includes full observability — a foundational requirement for any production-ready environment.

Contributors

Languages