Compare commits

..
12 Commits
Author SHA1 Message Date
Ubuntu bc848175c7 Update Q3 CI/CD and Q5 monitor documentation with screenshots
CI/CD - Build & Deploy to Ghaymah Cloud / build-and-test (push) Has been cancelled
CI/CD - Build & Deploy to Ghaymah Cloud / deploy-staging (push) Has been cancelled
CI/CD - Build & Deploy to Ghaymah Cloud / deploy-production (push) Has been cancelled
2026-07-28 14:32:16 +00:00
Ubuntu 54af9f97ac Restructure repository for final submission
CI/CD - Build & Deploy to Ghaymah Cloud / build-and-test (push) Has been cancelled
CI/CD - Build & Deploy to Ghaymah Cloud / deploy-staging (push) Has been cancelled
CI/CD - Build & Deploy to Ghaymah Cloud / deploy-production (push) Has been cancelled
2026-07-28 13:22:05 +00:00
Ubuntu b43f9bf706 add Q4
CI/CD - Build & Deploy to Ghaymah Cloud / build-and-test (push) Has been cancelled
CI/CD - Build & Deploy to Ghaymah Cloud / deploy-staging (push) Has been cancelled
CI/CD - Build & Deploy to Ghaymah Cloud / deploy-production (push) Has been cancelled
2026-07-28 03:23:39 +00:00
Ubuntu 8f27b9be75 add Q2 README 2026-07-28 01:32:11 +00:00
mohammed hamdy dd6d4d8be2 Merge pull request #1 from mohammedhamdy102003/develop
Test staging deployment
2026-07-27 19:06:32 +03:00
Ubuntu ce2b42226e Test staging deployment 2026-07-27 16:05:13 +00:00
mohammed hamdy 05b671034a Update GitHub Actions for staging and production deploys 2026-07-27 18:57:40 +03:00
Ubuntu a072e13715 Rename Ghaymah config files 2026-07-27 15:42:42 +00:00
mohammed hamdy 00681492f6 Refactor GitHub Actions workflow for production deployment
Updated workflow to deploy to production instead of staging. Added verification for secrets and improved Docker image build steps.
2026-07-27 18:34:42 +03:00
Ubuntu efd8891d0c Merge branch 'main' of https://github.com/mohammedhamdy102003/Ghaymah-Internship-Tasks 2026-07-27 15:03:36 +00:00
Ubuntu b95a9cacfe add json files 2026-07-27 14:58:38 +00:00
mohammed hamdy 4354ee1a32 Add CI/CD workflow for Ghaymah Cloud deployment 2026-07-27 17:56:41 +03:00
35 changed files with 2569 additions and 1 deletions
+18
View File
@@ -0,0 +1,18 @@
{
"id": "3dc9f5f9-2918-4a24-b329-6414989d141d",
"name": "exam-api-production",
"projectId": "6d1bb957-8a2c-4a88-86f3-6a937b7971f0",
"ports": [
{
"expose": true,
"number": 5000
}
],
"publicAccess": {
"enabled": true,
"domain": "auto"
},
"resourceTier": "t2",
"dockerFileName": "Dockerfile"
}
+18
View File
@@ -0,0 +1,18 @@
{
"id": "e577a6f6-fe08-41aa-9d41-0df0833e35e4",
"name": "exam-api-staging",
"projectId": "6d1bb957-8a2c-4a88-86f3-6a937b7971f0",
"ports": [
{
"expose": true,
"number": 5000
}
],
"publicAccess": {
"enabled": true,
"domain": "auto"
},
"resourceTier": "t1",
"dockerFileName": "Dockerfile"
}
+96
View File
@@ -0,0 +1,96 @@
name: CI/CD - Build & Deploy to Ghaymah Cloud
on:
push:
branches:
- develop
- main
jobs:
# ==========================================================
# Build & Smoke Test
# ==========================================================
build-and-test:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v5
- name: Build Docker image
run: |
docker build -t exam-api:${{ github.sha }} .
- name: Smoke Test
run: |
docker run -d -p 5000:5000 --name smoke-test exam-api:${{ github.sha }}
sleep 8
curl -f http://localhost:5000/health
docker stop smoke-test
docker rm smoke-test
# ==========================================================
# Deploy Staging
# ==========================================================
deploy-staging:
if: github.ref == 'refs/heads/develop'
needs: build-and-test
runs-on: ubuntu-latest
environment: staging
steps:
- uses: actions/checkout@v5
- name: Install Ghaymah CLI
run: |
curl -sSL https://cli.ghaymah.systems/install.sh | bash
- name: Login
run: |
$HOME/ghaymah/bin/gy auth login \
--email "${{ secrets.GHAYMAH_EMAIL }}" \
--password "${{ secrets.GHAYMAH_PW }}"
- name: Use Staging Config
run: |
cp .ghaymah.staging.json .ghaymah.json
- name: Deploy to Staging
run: |
$HOME/ghaymah/bin/gy resource app launch
# ==========================================================
# Deploy Production
# ==========================================================
deploy-production:
if: github.ref == 'refs/heads/main'
needs: build-and-test
runs-on: ubuntu-latest
environment: production
steps:
- uses: actions/checkout@v5
- name: Install Ghaymah CLI
run: |
curl -sSL https://cli.ghaymah.systems/install.sh | bash
- name: Login
run: |
$HOME/ghaymah/bin/gy auth login \
--email "${{ secrets.GHAYMAH_EMAIL }}" \
--password "${{ secrets.GHAYMAH_PW }}"
- name: Use Production Config
run: |
cp .ghaymah.production.json .ghaymah.json
- name: Deploy to Production
run: |
$HOME/ghaymah/bin/gy resource app launch
+270
View File
@@ -0,0 +1,270 @@
# Ghaymah SRE / DevOps Technical Assessment
This repository contains my complete submission for the **Ghaymah SRE / DevOps Technical Assessment**, including all required tasks and bonus challenges.
The project is organized into five independent tasks covering deployment, monitoring, incident analysis, CI/CD, scalability, cloud deployment, and the bonus integration tasks.
---
# Repository Structure
```text
.
├── .github/
│ └── workflows/
│ └── main.yml
├── q1-deploy-monitor/
├── q2-postmortem/
├── q3-cicd/
├── q4-scalability/
├── q5-mithal-monitor/
├── common-mortakaz/
└── common-qabilah/
```
The GitHub Actions workflow used for the CI/CD pipeline is located in:
```text
.github/workflows/main.yml
```
---
# Tasks
## ✅ Q1 – Deploy & Monitoring
Deploy a production-ready monitoring application with health checks and monitoring endpoints.
### Features
- Dockerized application
- Monitoring service
- Health endpoint
- Metrics endpoint
- Deployment configuration
📂 Folder
```text
q1-deploy-monitor/
```
For implementation details, see the README inside the task folder.
🌐 Live Demo
Application
> https://sre-2be55e4567b8.hosted.ghaymah.systems
Health Check
> https://sre-2be55e4567b8.hosted.ghaymah.systems/health
Metrics
> https://sre-2be55e4567b8.hosted.ghaymah.systems/api/metrics
---
## ✅ Q2 – Incident Postmortem
Root cause analysis and postmortem report for the production incident.
### Includes
- Timeline
- Root Cause Analysis
- Impact Assessment
- Corrective Actions
- Preventive Actions
📂 Folder
```text
q2-postmortem/
```
For implementation details, see the README inside the task folder.
---
## ✅ Q3 – CI/CD Pipeline
Implemented a complete CI/CD workflow that automatically builds and deploys the application.
### Features
- GitHub Actions
- Docker Build
- Smoke Testing
- Automatic Staging Deployment
- Manual Approval before Production
- Production Deployment using Ghaymah CLI
📂 Folder
```text
q3-cicd/
```
Workflow Location
```text
.github/workflows/main.yml
```
### CI/CD Pipeline
![CI/CD Pipeline](q3-cicd/images/production-and-staging-deployment.jpg)
### GitHub Actions Results
| Staging Pipeline | Production Pipeline |
|-----------------|---------------------|
| ![](q3-cicd/images/staging-pipeline-success.jpg) | ![](q3-cicd/images/production-pipeline-success.jpg) |
For implementation details, see the README inside the task folder.
🌐 Live Demo
Production
> https://exam-api-production-f87c48f822aa.hosted.ghaymah.systems
---
## ✅ Q4 – Scalability
Scalability proposal and production architecture improvements.
### Includes
- Bottleneck Analysis
- Scaling Strategy
- High Availability
- Load Balancing
- Database Improvements
- Monitoring Recommendations
📂 Folder
```text
q4-scalability/
```
For implementation details, see the README inside the task folder.
---
## ✅ Q5 – Mithal Monitor Deployment
Deploy the monitoring service on Ghaymah Cloud.
### Features
- Docker Deployment
- Public HTTPS Endpoint
- Health Monitoring
- Metrics Collection
- Real-Time Monitoring Dashboard
📂 Folder
```text
q5-mithal-monitor/
```
### Dashboard
![Mithal Dashboard](q5-mithal-monitor/images/mointor-dashboard.jpg)
For implementation details, see the README inside the task folder.
🌐 Live Demo
Application
> https://mithal-monitor-74639f9fbe38.hosted.ghaymah.systems
Health Check
> https://mithal-monitor-74639f9fbe38.hosted.ghaymah.systems/health
Metrics
> https://mithal-monitor-74639f9fbe38.hosted.ghaymah.systems/api/metrics
---
# ⭐ Bonus Tasks
## Common – Mortakaz Integration Proposal
This section contains two integration proposals for products available on **Mortakaz**.
### Includes
- Green Framework Integration Proposal
- Jadl Integration Proposal
📂 Folder
```text
common-mortakaz/
```
---
## Common – Qabilah Profile
My Qabilah profile created for the assessment.
📂 Folder
```text
common-qabilah/
```
Profile
> https://qabilah.com/profile/mohammedhamdy102003/posts
---
# Technologies Used
- Docker
- Python
- Flask
- GitHub Actions
- Ghaymah Cloud
- Ghaymah CLI
- GitHub Environments
- GitHub Secrets
- Gunicorn
- Linux
- Bash
- Git
- GitHub
- GitPasha
- Markdown
---
# Author
**Mohammed Hamdy**
Faculty of Engineering, Sohag University
DevOps / SRE Enthusiast
GitHub
> https://github.com/mohammedhamdy102003/Ghaymah-Internship-Tasks
GitPasha
> https://app.gitpasha.com/mohammedhamdy/ghaymah-exam-mohammedhamdy-sre
+72
View File
@@ -0,0 +1,72 @@
# Integration Proposal - Product 1: Green Framework
## 1. Selected Product Description
- **Product Name:** Green Framework
- **Overview:** A modern PHP framework designed for building web applications and REST APIs efficiently. It helps developers create scalable backend applications with a clean and organized structure.
---
## 2. How it integrates with `ghaymah.systems`
### Proposed Integration
1. Developers can deploy Green Framework applications on Ghaymah cloud infrastructure.
2. Source code can be connected to a CI/CD pipeline to automate building and deployment.
3. Docker containers can be used to package the application for consistent deployments.
4. Monitoring and logging tools can be connected to monitor application health and performance.
---
## 3. Added Value for the End User
- Faster application deployment.
- Easier application maintenance.
- More reliable hosting environment.
- Better visibility into application health and availability.
- Simplified deployment workflow for development teams.
---
## 4. Architecture Sketch
```text
Developer
│
▼
Git Repository
│
▼
CI/CD Pipeline
│
▼
Docker Container
│
▼
Ghaymah Infrastructure
│
▼
Green Framework Application
│
▼
Monitoring & Logs
```
---
## 5. Potential Technical or Commercial Challenges
### Technical
- Managing environment variables securely.
- Handling database migrations during deployments.
- Monitoring application performance across environments.
### Commercial
- Encouraging developers to adopt a new hosting platform.
- Providing clear migration guides for existing applications.
---
## 6. Which product is more viable?
**Green Framework** is the stronger integration candidate because every application built with the framework eventually needs a reliable deployment environment. Integrating it with Ghaymah would provide developers with a complete deployment workflow while supporting modern DevOps practices.
+67
View File
@@ -0,0 +1,67 @@
# Integration Proposal - Product 2: Jadel (جَدَل)
## 1. Selected Product Description
- **Product Name:** Jadel
- **Overview:** A SaaS platform for managing fitness centers and gyms, including memberships, subscriptions, attendance, and daily business operations.
---
## 2. How it integrates with `ghaymah.systems`
### Proposed Integration
1. Host the Jadel platform on Ghaymah infrastructure to improve service availability.
2. Run the application and its database in a secure cloud environment.
3. Connect monitoring tools to observe application health and detect failures.
4. Schedule regular backups to reduce the risk of data loss.
---
## 3. Added Value for the End User
- Higher application availability.
- Improved system reliability.
- Better monitoring for administrators.
- Easier infrastructure management.
- Ability to support business growth without major infrastructure changes.
---
## 4. Architecture Sketch
```text
Gym Staff
│
▼
Jadel Platform
│
▼
Ghaymah Infrastructure
│
┌───┴──────────┐
▼ ▼
Application Database
│
▼
Monitoring & Backups
```
---
## 5. Potential Technical or Commercial Challenges
### Technical
- Protecting customer information.
- Database migration and backup management.
- Maintaining service availability during updates.
### Commercial
- Convincing businesses to migrate from existing systems.
- Training users on the new platform.
---
## 6. Which product is more viable?
Although Jadel would benefit from cloud hosting, its adoption depends mainly on business demand within the fitness industry. Compared to Green Framework, it targets a more specific market, making its integration slightly less flexible.
+4
View File
@@ -0,0 +1,4 @@
Name: Mohammed Hamdy Mahmoud
Qabilah Profile:
https://qabilah.com/profile/mohammedhamdy102003/posts
+179
View File
@@ -0,0 +1,179 @@
# Q1 - Deploy and Monitor an API on Ghaymah Cloud
## Overview
This project demonstrates deploying a Dockerized Python API to **Ghaymah Cloud** and implementing a simple monitoring solution.
The application exposes a `/health` endpoint that is continuously monitored using a Python script. A lightweight HTML dashboard displays the application's health status, response time, uptime, and request statistics.
---
# Project Structure
```
Q1-Deploy-and-Monitoring/
│
├── screenshots/
│ ├── dashboard-local.png
│ └── dashboard-ghaymah.png
│
├── Dockerfile
├── app.py
├── health-check.py
├── dashboard.html
├── monitor-log.csv
├── requirements.txt
├── install-docker.sh
└── README.md
```
---
# Task Requirements
This implementation satisfies all requirements of Question 1.
| Requirement | Status |
|------------|--------|
| Dockerize the API | ✅ |
| Deploy to Ghaymah Cloud | ✅ |
| Implement `/health` endpoint | ✅ |
| Monitoring script (every 30 seconds) | ✅ |
| Monitoring Dashboard | ✅ |
---
# Technologies
- Python
- Flask
- Docker
- HTML
- CSS
- JavaScript
- Ghaymah Cloud
- Ghaymah CLI
---
# API
## Health Endpoint
```
GET /health
```
Example response
```json
{
"status": "healthy"
}
```
The monitoring script periodically sends requests to this endpoint to verify application availability.
---
# Docker
Build the Docker image
```bash
docker build -t exam-api .
```
Run the container
```bash
docker run -d -p 5000:5000 exam-api
```
---
# Monitoring Script
The monitoring script (`health-check.py`) executes every **30 seconds** and performs the following operations:
- Sends an HTTP request to `/health`
- Measures response latency
- Detects application availability
- Records monitoring results
- Updates `monitor-log.csv`
Run the monitor
```bash
python3 health-check.py
```
---
# Monitoring Dashboard
The dashboard was implemented using HTML, CSS and JavaScript.
Displayed metrics include:
- Current application status
- Response time
- Total requests
- Application uptime
- Monitoring history
---
# Deployment using Ghaymah CLI
The application was deployed to **Ghaymah Cloud** using the official CLI.
Deployment workflow:
1. Install Ghaymah CLI
2. Authenticate using account credentials
3. Select deployment configuration
4. Launch the application
Example commands
```bash
gy auth login
cp .ghaymah.production.json .ghaymah.json
gy resource app launch
```
---
# Dashboard Preview
## Local Testing
The dashboard was first tested locally against the application running on the EC2 instance before deploying to Ghaymah Cloud.
![Local Dashboard](screenshots/dashboard-local.png)
---
## Ghaymah Cloud Deployment
After deployment, the dashboard successfully monitored the live application hosted on **Ghaymah Cloud**.
![Ghaymah Dashboard](screenshots/dashboard-ghaymah.png)
---
# Result
The project successfully demonstrates:
- Docker containerization
- Cloud deployment on Ghaymah
- Automated health monitoring
- Response time tracking
- Monitoring dashboard
- Continuous application health verification
This implementation satisfies all requirements of **Question 1 – Deploy and Monitor an API on Ghaymah Cloud**.
View File
@@ -208,4 +208,4 @@ function formatUptime(seconds) {
</body>
</html>
<!-- staging test -->
Binary file not shown.

After

Width:  |  Height:  |  Size: 135 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

+72
View File
@@ -0,0 +1,72 @@
# Q2 – Incident Postmortem
## Scenario
The production API experienced repeated **OOMKilled** events, resulting in approximately **45 minutes** of service downtime.
This document provides a complete incident analysis, including the timeline, root cause, resolution steps, preventive actions, an auto-scaling strategy for Ghaymah Cloud, and an early detection approach using monitoring tools.
---
## Contents
- Incident Summary
- Impact Analysis
- Timeline
- Detection
- Root Cause Analysis
- Resolution
- Lessons Learned
- Preventive Actions
- Ghaymah Auto-Scaling Policy
- Early Detection Strategy
---
## Auto-Scaling on Ghaymah
The proposed scaling policy is designed for applications deployed on the **Ghaymah Container Platform**.
The application maintains at least two running replicas and automatically scales based on CPU and memory utilization.
Scaling thresholds:
- Scale Out:
- CPU > 70%
- Memory > 75%
- Duration: 2 minutes
- Scale In:
- CPU < 30%
- Memory < 40%
- Duration: 10 minutes
This configuration minimizes the probability of future OOMKilled incidents by distributing traffic across multiple container replicas.
---
## Monitoring Strategy
The application should be monitored using **Ghaymah Monitoring** together with the custom monitoring dashboard developed in Question 1.
The monitoring system continuously checks:
- Memory Usage
- CPU Usage
- Container Restart Count
- OOMKilled Events
- HTTP Response Time
- HTTP 5xx Error Rate
- `/health` Endpoint Status
Alerts are generated whenever abnormal resource utilization or unhealthy application status is detected.
---
## Conclusion
The incident was caused by excessive memory consumption that exceeded the container memory limit, resulting in repeated OOMKilled events.
The issue was resolved by rolling back to the previous stable deployment and verifying service recovery using the application's `/health` endpoint.
Future incidents can be mitigated through proactive monitoring, automated alerting, memory-based auto-scaling, and proper pre-production testing.
+216
View File
@@ -0,0 +1,216 @@
# Q3 – CI/CD Pipeline on Ghaymah Cloud
## Overview
This task implements a complete CI/CD pipeline using **GitHub Actions** and **Ghaymah Cloud**.
The pipeline automatically builds the application, performs a smoke test, deploys to the **staging** environment for development changes, and deploys to the **production** environment after merging into the `main` branch with **manual approval** enabled.
---
# Pipeline Workflow
```text
Developer
│
│ Push
▼
GitHub Repository
│
▼
GitHub Actions
│
├── Build Docker Image
├── Smoke Test (/health)
│
├──────────────┐
│ │
▼ ▼
develop main
│ │
Deploy Manual Approval
Staging │
▼
Deploy Production
```
---
# CI Pipeline
The Continuous Integration stage performs:
- Checkout repository
- Build Docker image
- Start container
- Execute smoke test
- Verify `/health` endpoint
- Stop and remove the test container
This ensures that only healthy builds continue to deployment.
---
# CD Pipeline
## Staging Deployment
Triggered automatically when code is pushed to the **develop** branch.
Steps:
1. Install Ghaymah CLI
2. Authenticate using GitHub Secrets
3. Load `.ghaymah.staging.json`
4. Deploy application to the staging environment
Purpose:
- Integration testing
- Validation before production
- Detect deployment issues early
---
## Production Deployment
Triggered after merging into the **main** branch.
Deployment requires **manual approval** through the GitHub Environment protection rules before execution.
Steps:
1. Install Ghaymah CLI
2. Authenticate using GitHub Secrets
3. Load `.ghaymah.production.json`
4. Deploy application to the production environment
Purpose:
- Stable release
- Manual verification before deployment
- Reduce production risks
---
# Pipeline Evidence
The following screenshots demonstrate successful execution of the CI/CD pipeline and deployment for both **Staging** and **Production** environments.
## Staging Pipeline
![Staging Pipeline](images/staging-pipeline-success.jpg)
---
## Production Pipeline
![Production Pipeline](images/production-pipeline-success.jpg)
---
## Deployment Verification
The following screenshot confirms that both **Staging** and **Production** applications were successfully deployed on **Ghaymah Cloud**.
![Deployment Verification](images/production-and-staging-deployment.jpg)
---
# Staging vs Production
| Staging | Production |
|----------|------------|
| Testing environment | Live environment |
| Used by developers | Used by end users |
| Automatic deployment | Protected deployment |
| Safe for validation | High availability |
| Can be updated frequently | Only verified releases |
---
# Manual Approval
Production deployments are protected using **GitHub Environments**.
The workflow pauses before deploying to production until manual approval is granted.
Benefits:
- Prevent accidental deployments
- Final verification before release
- Safer production deployments
---
# Ghaymah CLI Integration
Deployment is performed using the official **Ghaymah CLI**.
Installation:
```bash
curl -sSL https://cli.ghaymah.systems/install.sh | bash
```
Authentication:
```bash
gy auth login \
--email "<EMAIL>" \
--password "<PASSWORD>"
```
Deployment:
```bash
cp .ghaymah.production.json .ghaymah.json
gy resource app launch
```
For the staging environment, the workflow uses:
```bash
cp .ghaymah.staging.json .ghaymah.json
```
before deployment.
---
# GitHub Secrets
Sensitive credentials are stored securely as GitHub Secrets.
Secrets used:
- GHAYMAH_EMAIL
- GHAYMAH_PW
No credentials are stored inside the repository.
---
# Technologies Used
- GitHub Actions
- Docker
- Ghaymah Cloud
- Ghaymah CLI
- GitHub Environments
- GitHub Secrets
---
# Result
The pipeline successfully provides:
- Automated Docker image build
- Automated smoke testing
- Automatic deployment to the Staging environment
- Manual approval before Production deployment
- Secure deployment using GitHub Secrets
- Production deployment using Ghaymah CLI
This implementation demonstrates a complete production-ready CI/CD workflow following modern DevOps and SRE best practices.
Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

+76
View File
@@ -0,0 +1,76 @@
# Q4 - Scalability & Load Balancing
## Objective
Design a scalable architecture capable of handling **15,000 requests per second** on the Ghaymah Container Platform.
---
## Proposed Architecture
The solution uses:
- Ghaymah Load Balancer
- 39 Application Containers
- PostgreSQL Database
- Redis Cache
- Ghaymah Block Storage
- Monitoring and Health Checks
The Load Balancer distributes incoming traffic evenly across all application containers while health checks ensure traffic is only routed to healthy instances.
---
## Container Calculation
Expected Traffic
- 15,000 requests/second
Container Capacity
- 500 requests/second
Required Containers
15000 / 500 = 30
Including 30% reserve capacity
30 × 1.3 = 39 containers
---
## Cold Start Strategy
The platform keeps additional warm containers available to reduce startup latency during sudden traffic spikes.
New containers are automatically created when resource utilization reaches the defined thresholds and are added to the Load Balancer only after passing health checks.
---
## Ghaymah Block Storage
Persistent storage is attached to the database layer to ensure application data survives container restarts and deployments.
Suitable workloads include:
- Databases
- Logs
- Uploaded files
- Stateful applications
---
## Monitoring
The deployment should continuously monitor:
- CPU Utilization
- Memory Usage
- Response Time
- Error Rate
- Container Health
- Restart Count
Alerts should be triggered before resources reach critical levels to prevent service interruption.
+29
View File
@@ -0,0 +1,29 @@
# Container Capacity Calculation
## Given
- Expected Traffic = 15,000 requests/second
- One container capacity = 500 requests/second
- Required headroom = 30%
---
## Step 1
Number of containers without headroom
15000 / 500 = 30 Containers
---
## Step 2
Adding 30% reserve capacity
30 × 1.30 = 39 Containers
---
## Final Result
The application should run **39 containers** to safely handle the expected traffic while maintaining additional capacity for sudden traffic spikes and infrastructure failures.
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

+6
View File
@@ -0,0 +1,6 @@
.venv
__pycache__
*.pyc
.git
monitor-data.json
README.md
+17
View File
@@ -0,0 +1,17 @@
{
"id": "bbf0b1b5-2214-479e-b5e7-00be81bd0e7d",
"name": "mithal-monitor",
"projectId": "6d1bb957-8a2c-4a88-86f3-6a937b7971f0",
"ports": [
{
"expose": true,
"number": 5000
}
],
"publicAccess": {
"enabled": true,
"domain": "auto"
},
"resourceTier": "t1",
"dockerFileName": "Dockerfile"
}
+13
View File
@@ -0,0 +1,13 @@
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 5000
CMD ["gunicorn", "--bind", "0.0.0.0:5000", "app:app"]
+186
View File
@@ -0,0 +1,186 @@
# Mithal Monitor
A Flask-based monitoring application that periodically collects website metrics and exposes them through REST APIs.
The application is containerized with Docker and deployed on Ghaymah Cloud.
---
## Features
- Monitor website availability
- Measure DNS lookup time
- Measure HTTP latency
- Measure search latency
- Check SSL certificate expiration
- Store collected metrics
- REST API for health checks and metrics
- Dockerized deployment
- Deployed on Ghaymah
---
## Tech Stack
- Python
- Flask
- SQLite
- Docker
- Gunicorn
- Ghaymah Cloud
---
## API Endpoints
### Health Check
```http
GET /health
```
Example Response:
```json
{
"status": "healthy"
}
```
---
### Metrics
```http
GET /api/metrics
```
Example Response:
```json
[
{
"timestamp": "2026-07-28 12:11:50",
"uptime": true,
"status_code": 200,
"latency_ms": 34.03,
"dns_ms": 2.38,
"search_latency_ms": 108.42,
"ssl_days_left": 49,
"ssl_expiry": "2026-09-15"
}
]
```
---
## Running Locally
Clone the repository:
```bash
git clone <repo-url>
cd Q5-Mithal-Monitor
```
Install dependencies:
```bash
pip install -r requirements.txt
```
Run the application:
```bash
python app.py
```
The application will be available on:
```text
http://localhost:5000
```
---
## Docker
Build the image:
```bash
docker build -t mithal-monitor .
```
Run the container:
```bash
docker run -p 80:80 mithal-monitor
```
---
## Deployment
The application is deployed on Ghaymah Cloud.
Deployment URL:
```text
https://mithal-monitor-74639f9fbe38.hosted.ghaymah.systems
```
Health Endpoint:
```text
https://mithal-monitor-74639f9fbe38.hosted.ghaymah.systems/health
```
Metrics Endpoint:
```text
https://mithal-monitor-74639f9fbe38.hosted.ghaymah.systems/api/metrics
```
---
## Monitoring Dashboard
The deployed Mithal Monitor application provides a dashboard for monitoring website availability and performance metrics.
The dashboard displays:
- Current service status
- HTTP response latency
- DNS lookup time
- Search latency
- SSL certificate expiration status
- Historical monitoring data
- Recent monitoring records
### Dashboard Preview
![Mithal Monitor Dashboard](images/mointor-dashboard.jpg)
---
## Project Structure
```text
q5-mithal-monitor/
├── app.py
├── monitor.py
├── monitor-data.json
├── requirements.txt
├── Dockerfile
├── templates/
├── images/
│ └── mointor-dashboard.jpg
└── README.md
```
---
## Author
Mohammed Hamdy
+66
View File
@@ -0,0 +1,66 @@
from flask import Flask, jsonify, render_template
import json
import os
import threading
import time
import monitor # our monitor.py — reused directly, not run as a separate process
app = Flask(__name__)
DATA_FILE = "monitor-data.json"
def background_monitor_loop():
"""Runs monitor.monitor() every CHECK_INTERVAL seconds inside this
same process/container, since the Dockerfile only starts one
process (gunicorn running app.py). This replaces running
monitor.py as a separate process, which never actually happened
after deployment (only app.py ran, monitor.py was never invoked)."""
print("Starting background monitor thread...")
while True:
try:
monitor.monitor()
except Exception as e:
print("Monitor thread error:", e)
time.sleep(monitor.CHECK_INTERVAL)
# Start the background thread once, when this module is imported by
# gunicorn. Safe because the Dockerfile runs a single gunicorn worker
# (no --workers flag => defaults to 1), so this thread won't be
# duplicated across multiple worker processes.
monitor_thread = threading.Thread(target=background_monitor_loop, daemon=True)
monitor_thread.start()
@app.route("/")
def home():
return render_template("dashboard.html")
@app.route("/dashboard")
def dashboard():
return render_template("dashboard.html")
@app.route("/api/metrics")
def metrics():
if not os.path.exists(DATA_FILE):
return jsonify([])
with open(DATA_FILE, "r") as f:
data = json.load(f)
return jsonify(data)
@app.route("/health")
def health():
return {
"status": "healthy"
}
if __name__ == "__main__":
app.run(host="0.0.0.0", port=5000, debug=True)
Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

+692
View File
@@ -0,0 +1,692 @@
[
{
"timestamp": "2026-07-28 06:15:26",
"uptime": true,
"status_code": 200,
"latency_ms": 138.66,
"dns_ms": 1.88,
"ssl_expiry": "2026-09-15",
"ssl_days_left": 49,
"search_latency_ms": 83.64
},
{
"timestamp": "2026-07-28 06:16:27",
"uptime": true,
"status_code": 200,
"latency_ms": 77.9,
"dns_ms": 1.0,
"ssl_expiry": "2026-09-15",
"ssl_days_left": 49,
"search_latency_ms": 76.89
},
{
"timestamp": "2026-07-28 06:17:27",
"uptime": true,
"status_code": 200,
"latency_ms": 76.17,
"dns_ms": 0.99,
"ssl_expiry": "2026-09-15",
"ssl_days_left": 49,
"search_latency_ms": 90.49
},
{
"timestamp": "2026-07-28 06:18:27",
"uptime": true,
"status_code": 200,
"latency_ms": 83.58,
"dns_ms": 1.11,
"ssl_expiry": "2026-09-15",
"ssl_days_left": 49,
"search_latency_ms": 75.83
},
{
"timestamp": "2026-07-28 06:19:27",
"uptime": true,
"status_code": 200,
"latency_ms": 78.07,
"dns_ms": 0.82,
"ssl_expiry": "2026-09-15",
"ssl_days_left": 49,
"search_latency_ms": 83.3
},
{
"timestamp": "2026-07-28 06:20:27",
"uptime": true,
"status_code": 200,
"latency_ms": 96.93,
"dns_ms": 1.13,
"ssl_expiry": "2026-09-15",
"ssl_days_left": 49,
"search_latency_ms": 78.55
},
{
"timestamp": "2026-07-28 06:21:28",
"uptime": true,
"status_code": 200,
"latency_ms": 78.4,
"dns_ms": 1.37,
"ssl_expiry": "2026-09-15",
"ssl_days_left": 49,
"search_latency_ms": 76.19
},
{
"timestamp": "2026-07-28 06:22:28",
"uptime": true,
"status_code": 200,
"latency_ms": 88.03,
"dns_ms": 0.89,
"ssl_expiry": "2026-09-15",
"ssl_days_left": 49,
"search_latency_ms": 79.74
},
{
"timestamp": "2026-07-28 06:23:28",
"uptime": true,
"status_code": 200,
"latency_ms": 77.57,
"dns_ms": 1.22,
"ssl_expiry": "2026-09-15",
"ssl_days_left": 49,
"search_latency_ms": 76.56
},
{
"timestamp": "2026-07-28 06:24:28",
"uptime": true,
"status_code": 200,
"latency_ms": 75.44,
"dns_ms": 0.98,
"ssl_expiry": "2026-09-15",
"ssl_days_left": 49,
"search_latency_ms": 83.31
},
{
"timestamp": "2026-07-28 06:25:28",
"uptime": true,
"status_code": 200,
"latency_ms": 80.72,
"dns_ms": 0.9,
"ssl_expiry": "2026-09-15",
"ssl_days_left": 49,
"search_latency_ms": 74.22
},
{
"timestamp": "2026-07-28 06:26:29",
"uptime": true,
"status_code": 200,
"latency_ms": 81.2,
"dns_ms": 0.87,
"ssl_expiry": "2026-09-15",
"ssl_days_left": 49,
"search_latency_ms": 77.07
},
{
"timestamp": "2026-07-28 06:27:29",
"uptime": true,
"status_code": 200,
"latency_ms": 76.89,
"dns_ms": 1.5,
"ssl_expiry": "2026-09-15",
"ssl_days_left": 49,
"search_latency_ms": 74.69
},
{
"timestamp": "2026-07-28 06:28:29",
"uptime": true,
"status_code": 200,
"latency_ms": 78.55,
"dns_ms": 0.84,
"ssl_expiry": "2026-09-15",
"ssl_days_left": 49,
"search_latency_ms": 91.66
},
{
"timestamp": "2026-07-28 06:29:29",
"uptime": true,
"status_code": 200,
"latency_ms": 82.33,
"dns_ms": 1.07,
"ssl_expiry": "2026-09-15",
"ssl_days_left": 49,
"search_latency_ms": 75.03
},
{
"timestamp": "2026-07-28 06:30:30",
"uptime": true,
"status_code": 200,
"latency_ms": 91.92,
"dns_ms": 0.87,
"ssl_expiry": "2026-09-15",
"ssl_days_left": 49,
"search_latency_ms": 76.92
},
{
"timestamp": "2026-07-28 06:31:30",
"uptime": true,
"status_code": 200,
"latency_ms": 80.75,
"dns_ms": 0.9,
"ssl_expiry": "2026-09-15",
"ssl_days_left": 49,
"search_latency_ms": 76.38
},
{
"timestamp": "2026-07-28 06:32:30",
"uptime": true,
"status_code": 200,
"latency_ms": 79.62,
"dns_ms": 0.86,
"ssl_expiry": "2026-09-15",
"ssl_days_left": 49,
"search_latency_ms": 74.62
},
{
"timestamp": "2026-07-28 06:33:30",
"uptime": true,
"status_code": 200,
"latency_ms": 75.97,
"dns_ms": 0.82,
"ssl_expiry": "2026-09-15",
"ssl_days_left": 49,
"search_latency_ms": 82.48
},
{
"timestamp": "2026-07-28 06:34:30",
"uptime": true,
"status_code": 200,
"latency_ms": 79.56,
"dns_ms": 0.88,
"ssl_expiry": "2026-09-15",
"ssl_days_left": 49,
"search_latency_ms": 73.19
},
{
"timestamp": "2026-07-28 06:35:31",
"uptime": true,
"status_code": 200,
"latency_ms": 98.91,
"dns_ms": 0.86,
"ssl_expiry": "2026-09-15",
"ssl_days_left": 49,
"search_latency_ms": 82.02
},
{
"timestamp": "2026-07-28 06:36:31",
"uptime": true,
"status_code": 200,
"latency_ms": 74.78,
"dns_ms": 0.92,
"ssl_expiry": "2026-09-15",
"ssl_days_left": 49,
"search_latency_ms": 72.29
},
{
"timestamp": "2026-07-28 06:37:31",
"uptime": true,
"status_code": 200,
"latency_ms": 77.6,
"dns_ms": 0.89,
"ssl_expiry": "2026-09-15",
"ssl_days_left": 49,
"search_latency_ms": 74.45
},
{
"timestamp": "2026-07-28 06:38:31",
"uptime": true,
"status_code": 200,
"latency_ms": 78.87,
"dns_ms": 1.08,
"ssl_expiry": "2026-09-15",
"ssl_days_left": 49,
"search_latency_ms": 76.15
},
{
"timestamp": "2026-07-28 06:39:31",
"uptime": true,
"status_code": 200,
"latency_ms": 80.61,
"dns_ms": 1.34,
"ssl_expiry": "2026-09-15",
"ssl_days_left": 49,
"search_latency_ms": 72.64
},
{
"timestamp": "2026-07-28 06:40:32",
"uptime": true,
"status_code": 200,
"latency_ms": 80.76,
"dns_ms": 0.85,
"ssl_expiry": "2026-09-15",
"ssl_days_left": 49,
"search_latency_ms": 78.7
},
{
"timestamp": "2026-07-28 06:41:32",
"uptime": true,
"status_code": 200,
"latency_ms": 85.68,
"dns_ms": 0.83,
"ssl_expiry": "2026-09-15",
"ssl_days_left": 49,
"search_latency_ms": 80.57
},
{
"timestamp": "2026-07-28 06:42:39",
"uptime": true,
"status_code": 200,
"latency_ms": 6717.43,
"dns_ms": 0.87,
"ssl_expiry": "2026-09-15",
"ssl_days_left": 49,
"search_latency_ms": 83.14
},
{
"timestamp": "2026-07-28 06:43:39",
"uptime": true,
"status_code": 200,
"latency_ms": 76.05,
"dns_ms": 0.82,
"ssl_expiry": "2026-09-15",
"ssl_days_left": 49,
"search_latency_ms": 75.75
},
{
"timestamp": "2026-07-28 06:44:39",
"uptime": true,
"status_code": 200,
"latency_ms": 78.1,
"dns_ms": 0.8,
"ssl_expiry": "2026-09-15",
"ssl_days_left": 49,
"search_latency_ms": 85.88
},
{
"timestamp": "2026-07-28 06:45:39",
"uptime": true,
"status_code": 200,
"latency_ms": 112.41,
"dns_ms": 0.88,
"ssl_expiry": "2026-09-15",
"ssl_days_left": 49,
"search_latency_ms": 81.98
},
{
"timestamp": "2026-07-28 06:46:40",
"uptime": true,
"status_code": 200,
"latency_ms": 77.23,
"dns_ms": 0.92,
"ssl_expiry": "2026-09-15",
"ssl_days_left": 49,
"search_latency_ms": 73.45
},
{
"timestamp": "2026-07-28 06:47:40",
"uptime": true,
"status_code": 200,
"latency_ms": 79.96,
"dns_ms": 0.85,
"ssl_expiry": "2026-09-15",
"ssl_days_left": 49,
"search_latency_ms": 73.76
},
{
"timestamp": "2026-07-28 06:48:40",
"uptime": true,
"status_code": 200,
"latency_ms": 82.81,
"dns_ms": 0.88,
"ssl_expiry": "2026-09-15",
"ssl_days_left": 49,
"search_latency_ms": 73.3
},
{
"timestamp": "2026-07-28 06:49:40",
"uptime": true,
"status_code": 200,
"latency_ms": 83.33,
"dns_ms": 0.87,
"ssl_expiry": "2026-09-15",
"ssl_days_left": 49,
"search_latency_ms": 72.03
},
{
"timestamp": "2026-07-28 06:50:40",
"uptime": true,
"status_code": 200,
"latency_ms": 81.64,
"dns_ms": 0.85,
"ssl_expiry": "2026-09-15",
"ssl_days_left": 49,
"search_latency_ms": 73.87
},
{
"timestamp": "2026-07-28 06:51:41",
"uptime": true,
"status_code": 200,
"latency_ms": 87.24,
"dns_ms": 0.83,
"ssl_expiry": "2026-09-15",
"ssl_days_left": 49,
"search_latency_ms": 79.31
},
{
"timestamp": "2026-07-28 06:52:41",
"uptime": true,
"status_code": 200,
"latency_ms": 80.97,
"dns_ms": 1.19,
"ssl_expiry": "2026-09-15",
"ssl_days_left": 49,
"search_latency_ms": 77.43
},
{
"timestamp": "2026-07-28 06:53:41",
"uptime": true,
"status_code": 200,
"latency_ms": 81.17,
"dns_ms": 0.98,
"ssl_expiry": "2026-09-15",
"ssl_days_left": 49,
"search_latency_ms": 73.72
},
{
"timestamp": "2026-07-28 06:54:41",
"uptime": true,
"status_code": 200,
"latency_ms": 82.47,
"dns_ms": 0.86,
"ssl_expiry": "2026-09-15",
"ssl_days_left": 49,
"search_latency_ms": 75.56
},
{
"timestamp": "2026-07-28 06:55:41",
"uptime": true,
"status_code": 200,
"latency_ms": 80.53,
"dns_ms": 0.98,
"ssl_expiry": "2026-09-15",
"ssl_days_left": 49,
"search_latency_ms": 80.2
},
{
"timestamp": "2026-07-28 06:56:42",
"uptime": true,
"status_code": 200,
"latency_ms": 83.64,
"dns_ms": 0.82,
"ssl_expiry": "2026-09-15",
"ssl_days_left": 49,
"search_latency_ms": 71.71
},
{
"timestamp": "2026-07-28 06:57:42",
"uptime": true,
"status_code": 200,
"latency_ms": 81.55,
"dns_ms": 0.84,
"ssl_expiry": "2026-09-15",
"ssl_days_left": 49,
"search_latency_ms": 72.56
},
{
"timestamp": "2026-07-28 06:58:42",
"uptime": true,
"status_code": 200,
"latency_ms": 82.6,
"dns_ms": 0.88,
"ssl_expiry": "2026-09-15",
"ssl_days_left": 49,
"search_latency_ms": 78.6
},
{
"timestamp": "2026-07-28 06:59:42",
"uptime": true,
"status_code": 200,
"latency_ms": 79.31,
"dns_ms": 0.86,
"ssl_expiry": "2026-09-15",
"ssl_days_left": 49,
"search_latency_ms": 74.71
},
{
"timestamp": "2026-07-28 07:00:43",
"uptime": true,
"status_code": 200,
"latency_ms": 99.38,
"dns_ms": 0.88,
"ssl_expiry": "2026-09-15",
"ssl_days_left": 49,
"search_latency_ms": 73.06
},
{
"timestamp": "2026-07-28 07:01:43",
"uptime": true,
"status_code": 200,
"latency_ms": 78.82,
"dns_ms": 0.86,
"ssl_expiry": "2026-09-15",
"ssl_days_left": 49,
"search_latency_ms": 79.59
},
{
"timestamp": "2026-07-28 07:02:43",
"uptime": true,
"status_code": 200,
"latency_ms": 97.32,
"dns_ms": 1.02,
"ssl_expiry": "2026-09-15",
"ssl_days_left": 49,
"search_latency_ms": 85.14
},
{
"timestamp": "2026-07-28 07:03:43",
"uptime": true,
"status_code": 200,
"latency_ms": 97.17,
"dns_ms": 0.88,
"ssl_expiry": "2026-09-15",
"ssl_days_left": 49,
"search_latency_ms": 75.99
},
{
"timestamp": "2026-07-28 07:04:43",
"uptime": true,
"status_code": 200,
"latency_ms": 77.36,
"dns_ms": 0.94,
"ssl_expiry": "2026-09-15",
"ssl_days_left": 49,
"search_latency_ms": 75.38
},
{
"timestamp": "2026-07-28 07:05:44",
"uptime": true,
"status_code": 200,
"latency_ms": 126.34,
"dns_ms": 0.94,
"ssl_expiry": "2026-09-15",
"ssl_days_left": 49,
"search_latency_ms": 78.33
},
{
"timestamp": "2026-07-28 07:06:44",
"uptime": true,
"status_code": 200,
"latency_ms": 78.6,
"dns_ms": 0.83,
"ssl_expiry": "2026-09-15",
"ssl_days_left": 49,
"search_latency_ms": 77.94
},
{
"timestamp": "2026-07-28 07:07:44",
"uptime": true,
"status_code": 200,
"latency_ms": 80.89,
"dns_ms": 0.84,
"ssl_expiry": "2026-09-15",
"ssl_days_left": 49,
"search_latency_ms": 74.3
},
{
"timestamp": "2026-07-28 07:08:44",
"uptime": true,
"status_code": 200,
"latency_ms": 77.8,
"dns_ms": 1.0,
"ssl_expiry": "2026-09-15",
"ssl_days_left": 49,
"search_latency_ms": 79.14
},
{
"timestamp": "2026-07-28 07:09:45",
"uptime": true,
"status_code": 200,
"latency_ms": 83.49,
"dns_ms": 0.86,
"ssl_expiry": "2026-09-15",
"ssl_days_left": 49,
"search_latency_ms": 75.71
},
{
"timestamp": "2026-07-28 07:10:45",
"uptime": true,
"status_code": 200,
"latency_ms": 72.7,
"dns_ms": 0.86,
"ssl_expiry": "2026-09-15",
"ssl_days_left": 49,
"search_latency_ms": 80.3
},
{
"timestamp": "2026-07-28 07:11:45",
"uptime": true,
"status_code": 200,
"latency_ms": 79.11,
"dns_ms": 0.86,
"ssl_expiry": "2026-09-15",
"ssl_days_left": 49,
"search_latency_ms": 79.6
},
{
"timestamp": "2026-07-28 07:12:45",
"uptime": true,
"status_code": 200,
"latency_ms": 80.55,
"dns_ms": 0.88,
"ssl_expiry": "2026-09-15",
"ssl_days_left": 49,
"search_latency_ms": 76.45
},
{
"timestamp": "2026-07-28 07:13:45",
"uptime": true,
"status_code": 200,
"latency_ms": 80.62,
"dns_ms": 0.85,
"ssl_expiry": "2026-09-15",
"ssl_days_left": 49,
"search_latency_ms": 76.94
},
{
"timestamp": "2026-07-28 07:14:46",
"uptime": true,
"status_code": 200,
"latency_ms": 82.55,
"dns_ms": 0.85,
"ssl_expiry": "2026-09-15",
"ssl_days_left": 49,
"search_latency_ms": 74.5
},
{
"timestamp": "2026-07-28 07:15:47",
"uptime": true,
"status_code": 200,
"latency_ms": 986.76,
"dns_ms": 0.86,
"ssl_expiry": "2026-09-15",
"ssl_days_left": 49,
"search_latency_ms": 84.61
},
{
"timestamp": "2026-07-28 07:16:47",
"uptime": true,
"status_code": 200,
"latency_ms": 103.86,
"dns_ms": 0.98,
"ssl_expiry": "2026-09-15",
"ssl_days_left": 49,
"search_latency_ms": 100.18
},
{
"timestamp": "2026-07-28 07:17:48",
"uptime": true,
"status_code": 200,
"latency_ms": 910.26,
"dns_ms": 0.88,
"ssl_expiry": "2026-09-15",
"ssl_days_left": 49,
"search_latency_ms": 81.43
},
{
"timestamp": "2026-07-28 07:18:49",
"uptime": true,
"status_code": 200,
"latency_ms": 910.72,
"dns_ms": 0.89,
"ssl_expiry": "2026-09-15",
"ssl_days_left": 49,
"search_latency_ms": 83.03
},
{
"timestamp": "2026-07-28 07:19:49",
"uptime": true,
"status_code": 200,
"latency_ms": 76.95,
"dns_ms": 0.86,
"ssl_expiry": "2026-09-15",
"ssl_days_left": 49,
"search_latency_ms": 74.19
},
{
"timestamp": "2026-07-28 07:20:50",
"uptime": true,
"status_code": 200,
"latency_ms": 93.67,
"dns_ms": 1.39,
"ssl_expiry": "2026-09-15",
"ssl_days_left": 49,
"search_latency_ms": 75.06
},
{
"timestamp": "2026-07-28 07:21:50",
"uptime": true,
"status_code": 200,
"latency_ms": 83.26,
"dns_ms": 0.87,
"ssl_expiry": "2026-09-15",
"ssl_days_left": 49,
"search_latency_ms": 74.3
},
{
"timestamp": "2026-07-28 07:22:50",
"uptime": true,
"status_code": 200,
"latency_ms": 83.7,
"dns_ms": 0.84,
"ssl_expiry": "2026-09-15",
"ssl_days_left": 49,
"search_latency_ms": 77.12
},
{
"timestamp": "2026-07-28 07:23:50",
"uptime": true,
"status_code": 200,
"latency_ms": 76.43,
"dns_ms": 0.87,
"ssl_expiry": "2026-09-15",
"ssl_days_left": 49,
"search_latency_ms": 75.44
}
]
+187
View File
@@ -0,0 +1,187 @@
import requests
import socket
import ssl
import json
import os
import time
import dns.resolver
from datetime import datetime, UTC
# ===========================================
# Configuration
# ===========================================
BASE_URL = "https://mithal.space"
SEARCH_URL = "https://mithal.space/search?q=cloud"
OUTPUT_FILE = "monitor-data.json"
CHECK_INTERVAL = 60
# ===========================================
# HTTP Latency + Uptime
# ===========================================
def check_http():
start = time.perf_counter()
response = requests.get(BASE_URL, timeout=10)
latency = (time.perf_counter() - start) * 1000
return response.status_code, round(latency, 2)
# ===========================================
# Search Response
# ===========================================
def check_search():
start = time.perf_counter()
response = requests.get(SEARCH_URL, timeout=10)
latency = (time.perf_counter() - start) * 1000
return round(latency, 2)
# ===========================================
# DNS Lookup Time
# ===========================================
def check_dns():
resolver = dns.resolver.Resolver()
start = time.perf_counter()
resolver.resolve("mithal.space")
latency = (time.perf_counter() - start) * 1000
return round(latency, 2)
# ===========================================
# SSL Certificate
# ===========================================
def check_ssl():
hostname = "mithal.space"
context = ssl.create_default_context()
with socket.create_connection((hostname, 443), timeout=10) as sock:
with context.wrap_socket(sock, server_hostname=hostname) as ssock:
cert = ssock.getpeercert()
expiry = datetime.strptime(
cert["notAfter"],
"%b %d %H:%M:%S %Y %Z"
).replace(tzinfo=UTC)
days_left = (expiry - datetime.now(UTC)).days
return expiry.strftime("%Y-%m-%d"), days_left
# ===========================================
# JSON
# ===========================================
def load_history():
if not os.path.exists(OUTPUT_FILE):
return []
try:
with open(OUTPUT_FILE, "r") as f:
content = f.read().strip()
if not content:
return []
return json.loads(content)
except (json.JSONDecodeError, FileNotFoundError):
return []
def save_history(data):
with open(OUTPUT_FILE, "w") as f:
json.dump(data, f, indent=4)
# ===========================================
# Monitoring
# ===========================================
def monitor():
status_code, latency = check_http()
ssl_expiry, ssl_days = check_ssl()
entry = {
"timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
"uptime": status_code == 200,
"status_code": status_code,
"latency_ms": latency,
"dns_ms": check_dns(),
"ssl_expiry": ssl_expiry,
"ssl_days_left": ssl_days,
"search_latency_ms": check_search()
}
history = load_history()
history.append(entry)
# آخر 24 ساعة (كل دقيقة)
history = history[-1440:]
save_history(history)
print("=" * 60)
print(json.dumps(entry, indent=4))
# ===========================================
# Main Loop
# ===========================================
if __name__ == "__main__":
print("Starting Mithal Monitoring...")
while True:
try:
monitor()
except Exception as e:
print("ERROR:", e)
time.sleep(CHECK_INTERVAL)
+4
View File
@@ -0,0 +1,4 @@
Flask==3.1.1
requests==2.32.4
dnspython==2.7.0
gunicorn==23.0.0
+280
View File
@@ -0,0 +1,280 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Mithal Monitoring Dashboard</title>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<style>
*{
margin:0;
padding:0;
box-sizing:border-box;
font-family:Arial,Helvetica,sans-serif;
}
body{
background:#0f172a;
color:white;
padding:30px;
}
.container{
max-width:1400px;
margin:auto;
}
h1{
text-align:center;
margin-bottom:30px;
}
.grid{
display:grid;
grid-template-columns:repeat(auto-fit,minmax(220px,1fr));
gap:20px;
margin-bottom:25px;
}
.card{
background:#1e293b;
border-radius:12px;
padding:20px;
box-shadow:0 0 10px rgba(0,0,0,.3);
}
.card h3{
color:#94a3b8;
margin-bottom:15px;
}
.value{
font-size:32px;
font-weight:bold;
}
.green{
color:#22c55e;
}
.red{
color:#ef4444;
}
.orange{
color:#f59e0b;
}
.chart{
background:#1e293b;
padding:20px;
border-radius:12px;
margin-bottom:25px;
}
.table-card{
background:#1e293b;
border-radius:12px;
padding:20px;
}
table{
width:100%;
border-collapse:collapse;
}
th{
background:#334155;
padding:12px;
}
td{
text-align:center;
padding:10px;
border-bottom:1px solid #334155;
}
footer{
text-align:center;
margin-top:25px;
color:#94a3b8;
}
</style>
</head>
<body>
<div class="container">
<h1>🚀 Mithal.space Monitoring Dashboard</h1>
<div class="grid">
<div class="card">
<h3>Status</h3>
<div id="status" class="value green">Loading...</div>
</div>
<div class="card">
<h3>HTTP Latency</h3>
<div id="latency" class="value">--</div>
</div>
<div class="card">
<h3>DNS Lookup</h3>
<div id="dns" class="value">--</div>
</div>
<div class="card">
<h3>Search Response</h3>
<div id="search" class="value">--</div>
</div>
<div class="card">
<h3>SSL Days Left</h3>
<div id="ssl" class="value">--</div>
</div>
<div class="card">
<h3>24h Uptime</h3>
<div id="uptime" class="value">--</div>
</div>
</div>
<div class="chart">
<canvas id="latencyChart"></canvas>
</div>
<div class="table-card">
<h2 style="margin-bottom:20px;">Last 10 Checks</h2>
<table>
<thead>
<tr>
<th>Time</th>
<th>Status</th>
<th>Latency</th>
<th>DNS</th>
<th>Search</th>
<th>SSL Days</th>
</tr>
</thead>
<tbody id="history"></tbody>
</table>
</div>
<footer>Updates every 60 seconds</footer>
</div>
<script>
let chart;
async function loadData(){
const response = await fetch("/api/metrics");
const data = await response.json();
if(data.length === 0){
return;
}
const latest = data[data.length - 1];
document.getElementById("status").innerHTML =
latest.uptime
? '<span class="green">● ONLINE</span>'
: '<span class="red">● OFFLINE</span>';
document.getElementById("latency").innerHTML = latest.latency_ms + " ms";
document.getElementById("dns").innerHTML = latest.dns_ms + " ms";
document.getElementById("search").innerHTML = latest.search_latency_ms + " ms";
document.getElementById("ssl").innerHTML = latest.ssl_days_left + " days";
const uptimePercent = (
data.filter(x => x.uptime).length / data.length * 100
).toFixed(2);
document.getElementById("uptime").innerHTML = uptimePercent + "%";
const labels = data.slice(-60).map(x => x.timestamp.split(" ")[1]);
const latency = data.slice(-60).map(x => x.latency_ms);
if(chart){
chart.destroy();
}
chart = new Chart(
document.getElementById("latencyChart"),
{
type: "line",
data: {
labels: labels,
datasets: [{
label: "Latency (ms)",
data: latency,
borderWidth: 3,
fill: false,
tension: .3,
borderColor: "#38bdf8"
}]
},
options: {
responsive: true,
plugins: {
legend: {
labels: {
color: "white"
}
}
},
scales: {
x: {
ticks: {
color: "white"
}
},
y: {
ticks: {
color: "white"
}
}
}
}
}
);
const tbody = document.getElementById("history");
tbody.innerHTML = "";
data.slice(-10).reverse().forEach(row => {
tbody.innerHTML += `
<tr>
<td>${row.timestamp}</td>
<td>
${row.uptime
? '<span class="green">Online</span>'
: '<span class="red">Offline</span>'}
</td>
<td>${row.latency_ms} ms</td>
<td>${row.dns_ms} ms</td>
<td>${row.search_latency_ms} ms</td>
<td>${row.ssl_days_left} days</td>
</tr>
`;
});
}
loadData();
setInterval(loadData, 60000);
</script>
</body>
</html>