Aller au contenu

Sitemap

“Build a self-hosted monitoring pipeline using Prometheus, Grafana, and Kubernetes in a local CI/CD environment.”

To ensure your application is stable and healthy in production, you need effective monitoring. Here is a demonstration to implement a monitoring stack using:

  • Prometheus (metrics collection)
  • Grafana (visualization)
  • Alertmanager (alerts)
  • KIND (Kubernetes in Docker)
  • Jenkins (CI/CD pipeline)

🧱 Prerequisites

  • Node.js app (e.g. Bluerise Weather App)
  • Docker & DockerHub/GitHub Container Registry access
  • Jenkins installed locally
  • KIND installed
  • Helm installed

1. 🛠️ Node.js Application Setup

🔗 Dependencies

Inside /testApp/Bluerise-Weather-App/:

npm install express dotenv axios prom-client

🧩 Modify index.js

Add Prometheus client metrics to the app:

require('dotenv').config();
const express = require('express');
const axios = require('axios');
const path = require('path');
const fs = require('fs');
const client = require('prom-client');  // 📈 Add prom-client

const app = express();
const API_KEY = process.env.WEATHER_API_KEY;
const PORT = process.env.PORT || 3000;

// Middleware
app.use(express.urlencoded({ extended: true }));
app.use(express.static('public'));

// 📊 Prometheus metrics setup
const register = new client.Registry();
client.collectDefaultMetrics({ register }); // collects CPU, memory, etc.

const weatherCheckCounter = new client.Counter({
  name: 'weather_checks_total',
  help: 'Total number of weather data fetches',
});
register.registerMetric(weatherCheckCounter);

// Routes
app.get('/', (req, res) => {
  res.sendFile(path.join(__dirname, 'public', 'index.html'));
});

