From 00bcc3dadd9e58eeab7ce0b54c743e43714ff526 Mon Sep 17 00:00:00 2001 From: Sayed Abubaker Hashimi Date: Wed, 7 May 2025 13:02:50 +0200 Subject: [PATCH] new exam files --- sk1/.gitignore | 62 ++++++++++++ {z2 => sk1}/Dockerfile | 0 z2/namespace.yaml => sk1/k8s/Namespace.yaml | 8 +- .../k8s/app-deployment.yaml | 53 +++++----- z2/service.yaml => sk1/k8s/app-service.yaml | 26 ++--- sk1/k8s/contact-app-lb.yaml | 13 +++ sk1/k8s/global-ip.yaml | 9 ++ sk1/k8s/ingress.yaml | 28 ++++++ sk1/k8s/mongo-deployment.yaml | 33 +++++++ sk1/k8s/mongo-pvc.yaml | 10 ++ sk1/k8s/mongo-service.yaml | 11 +++ {z2 => sk1}/package.json | 0 sk1/prepare-app.sh | 97 +++++++++++++++++++ sk1/remove-app.sh | 33 +++++++ {z2 => sk1}/src/models/contact.js | 0 {z2 => sk1}/src/public/app.js | 0 {z2 => sk1}/src/public/index.html | 0 {z2 => sk1}/src/public/style.css | 0 {z2 => sk1}/src/routes/contacts.js | 12 --- {z2 => sk1}/src/server.js | 2 +- z2/README.md | 81 ---------------- z2/prepare-app.sh | 11 --- z2/src/models/contact.js:Zone.Identifier | 0 z2/src/public/app.js:Zone.Identifier | 0 z2/src/public/index.html:Zone.Identifier | 0 z2/src/public/style.css:Zone.Identifier | 0 z2/src/routes/contacts.js:Zone.Identifier | 0 z2/src/server.js:Zone.Identifier | 0 z2/start-app.sh | 7 -- z2/statefulset.yaml | 73 -------------- z2/stop-app.sh | 7 -- 31 files changed, 343 insertions(+), 233 deletions(-) create mode 100644 sk1/.gitignore rename {z2 => sk1}/Dockerfile (100%) mode change 100644 => 100755 rename z2/namespace.yaml => sk1/k8s/Namespace.yaml (65%) rename z2/deployment.yaml => sk1/k8s/app-deployment.yaml (55%) rename z2/service.yaml => sk1/k8s/app-service.yaml (51%) create mode 100644 sk1/k8s/contact-app-lb.yaml create mode 100644 sk1/k8s/global-ip.yaml create mode 100644 sk1/k8s/ingress.yaml create mode 100644 sk1/k8s/mongo-deployment.yaml create mode 100644 sk1/k8s/mongo-pvc.yaml create mode 100644 sk1/k8s/mongo-service.yaml rename {z2 => sk1}/package.json (100%) mode change 100644 => 100755 create mode 100755 sk1/prepare-app.sh create mode 100755 sk1/remove-app.sh rename {z2 => sk1}/src/models/contact.js (100%) rename {z2 => sk1}/src/public/app.js (100%) rename {z2 => sk1}/src/public/index.html (100%) rename {z2 => sk1}/src/public/style.css (100%) rename {z2 => sk1}/src/routes/contacts.js (83%) rename {z2 => sk1}/src/server.js (96%) delete mode 100644 z2/README.md delete mode 100644 z2/prepare-app.sh delete mode 100644 z2/src/models/contact.js:Zone.Identifier delete mode 100644 z2/src/public/app.js:Zone.Identifier delete mode 100644 z2/src/public/index.html:Zone.Identifier delete mode 100644 z2/src/public/style.css:Zone.Identifier delete mode 100644 z2/src/routes/contacts.js:Zone.Identifier delete mode 100644 z2/src/server.js:Zone.Identifier delete mode 100644 z2/start-app.sh delete mode 100644 z2/statefulset.yaml delete mode 100644 z2/stop-app.sh diff --git a/sk1/.gitignore b/sk1/.gitignore new file mode 100644 index 0000000..6f5860a --- /dev/null +++ b/sk1/.gitignore @@ -0,0 +1,62 @@ +# Node.js dependencies +node_modules/ +npm-debug.log +yarn-debug.log +yarn-error.log +package-lock.json +yarn.lock + +# Environment variables and secrets +.env +.env.local +.env.development +.env.test +.env.production +*.env + +# Google Cloud and Kubernetes +gcloud.json +key.json +*.kubeconfig + +# Log files +logs/ +*.log + +# Build files +dist/ +build/ +coverage/ +.next/ + +# Docker +.docker/ +docker-compose.override.yml + +# IDE and editor files +.idea/ +.vscode/ +*.swp +*.swo +.DS_Store +Thumbs.db +*~ + +# Generated Kubernetes files +k8s/ingress-with-domain.yaml +k8s/*-updated.yaml +k8s/generated/ + +# Windows WSL file identifiers +*Zone.Identifier + +# Temporary files +*.tmp +*.bak +.cache/ +*.tgz +.npm/ + +# Testing +coverage/ +.nyc_output/ \ No newline at end of file diff --git a/z2/Dockerfile b/sk1/Dockerfile old mode 100644 new mode 100755 similarity index 100% rename from z2/Dockerfile rename to sk1/Dockerfile diff --git a/z2/namespace.yaml b/sk1/k8s/Namespace.yaml similarity index 65% rename from z2/namespace.yaml rename to sk1/k8s/Namespace.yaml index b5e5e3c..a47e2ec 100644 --- a/z2/namespace.yaml +++ b/sk1/k8s/Namespace.yaml @@ -1,4 +1,4 @@ -apiVersion: v1 -kind: Namespace -metadata: - name: contact-app +apiVersion: v1 +kind: Namespace +metadata: + name: contact-app \ No newline at end of file diff --git a/z2/deployment.yaml b/sk1/k8s/app-deployment.yaml similarity index 55% rename from z2/deployment.yaml rename to sk1/k8s/app-deployment.yaml index 18512d7..8a4dfd9 100644 --- a/z2/deployment.yaml +++ b/sk1/k8s/app-deployment.yaml @@ -1,24 +1,29 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: contact-app - namespace: contact-app -spec: - replicas: 1 - selector: - matchLabels: - app: contact-app - template: - metadata: - labels: - app: contact-app - spec: - containers: - - name: contact-app - image: contact-app:latest - imagePullPolicy: IfNotPresent - ports: - - containerPort: 3000 - env: - - name: MONGODB_URI - value: "mongodb://mongo:27017/contacts" +apiVersion: apps/v1 +kind: Deployment +metadata: + name: contact-app +spec: + replicas: 1 + selector: + matchLabels: + app: contact-app + template: + metadata: + labels: + app: contact-app + spec: + containers: + - name: contact-app + image: + ports: + - containerPort: 3000 + env: + - name: MONGODB_URI + value: "mongodb://mongo:27017/contactlist" + resources: + requests: + cpu: "100m" + memory: "128Mi" + limits: + cpu: "200m" + memory: "256Mi" \ No newline at end of file diff --git a/z2/service.yaml b/sk1/k8s/app-service.yaml similarity index 51% rename from z2/service.yaml rename to sk1/k8s/app-service.yaml index 021221c..80aa7a4 100644 --- a/z2/service.yaml +++ b/sk1/k8s/app-service.yaml @@ -1,13 +1,13 @@ -apiVersion: v1 -kind: Service -metadata: - name: contact-app - namespace: contact-app -spec: - selector: - app: contact-app - ports: - - protocol: TCP - port: 3000 - targetPort: 3000 - type: NodePort +apiVersion: v1 +kind: Service +metadata: + name: contact-app +spec: + type: ClusterIP + ports: + - port: 80 + targetPort: 3000 + protocol: TCP + name: http + selector: + app: contact-app \ No newline at end of file diff --git a/sk1/k8s/contact-app-lb.yaml b/sk1/k8s/contact-app-lb.yaml new file mode 100644 index 0000000..d98a206 --- /dev/null +++ b/sk1/k8s/contact-app-lb.yaml @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: Service +metadata: + name: contact-app-lb +spec: + type: LoadBalancer + selector: + app: contact-app + ports: + - port: 80 + targetPort: 3000 + protocol: TCP + name: http \ No newline at end of file diff --git a/sk1/k8s/global-ip.yaml b/sk1/k8s/global-ip.yaml new file mode 100644 index 0000000..c871104 --- /dev/null +++ b/sk1/k8s/global-ip.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: Service +metadata: + name: contact-app-ip +spec: + type: LoadBalancer + ports: + - port: 80 + targetPort: 80 \ No newline at end of file diff --git a/sk1/k8s/ingress.yaml b/sk1/k8s/ingress.yaml new file mode 100644 index 0000000..a212ff0 --- /dev/null +++ b/sk1/k8s/ingress.yaml @@ -0,0 +1,28 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: contact-app-ingress + annotations: + networking.gke.io/managed-certificates: contact-app-cert + kubernetes.io/ingress.global-static-ip-name: contact-app-ip +spec: + ingressClassName: gce + rules: + - http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: contact-app + port: + number: 80 +--- +apiVersion: networking.gke.io/v1 +kind: ManagedCertificate +metadata: + name: contact-app-cert +spec: + domains: + # This will be populated with the automatic domain from GKE + - DOMAIN_PLACEHOLDER \ No newline at end of file diff --git a/sk1/k8s/mongo-deployment.yaml b/sk1/k8s/mongo-deployment.yaml new file mode 100644 index 0000000..427a27a --- /dev/null +++ b/sk1/k8s/mongo-deployment.yaml @@ -0,0 +1,33 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: mongo +spec: + replicas: 1 + selector: + matchLabels: + app: mongo + template: + metadata: + labels: + app: mongo + spec: + containers: + - name: mongo + image: mongo:4.4 + ports: + - containerPort: 27017 + volumeMounts: + - name: mongo-storage + mountPath: /data/db + resources: + requests: + memory: "256Mi" + cpu: "100m" + limits: + memory: "512Mi" + cpu: "300m" + volumes: + - name: mongo-storage + persistentVolumeClaim: + claimName: mongo-pvc \ No newline at end of file diff --git a/sk1/k8s/mongo-pvc.yaml b/sk1/k8s/mongo-pvc.yaml new file mode 100644 index 0000000..d04bad6 --- /dev/null +++ b/sk1/k8s/mongo-pvc.yaml @@ -0,0 +1,10 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: mongo-pvc +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 1Gi \ No newline at end of file diff --git a/sk1/k8s/mongo-service.yaml b/sk1/k8s/mongo-service.yaml new file mode 100644 index 0000000..def6025 --- /dev/null +++ b/sk1/k8s/mongo-service.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: Service +metadata: + name: mongo +spec: + clusterIP: None + selector: + app: mongo + ports: + - port: 27017 + targetPort: 27017 \ No newline at end of file diff --git a/z2/package.json b/sk1/package.json old mode 100644 new mode 100755 similarity index 100% rename from z2/package.json rename to sk1/package.json diff --git a/sk1/prepare-app.sh b/sk1/prepare-app.sh new file mode 100755 index 0000000..276761a --- /dev/null +++ b/sk1/prepare-app.sh @@ -0,0 +1,97 @@ +#!/bin/bash +# filepath: prepare-app.sh +set -e + +# Set variables +PROJECT_ID=$(gcloud config get-value project) +REGION=europe-central2 +ZONE=europe-central2-a +CLUSTER_NAME=sayed-cluster-1 + +# Build and push the Docker image +echo "[1/4] Building and pushing Docker image..." +docker build -t gcr.io/$PROJECT_ID/contact-app:latest . +gcloud auth configure-docker +docker push gcr.io/$PROJECT_ID/contact-app:latest + +# Check if cluster exists, if not create it +echo "[2/4] Checking for GKE cluster..." +if ! gcloud container clusters describe $CLUSTER_NAME --region $REGION --project $PROJECT_ID &>/dev/null; then + echo "Cluster $CLUSTER_NAME not found. Creating cluster..." + gcloud container clusters create-auto $CLUSTER_NAME \ + --region $REGION \ + --project $PROJECT_ID + echo "Cluster created successfully." +else + echo "Cluster $CLUSTER_NAME already exists." +fi + +# Connect to the cluster +echo "[3/4] Connecting to GKE cluster..." +gcloud container clusters get-credentials $CLUSTER_NAME --region $REGION --project $PROJECT_ID +kubectl config set-context --current --namespace=default + +# Create namespace and apply MongoDB resources +echo "[4/4] Deploying Kubernetes resources..." +kubectl create namespace contact-app --dry-run=client -o yaml | kubectl apply -f - +kubectl apply -n contact-app -f k8s/mongo-pvc.yaml +kubectl apply -n contact-app -f k8s/mongo-deployment.yaml +kubectl apply -n contact-app -f k8s/mongo-service.yaml + +# Apply app with image substitution and create LoadBalancer service +echo "Deploying application..." +sed "s||gcr.io/$PROJECT_ID/contact-app:latest|g" k8s/app-deployment.yaml | kubectl apply -n contact-app -f - +kubectl apply -n contact-app -f k8s/app-service.yaml +kubectl apply -n contact-app -f k8s/contact-app-lb.yaml + +# Wait for LoadBalancer to get external IP +echo "Waiting for LoadBalancer to get external IP..." +while [[ -z $(kubectl get service contact-app-lb -n contact-app -o jsonpath='{.status.loadBalancer.ingress[0].ip}' 2>/dev/null) ]]; do + echo -n "." + sleep 5 +done + +# Get the external IP +EXTERNAL_IP=$(kubectl get service contact-app-lb -n contact-app -o jsonpath='{.status.loadBalancer.ingress[0].ip}') +echo "External IP: $EXTERNAL_IP" + +# Create a domain using nip.io +DOMAIN="${EXTERNAL_IP}.nip.io" +echo "Domain: $DOMAIN" + +# Create and apply ingress with the domain +cat < k8s/ingress-with-domain.yaml +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: contact-app-ingress + annotations: + networking.gke.io/managed-certificates: contact-app-cert + kubernetes.io/ingress.class: "gce" +spec: + rules: + - host: ${DOMAIN} + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: contact-app + port: + number: 80 +--- +apiVersion: networking.gke.io/v1 +kind: ManagedCertificate +metadata: + name: contact-app-cert +spec: + domains: + - ${DOMAIN} +EOF + +kubectl apply -n contact-app -f k8s/ingress-with-domain.yaml + +echo "Deployment started. It may take a few minutes for the HTTPS certificate to be provisioned." +echo "Once provisioned, your application will be available at:" +echo "https://${DOMAIN}" \ No newline at end of file diff --git a/sk1/remove-app.sh b/sk1/remove-app.sh new file mode 100755 index 0000000..b45eab5 --- /dev/null +++ b/sk1/remove-app.sh @@ -0,0 +1,33 @@ +#!/bin/bash +# filepath: remove-app.sh +set -e + +# Set variables +PROJECT_ID=$(gcloud config get-value project) +REGION=europe-central2 +ZONE=europe-central2-a +CLUSTER_NAME=sayed-cluster-1 + +# Get cluster credentials +gcloud container clusters get-credentials $CLUSTER_NAME --region $REGION --project $PROJECT_ID + +echo "Removing application resources..." + +# Delete Ingress and certificates +kubectl delete ingress contact-app-ingress -n contact-app --ignore-not-found=true +kubectl delete managedcertificate contact-app-cert -n contact-app --ignore-not-found=true + +# Delete app deployment and services +kubectl delete deployment contact-app -n contact-app --ignore-not-found=true +kubectl delete service contact-app -n contact-app --ignore-not-found=true +kubectl delete service contact-app-lb -n contact-app --ignore-not-found=true + +# Delete MongoDB deployment, service, and PVC +kubectl delete deployment mongo -n contact-app --ignore-not-found=true +kubectl delete service mongo -n contact-app --ignore-not-found=true +kubectl delete pvc mongo-pvc -n contact-app --ignore-not-found=true + +# Delete namespace +kubectl delete namespace contact-app --ignore-not-found=true + +echo "Application and associated resources have been removed." \ No newline at end of file diff --git a/z2/src/models/contact.js b/sk1/src/models/contact.js similarity index 100% rename from z2/src/models/contact.js rename to sk1/src/models/contact.js diff --git a/z2/src/public/app.js b/sk1/src/public/app.js similarity index 100% rename from z2/src/public/app.js rename to sk1/src/public/app.js diff --git a/z2/src/public/index.html b/sk1/src/public/index.html similarity index 100% rename from z2/src/public/index.html rename to sk1/src/public/index.html diff --git a/z2/src/public/style.css b/sk1/src/public/style.css similarity index 100% rename from z2/src/public/style.css rename to sk1/src/public/style.css diff --git a/z2/src/routes/contacts.js b/sk1/src/routes/contacts.js similarity index 83% rename from z2/src/routes/contacts.js rename to sk1/src/routes/contacts.js index b473950..68b71cd 100644 --- a/z2/src/routes/contacts.js +++ b/sk1/src/routes/contacts.js @@ -49,16 +49,4 @@ router.delete('/:id', async (req, res) => { } }); -module.exports = router; -router.post('/', async (req, res) => { /* ... */ }); - -// Get all contacts -router.get('/', async (req, res) => { /* ... */ }); - -// Update a contact -router.put('/:id', async (req, res) => { /* ... */ }); - -// Delete a contact -router.delete('/:id', async (req, res) => { /* ... */ }); - module.exports = router; \ No newline at end of file diff --git a/z2/src/server.js b/sk1/src/server.js similarity index 96% rename from z2/src/server.js rename to sk1/src/server.js index ae465fa..cfeb348 100644 --- a/z2/src/server.js +++ b/sk1/src/server.js @@ -26,6 +26,6 @@ mongoose.connect(MONGODB_URI, { app.use('/api/contacts', contactsRouter); // Start the server -app.listen(PORT, () => { +app.listen(PORT, '0.0.0.0', () => { console.log(`Server is running on http://localhost:${PORT}`); }); \ No newline at end of file diff --git a/z2/README.md b/z2/README.md deleted file mode 100644 index f229908..0000000 --- a/z2/README.md +++ /dev/null @@ -1,81 +0,0 @@ -# Contact List Web Application on Kubernetes - -## Description - -A simple web-based contact list app with name and number fields, using Node.js/Express and MongoDB. Data is persisted in MongoDB. - -## Containers - -- **contact-app**: Node.js/Express web server serving the frontend and API. -- **mongo**: Official MongoDB container for data storage. - -## Kubernetes Objects - -- **Namespace**: `contact-app` — all resources are grouped here. -- **Deployment**: Runs the Node.js web app. -- **StatefulSet**: Runs MongoDB with persistent storage. -- **PersistentVolume/PersistentVolumeClaim**: Stores MongoDB data. -- **Service**: - - `contact-app`: Exposes the web app on a NodePort. - - `mongo`: Headless service for MongoDB. - -## Virtual Networks and Volumes - -- **Headless Service**: For MongoDB pod DNS discovery. -- **Named Volume**: `/data/mongo` on the host, mounted to MongoDB for persistence. - -## Container Configuration - -- The Node.js app uses the `MONGODB_URI` environment variable to connect to MongoDB. -- MongoDB uses a persistent volume for `/data/db`. - -## Instructions - -### Prepare - -```bash -chmod +x prepare-app.sh start-app.sh stop-app.sh -./prepare-app.sh -``` - -### Run - -```bash -./start-app.sh -``` - -### Access the App - -1. Get the NodePort: - ```bash - kubectl -n contact-app get service contact-app - ``` -2. Open in browser: - `http://:` - - Get Minikube IP: `minikube ip` - -### Pause/Stop - -```bash -./stop-app.sh -``` - -### Delete Volumes (optional) - -```bash -rm -rf /data/mongo -``` - ---- - -## View the Application - -- Open the NodePort URL in your browser. -- Add and view contacts via the web UI. - ---- - -## Notes - -- Make sure Docker is running and Minikube is started. -- All kubectl commands assume your context is set to Minikube. \ No newline at end of file diff --git a/z2/prepare-app.sh b/z2/prepare-app.sh deleted file mode 100644 index e662bed..0000000 --- a/z2/prepare-app.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/bash -# filepath: prepare-app.sh - -# Use Minikube's Docker daemon -eval $(minikube docker-env) - -# Build Docker image for the Node.js app -docker build -t contact-app:latest . - -# Create local directory for MongoDB data if not exists -sudo mkdir -p /data/mongo diff --git a/z2/src/models/contact.js:Zone.Identifier b/z2/src/models/contact.js:Zone.Identifier deleted file mode 100644 index e69de29..0000000 diff --git a/z2/src/public/app.js:Zone.Identifier b/z2/src/public/app.js:Zone.Identifier deleted file mode 100644 index e69de29..0000000 diff --git a/z2/src/public/index.html:Zone.Identifier b/z2/src/public/index.html:Zone.Identifier deleted file mode 100644 index e69de29..0000000 diff --git a/z2/src/public/style.css:Zone.Identifier b/z2/src/public/style.css:Zone.Identifier deleted file mode 100644 index e69de29..0000000 diff --git a/z2/src/routes/contacts.js:Zone.Identifier b/z2/src/routes/contacts.js:Zone.Identifier deleted file mode 100644 index e69de29..0000000 diff --git a/z2/src/server.js:Zone.Identifier b/z2/src/server.js:Zone.Identifier deleted file mode 100644 index e69de29..0000000 diff --git a/z2/start-app.sh b/z2/start-app.sh deleted file mode 100644 index 0c21a5b..0000000 --- a/z2/start-app.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash -# filepath: start-app.sh - -kubectl apply -f namespace.yaml -kubectl apply -f statefulset.yaml -kubectl apply -f deployment.yaml -kubectl apply -f service.yaml diff --git a/z2/statefulset.yaml b/z2/statefulset.yaml deleted file mode 100644 index ec0aac7..0000000 --- a/z2/statefulset.yaml +++ /dev/null @@ -1,73 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: mongo - namespace: contact-app - labels: - app: mongo -spec: - ports: - - port: 27017 - name: mongo - clusterIP: None - selector: - app: mongo ---- -apiVersion: v1 -kind: PersistentVolume -metadata: - name: mongo-pv - namespace: contact-app -spec: - capacity: - storage: 1Gi - accessModes: - - ReadWriteOnce - hostPath: - path: "/data/mongo" ---- -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - name: mongo-pvc - namespace: contact-app -spec: - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 1Gi ---- -apiVersion: apps/v1 -kind: StatefulSet -metadata: - name: mongo - namespace: contact-app -spec: - serviceName: "mongo" - replicas: 1 - selector: - matchLabels: - app: mongo - template: - metadata: - labels: - app: mongo - spec: - containers: - - name: mongo - image: mongo:6.0 - ports: - - containerPort: 27017 - volumeMounts: - - name: mongo-storage - mountPath: /data/db - volumeClaimTemplates: - - metadata: - name: mongo-storage - namespace: contact-app - spec: - accessModes: [ "ReadWriteOnce" ] - resources: - requests: - storage: 1Gi diff --git a/z2/stop-app.sh b/z2/stop-app.sh deleted file mode 100644 index 5033b1e..0000000 --- a/z2/stop-app.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash -# filepath: stop-app.sh - -kubectl delete -f service.yaml -kubectl delete -f deployment.yaml -kubectl delete -f statefulset.yaml -kubectl delete -f namespace.yaml