app.post('/weather', async (req, res) => {
  const city = req.body.city;
  const url = \`http://api.weatherapi.com/v1/current.json?key=${API_KEY}&q=${encodeURIComponent(city)}\`;

  try {
    const response = await axios.get(url);
    const weather = response.data;

    // 👇 Increment weather check counter
    weatherCheckCounter.inc();

    let responseHtml = fs.readFileSync(path.join(__dirname, 'public', 'response.html'), 'utf8');
    responseHtml = responseHtml.replace('{{city}}', weather.location.name);
    responseHtml = responseHtml.replace('{{country}}', weather.location.country);
    responseHtml = responseHtml.replace('{{temp}}', weather.current.temp_c);
    responseHtml = responseHtml.replace('{{condition}}', weather.current.condition.text);
    responseHtml = responseHtml.replace('{{humidity}}', weather.current.humidity);
    responseHtml = responseHtml.replace('{{wind}}', weather.current.wind_kph);

    res.send(responseHtml);
  } catch (err) {
    res.send(\`
      <p>Error: Could not fetch weather data. Please check the city name.</p>
      <a href="/">Try again</a>
    \`);
  }
});

// 📊 Metrics endpoint for Prometheus
app.get('/metrics', async (req, res) => {
  res.set('Content-Type', register.contentType);
  res.end(await register.metrics());
});

app.listen(PORT, () => {
  console.log(\`🌐 Server is running on http://localhost:${PORT}\`);
});

Test locally:

node index.js  
curl http://localhost:3000/metrics

2. 🐳 Containerize the App

📦 Dockerfile

# Use the official Node.js image as the base image
FROM node:18

# Set the working directory inside the container
WORKDIR /usr/src/app

# Copy the package.json and package-lock.json (if available)
COPY package*.json ./

# Install dependencies declared in package.json
RUN npm install 

# Copy the rest of the application code
COPY . .

# Expose the port the app will run on (for example, port 3000)
EXPOSE 3000

# Command to run the app
CMD ["npm", "start"]

3. 🤖 Jenkins Pipeline Setup

📁 Jenkinsfile

pipeline {
    agent any

    environment {
        IMAGE_NAME = 'johnstx/bluerise'
        IMAGE_TAG = 'v1.3'
        REGISTRY_CREDENTIALS_ID = 'dockerhub-login'  // Jenkins credentials ID
    }

    stages {

        stage('Clean Workspace') {
          steps {
                    cleanWs() // Cleans the workspace before running the pipeline
          }
        }

        stage('Checkout') {
            steps {
                checkout scm
            }
        }

        stage('Build Docker Image') {
            steps {
                script {
                    sh "docker build -t ${IMAGE_NAME}:${IMAGE_TAG} ."
                }
            }
        }

        stage('Login to Docker Registry') {
            steps {
                script {
                    withCredentials([usernamePassword(credentialsId: "${REGISTRY_CREDENTIALS_ID}", usernameVariable: 'DOCKER_USER', passwordVariable: 'DOCKER_PASS')]) {
                        sh "echo $DOCKER_PASS | docker login -u $DOCKER_USER --password-stdin"
                    }
                }
            }
        }

        stage('Push Image') {
            steps {
                script {
                    sh "docker push ${IMAGE_NAME}:${IMAGE_TAG}"
                }
            }
        }
    }

    post {
        always {
            sh 'docker logout'
        }
    }
}

4. ☸️ Kubernetes Setup (KIND)

🧪 Create Cluster

kind create cluster --name bluerise

📂 Apply Deployment & Service

Create bluerise-deploy.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: bluerise
spec:
  replicas: 1
  selector:
    matchLabels:
      app: bluerise
  template:
    metadata:
      labels:
        app: bluerise
    spec:
      containers:
        - name: bluerise
          image: johnstx/bluerise:v1.0
          ports:
            - containerPort: 3000
---
apiVersion: v1
kind: Service
metadata:
  name: bluerise
  labels:
    app: bluerise
spec:
  ports:
    - port: 3000
      targetPort: 3000
  selector:
    app: bluerise
kubectl apply -f bluerise-deploy.yaml

5. 📊 Prometheus + Grafana Monitoring Stack

🎯 Add Helm Repo & Install Stack

helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update
kubectl create namespace monitoring
helm install monitors prometheus-community/kube-prometheus-stack -n monitoring

6. 📡 Create ServiceMonitor

Save as servicemonitor.yaml:

apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: bluerise-monitor
  labels:
    release: monitors
spec:
  selector:
    matchLabels:
      app: bluerise
  endpoints:
    - port: http
      path: /metrics
      interval: 15s
  namespaceSelector:
    matchNames:
      - default
kubectl apply -f servicemonitor.yaml

7. 🔎 Access Prometheus & Grafana

🧭 Prometheus

kubectl port-forward svc/monitors-kube-prometheus-prometheus -n monitoring 9090

Open: http://localhost:9090/targets
Check if bluerise target is UP

check for a query e.g

weather_check_total

Grafana helps plot these metrics into dashboards. Common dashboard panels include:

  • ✅ Request rate over time
  • ⏱️ Request latency (95th percentile)
  • ❌ Error rate (4xx/5xx)
  • 📊 Memory/CPU usage per pod e.t.c

📈 Grafana

kubectl port-forward svc/monitors-grafana -n monitoring 3000:80

Open: http://localhost:3000
Login:

User: admin

Password: prom-operator (or check via kubectl get secret)

A grafana dashboard

Now go ahead and grab those metrics with the Prometheus stack — because what you don’t measure, you can’t improve!!

Grafana #prometheus-stack #ServiceMonitors #Alertmanager #Monitoring #metrics #healthchecks

Sharing lessons and real-world experiences from the trenches of Cloud Infrastructure, Security Automation, DevSecOps, CI/CD, GitOps and Platform Reliability.

More from Johnstx

[

See more recommendations

](https://medium.com/?source=post_page---read_next_recirc--95a64ac5cfe2---------------------------------------)