Téléverser les fichiers vers "z2"
This commit is contained in:
parent
9bf0df0594
commit
870c425c5f
10
z2/Dockerfile
Normal file
10
z2/Dockerfile
Normal file
@ -0,0 +1,10 @@
|
||||
FROM python:3.9-slim
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY requirements.txt .
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
|
||||
COPY app.py .
|
||||
|
||||
CMD ["python", "app.py"]
|
82
z2/README.md
Normal file
82
z2/README.md
Normal file
@ -0,0 +1,82 @@
|
||||
# PI Visualization Web Application on Kubernetes
|
||||
|
||||
## Overview
|
||||
This project deploys a Python web application using Flask on a Kubernetes cluster. The application displays the first 300 digits of PI with an interactive visualization. The deployment includes a Namespace, Deployment, StatefulSet (with PersistentVolume and PersistentVolumeClaim), and Service.
|
||||
|
||||
## Application Description
|
||||
- The web application visualizes PI digits at the root URL (`/`).
|
||||
- Each digit appears one at a time (every 0.5 seconds by default) with a unique color.
|
||||
- Users can control the animation (start, pause, reset) and adjust the speed.
|
||||
- The application uses one container image built from the Dockerfile provided.
|
||||
|
||||
## Containers
|
||||
- **simple-web-app**: Runs the Python Flask application on port 5000.
|
||||
|
||||
## Kubernetes Objects
|
||||
- **Namespace**: Isolates all the resources under `my-app`.
|
||||
- **Deployment**: Manages the stateless web application pods with 2 replicas for high availability.
|
||||
- **StatefulSet**: Manages stateful application pods that require persistent storage.
|
||||
- **PersistentVolume (PV)**: Provides persistent storage from the host (1GB).
|
||||
- **PersistentVolumeClaim (PVC)**: Claims the PV for storage.
|
||||
- **Service**: Exposes the web application externally via NodePort 30007.
|
||||
|
||||
## Networking and Storage
|
||||
- The application uses Kubernetes networking to enable communication between pods.
|
||||
- The StatefulSet uses a volume claim template that binds to a PersistentVolume mounted at `/data`.
|
||||
|
||||
## Container Configuration
|
||||
- The container is based on Python and includes Flask.
|
||||
- It exposes port 5000 to serve the web application.
|
||||
- Resource limits and readiness probes are configured for better stability.
|
||||
|
||||
## How to Prepare, Run, Pause, and Delete the Application
|
||||
|
||||
1. **Prepare the application:**
|
||||
```bash
|
||||
./prepare-app.sh
|
||||
```
|
||||
This script builds the Docker image and creates the directory for persistent volume.
|
||||
|
||||
2. **Start the application:**
|
||||
```bash
|
||||
./start-app.sh
|
||||
```
|
||||
This script creates all necessary Kubernetes objects in the correct order.
|
||||
|
||||
3. **Pause or delete the application:**
|
||||
```bash
|
||||
./stop-app.sh
|
||||
```
|
||||
This script removes all Kubernetes objects created by `start-app.sh`.
|
||||
|
||||
## Accessing the Application
|
||||
To access the application:
|
||||
|
||||
1. Find the IP address of your Kubernetes node:
|
||||
```bash
|
||||
kubectl get nodes -o wide
|
||||
```
|
||||
|
||||
2. Access the application in your browser at:
|
||||
```
|
||||
http://<NODE_IP>:30007
|
||||
```
|
||||
Where `<NODE_IP>` is the IP address of any of your Kubernetes nodes.
|
||||
|
||||
## Troubleshooting
|
||||
If you encounter issues:
|
||||
|
||||
1. Check pod status:
|
||||
```bash
|
||||
kubectl get pods -n my-app
|
||||
```
|
||||
|
||||
2. View pod logs:
|
||||
```bash
|
||||
kubectl logs <pod-name> -n my-app
|
||||
```
|
||||
|
||||
3. Check service status:
|
||||
```bash
|
||||
kubectl get svc -n my-app
|
||||
```
|
314
z2/app.py
Normal file
314
z2/app.py
Normal file
@ -0,0 +1,314 @@
|
||||
from flask import Flask, render_template_string
|
||||
import math
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
@app.route('/')
|
||||
def index():
|
||||
# Calculate PI to 300 decimal places
|
||||
# Since math.pi doesn't provide that many digits, we're using a string representation
|
||||
# that includes the first 300 decimal places of PI
|
||||
pi_digits = "3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679821480865132823066470938446095505822317253594081284811174502841027019385211055596446229489549303819644288109756659334461284756482337867831652712019091456485669234603486104543266482133936072602491412737245870066063155881748815209209628292540917153643678925903600113305305488204665213841469519415116094330572703657595919530921861173819326117931051185480744623799627495673518857527248912279381830119491298336733624406566430860213949463952247371907021798609437027705392171762931767523846748184676694051320005681271452635608277857713427577896091736371787214684409012249534301465495853710507922796892589235420199561121290219608640344181598136297747713099605187072113499999983729780499510597317328160963185950244594553469083026425223082533446850352619311881710100031378387528865875332083814206171776691473035982534904287554687311595628638823537875937519577818577805321712268066130019278766111959092164201989"
|
||||
|
||||
# HTML template with embedded CSS and JavaScript
|
||||
html_template = """
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>PI Digits Visualization</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Arial', sans-serif;
|
||||
background: linear-gradient(135deg, #1a1a2e, #16213e);
|
||||
color: #fff;
|
||||
min-height: 100vh;
|
||||
margin: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 800px;
|
||||
background-color: rgba(0, 0, 0, 0.3);
|
||||
border-radius: 15px;
|
||||
padding: 30px;
|
||||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
h1 {
|
||||
color: #4cc9f0;
|
||||
font-size: 2.5rem;
|
||||
margin-bottom: 20px;
|
||||
text-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.pi-display {
|
||||
font-family: 'Courier New', monospace;
|
||||
font-size: 2rem;
|
||||
letter-spacing: 2px;
|
||||
margin: 20px 0;
|
||||
min-height: 60px;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.digit {
|
||||
padding: 5px;
|
||||
margin: 2px;
|
||||
border-radius: 5px;
|
||||
display: inline-block;
|
||||
opacity: 0;
|
||||
transform: scale(0.5);
|
||||
transition: transform 0.3s ease, opacity 0.3s ease;
|
||||
}
|
||||
|
||||
.digit.visible {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
|
||||
.decimal {
|
||||
color: #f72585;
|
||||
font-weight: bold;
|
||||
font-size: 2.2rem;
|
||||
}
|
||||
|
||||
.controls {
|
||||
margin-top: 30px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 20px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
button {
|
||||
background-color: #4361ee;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 12px 25px;
|
||||
border-radius: 30px;
|
||||
cursor: pointer;
|
||||
font-size: 1rem;
|
||||
transition: background-color 0.3s, transform 0.2s;
|
||||
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background-color: #4cc9f0;
|
||||
transform: translateY(-3px);
|
||||
}
|
||||
|
||||
button:active {
|
||||
transform: translateY(1px);
|
||||
}
|
||||
|
||||
.speed-control {
|
||||
margin-top: 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.speed-label {
|
||||
margin-bottom: 10px;
|
||||
color: #4cc9f0;
|
||||
}
|
||||
|
||||
.info {
|
||||
margin-top: 30px;
|
||||
font-size: 1rem;
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
.counter {
|
||||
font-size: 1.2rem;
|
||||
color: #4cc9f0;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.pi-display {
|
||||
font-size: 1.5rem;
|
||||
max-height: 200px;
|
||||
}
|
||||
h1 {
|
||||
font-size: 1.8rem;
|
||||
}
|
||||
.controls {
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
button {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>PI Digits Visualization</h1>
|
||||
|
||||
<div class="pi-display" id="pi-display"></div>
|
||||
|
||||
<div class="counter">Digits displayed: <span id="counter">0</span> / 1002 </div>
|
||||
|
||||
<div class="controls">
|
||||
<button id="start-btn">Start</button>
|
||||
<button id="pause-btn">Pause</button>
|
||||
<button id="reset-btn">Reset</button>
|
||||
<button id="speed-up-btn">Speed Up</button>
|
||||
<button id="slow-down-btn">Slow Down</button>
|
||||
</div>
|
||||
|
||||
<div class="speed-control">
|
||||
<div class="speed-label">Current speed: <span id="speed-value">0.5</span> seconds per digit</div>
|
||||
</div>
|
||||
|
||||
<div class="info">
|
||||
<p>PI digits are displayed one by one with a unique color for each digit.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// First 300 decimal places of PI
|
||||
const PI_DIGITS = "{{ pi_digits }}";
|
||||
let currentIndex = 0;
|
||||
let animationInterval;
|
||||
let isPlaying = false;
|
||||
let displaySpeed = 500; // milliseconds (0.5 seconds)
|
||||
|
||||
const piDisplay = document.getElementById('pi-display');
|
||||
const counter = document.getElementById('counter');
|
||||
const startBtn = document.getElementById('start-btn');
|
||||
const pauseBtn = document.getElementById('pause-btn');
|
||||
const resetBtn = document.getElementById('reset-btn');
|
||||
const speedUpBtn = document.getElementById('speed-up-btn');
|
||||
const slowDownBtn = document.getElementById('slow-down-btn');
|
||||
const speedValue = document.getElementById('speed-value');
|
||||
|
||||
// Generate a random color from a predefined palette
|
||||
function getRandomColor() {
|
||||
const colors = [
|
||||
'#f72585', '#b5179e', '#7209b7', '#560bad',
|
||||
'#480ca8', '#3a0ca3', '#3f37c9', '#4361ee',
|
||||
'#4895ef', '#4cc9f0', '#80ffdb', '#72efdd',
|
||||
'#64dfdf', '#56cfe1', '#48bfe3', '#4ea8de'
|
||||
];
|
||||
return colors[Math.floor(Math.random() * colors.length)];
|
||||
}
|
||||
|
||||
// Add a digit to the display
|
||||
function addDigit() {
|
||||
if (currentIndex < PI_DIGITS.length) {
|
||||
const digit = PI_DIGITS[currentIndex];
|
||||
const digitElement = document.createElement('span');
|
||||
|
||||
digitElement.textContent = digit;
|
||||
digitElement.className = 'digit';
|
||||
if (digit === '.') {
|
||||
digitElement.classList.add('decimal');
|
||||
} else {
|
||||
digitElement.style.color = getRandomColor();
|
||||
}
|
||||
|
||||
piDisplay.appendChild(digitElement);
|
||||
|
||||
// Appearance animation
|
||||
setTimeout(() => {
|
||||
digitElement.classList.add('visible');
|
||||
}, 50);
|
||||
|
||||
currentIndex++;
|
||||
counter.textContent = currentIndex;
|
||||
|
||||
// Auto-scroll to show the latest digits
|
||||
piDisplay.scrollTop = piDisplay.scrollHeight;
|
||||
|
||||
// Auto-stop at the end
|
||||
if (currentIndex >= PI_DIGITS.length) {
|
||||
clearInterval(animationInterval);
|
||||
isPlaying = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Start the animation
|
||||
function startAnimation() {
|
||||
if (!isPlaying && currentIndex < PI_DIGITS.length) {
|
||||
animationInterval = setInterval(addDigit, displaySpeed);
|
||||
isPlaying = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Pause
|
||||
function pauseAnimation() {
|
||||
clearInterval(animationInterval);
|
||||
isPlaying = false;
|
||||
}
|
||||
|
||||
// Reset
|
||||
function resetAnimation() {
|
||||
pauseAnimation();
|
||||
piDisplay.innerHTML = '';
|
||||
currentIndex = 0;
|
||||
counter.textContent = '0';
|
||||
}
|
||||
|
||||
// Speed up the animation
|
||||
function speedUp() {
|
||||
if (displaySpeed > 100) {
|
||||
displaySpeed -= 100;
|
||||
updateSpeed();
|
||||
if (isPlaying) {
|
||||
pauseAnimation();
|
||||
startAnimation();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Slow down the animation
|
||||
function slowDown() {
|
||||
if (displaySpeed < 2000) {
|
||||
displaySpeed += 100;
|
||||
updateSpeed();
|
||||
if (isPlaying) {
|
||||
pauseAnimation();
|
||||
startAnimation();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update the speed display
|
||||
function updateSpeed() {
|
||||
speedValue.textContent = (displaySpeed / 1000).toFixed(1);
|
||||
}
|
||||
|
||||
// Event listeners
|
||||
startBtn.addEventListener('click', startAnimation);
|
||||
pauseBtn.addEventListener('click', pauseAnimation);
|
||||
resetBtn.addEventListener('click', resetAnimation);
|
||||
speedUpBtn.addEventListener('click', speedUp);
|
||||
slowDownBtn.addEventListener('click', slowDown);
|
||||
|
||||
// Add the first two digits automatically
|
||||
addDigit(); // Adds "3"
|
||||
addDigit(); // Adds "."
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
|
||||
return render_template_string(html_template, pi_digits=pi_digits)
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run(host='0.0.0.0', port=5000, debug=True)
|
40
z2/deployment.yaml
Normal file
40
z2/deployment.yaml
Normal file
@ -0,0 +1,40 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: web-app-deployment
|
||||
namespace: my-app
|
||||
spec:
|
||||
replicas: 2
|
||||
selector:
|
||||
matchLabels:
|
||||
app: web-app
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: web-app
|
||||
spec:
|
||||
containers:
|
||||
- name: web-app-container
|
||||
image: antonin193/simple-web-app:latest
|
||||
imagePullPolicy: Always
|
||||
ports:
|
||||
- containerPort: 5000
|
||||
resources:
|
||||
requests:
|
||||
memory: "64Mi"
|
||||
cpu: "100m"
|
||||
limits:
|
||||
memory: "128Mi"
|
||||
cpu: "200m"
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: 5000
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 10
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: 5000
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 20
|
7
z2/namespace.yaml
Normal file
7
z2/namespace.yaml
Normal file
@ -0,0 +1,7 @@
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: my-app
|
||||
labels:
|
||||
name: my-app
|
||||
environment: development
|
29
z2/persistent-storage.yaml
Normal file
29
z2/persistent-storage.yaml
Normal file
@ -0,0 +1,29 @@
|
||||
apiVersion: v1
|
||||
kind: PersistentVolume
|
||||
metadata:
|
||||
name: data-pv
|
||||
namespace: my-app
|
||||
labels:
|
||||
type: local
|
||||
spec:
|
||||
capacity:
|
||||
storage: 1Gi
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
persistentVolumeReclaimPolicy: Retain
|
||||
storageClassName: manual
|
||||
hostPath:
|
||||
path: /data/stateful
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: data-pvc
|
||||
namespace: my-app
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
storageClassName: manual
|
||||
resources:
|
||||
requests:
|
||||
storage: 1Gi
|
22
z2/prepare-app.sh
Normal file
22
z2/prepare-app.sh
Normal file
@ -0,0 +1,22 @@
|
||||
#!/bin/bash
|
||||
# Script to prepare the application environment
|
||||
|
||||
export REGION="local"
|
||||
echo "Preparing deployment for region: $REGION"
|
||||
|
||||
# Make sure scripts are executable
|
||||
chmod +x start-app.sh stop-app.sh
|
||||
|
||||
# Build and tag Docker image
|
||||
docker build -t simple-web-app:latest .
|
||||
docker tag simple-web-app:latest antonin193/simple-web-app:latest
|
||||
|
||||
# Push to Docker Hub if needed
|
||||
echo "Pushing image to Docker Hub..."
|
||||
docker push antonin193/simple-web-app:latest
|
||||
|
||||
# Create directory for persistent volume (Docker Desktop supports hostPath)
|
||||
sudo mkdir -p /data/stateful
|
||||
sudo chmod 777 /data/stateful
|
||||
|
||||
echo "Preparation complete: Docker image built, tagged, and volume directory created."
|
3
z2/requirements.txt
Normal file
3
z2/requirements.txt
Normal file
@ -0,0 +1,3 @@
|
||||
Flask>=2.0.0
|
||||
Werkzeug>=2.0.0
|
||||
gunicorn>=20.1.0
|
16
z2/service.yaml
Normal file
16
z2/service.yaml
Normal file
@ -0,0 +1,16 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: web-app-service
|
||||
namespace: my-app
|
||||
labels:
|
||||
app: web-app
|
||||
spec:
|
||||
type: LoadBalancer
|
||||
selector:
|
||||
app: web-app
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 80
|
||||
targetPort: 5000
|
||||
sessionAffinity: None
|
73
z2/start-app.sh
Normal file
73
z2/start-app.sh
Normal file
@ -0,0 +1,73 @@
|
||||
#!/bin/bash
|
||||
# Script to deploy the app locally using Docker Desktop + Kubernetes
|
||||
|
||||
# Set default region (optional but kept for consistency)
|
||||
if [ -z "$REGION" ]; then
|
||||
export REGION="local"
|
||||
echo "No region specified. Using default region: $REGION"
|
||||
else
|
||||
echo "Deploying to region: $REGION"
|
||||
fi
|
||||
|
||||
# Build Docker image
|
||||
echo "Building Docker image..."
|
||||
docker build -t antonin193/simple-web-app:latest .
|
||||
|
||||
# Push to Docker Hub (optional for local, but kept if needed across machines)
|
||||
echo "Pushing image to Docker Hub..."
|
||||
docker push antonin193/simple-web-app:latest
|
||||
|
||||
# Create Kubernetes Namespace
|
||||
echo "Creating namespace..."
|
||||
kubectl apply -f namespace.yaml
|
||||
|
||||
# Create PersistentVolume and PersistentVolumeClaim
|
||||
echo "Creating persistent storage..."
|
||||
kubectl apply -f persistent-storage.yaml
|
||||
|
||||
# Wait briefly for resources to be established
|
||||
sleep 2
|
||||
|
||||
# Deploy application
|
||||
echo "Creating Deployment..."
|
||||
kubectl apply -f deployment.yaml
|
||||
|
||||
echo "Creating StatefulSet..."
|
||||
kubectl apply -f statefulset.yaml
|
||||
|
||||
echo "Creating Service..."
|
||||
kubectl apply -f service.yaml
|
||||
|
||||
# Wait for LoadBalancer IP (Docker Desktop uses host network so it's usually localhost)
|
||||
echo "Waiting for LoadBalancer to obtain an external IP (or localhost for Docker Desktop)..."
|
||||
external_ip=""
|
||||
attempt=0
|
||||
max_attempts=4
|
||||
|
||||
while [ -z "$external_ip" ] && [ $attempt -lt $max_attempts ]; do
|
||||
sleep 10
|
||||
attempt=$((attempt+1))
|
||||
external_ip=$(kubectl get svc web-app-service -n my-app --template="{{range .status.loadBalancer.ingress}}{{.ip}}{{end}}" 2>/dev/null)
|
||||
|
||||
if [ -z "$external_ip" ]; then
|
||||
echo "Waiting for external IP... Attempt $attempt of $max_attempts"
|
||||
fi
|
||||
done
|
||||
|
||||
# Fallback to localhost if no external IP is found (common in Docker Desktop)
|
||||
if [ -z "$external_ip" ]; then
|
||||
external_ip="localhost"
|
||||
echo ""
|
||||
echo "=========================================================="
|
||||
echo "Could not get external IP from LoadBalancer. Defaulting to localhost."
|
||||
echo "You can try accessing your app at: http://localhost:80"
|
||||
echo "Or check service status manually with:"
|
||||
echo "kubectl get svc web-app-service -n my-app"
|
||||
echo "=========================================================="
|
||||
else
|
||||
echo ""
|
||||
echo "=========================================================="
|
||||
echo "Application deployed successfully!"
|
||||
echo "You can try accessing your app at: http://localhost:80"
|
||||
echo "=========================================================="
|
||||
fi
|
48
z2/statefulset.yaml
Normal file
48
z2/statefulset.yaml
Normal file
@ -0,0 +1,48 @@
|
||||
apiVersion: apps/v1
|
||||
kind: StatefulSet
|
||||
metadata:
|
||||
name: stateful-app
|
||||
namespace: my-app
|
||||
spec:
|
||||
serviceName: "stateful-app"
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: stateful-app
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: stateful-app
|
||||
spec:
|
||||
containers:
|
||||
- name: stateful-app-container
|
||||
image: antonin193/simple-web-app:latest
|
||||
imagePullPolicy: Always
|
||||
ports:
|
||||
- containerPort: 5000
|
||||
resources:
|
||||
requests:
|
||||
memory: "64Mi"
|
||||
cpu: "100m"
|
||||
limits:
|
||||
memory: "128Mi"
|
||||
cpu: "200m"
|
||||
volumeMounts:
|
||||
- name: app-storage
|
||||
mountPath: /data
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: 5000
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 20
|
||||
volumeClaimTemplates:
|
||||
- metadata:
|
||||
name: app-storage
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
storageClassName: manual
|
||||
resources:
|
||||
requests:
|
||||
storage: 1Gi
|
50
z2/stop-app.sh
Normal file
50
z2/stop-app.sh
Normal file
@ -0,0 +1,50 @@
|
||||
#!/bin/bash
|
||||
# Delete Kubernetes objects in reverse order
|
||||
|
||||
# Check if REGION environment variable is set
|
||||
if [ -z "$REGION" ]; then
|
||||
# Default region if not set
|
||||
export REGION="westeurope"
|
||||
echo "No region specified. Using default region: $REGION"
|
||||
else
|
||||
echo "Stopping application in region: $REGION"
|
||||
fi
|
||||
|
||||
# For AKS deployments, make sure we're connected to the right cluster
|
||||
if command -v az &> /dev/null; then
|
||||
echo "Checking AKS connection for region $REGION..."
|
||||
# You might need to adjust these parameters based on your specific Azure setup
|
||||
RESOURCE_GROUP="flask-rg-$REGION"
|
||||
CLUSTER_NAME="flask-aks-$REGION"
|
||||
|
||||
# Try to connect to the Azure cluster
|
||||
if ! az aks get-credentials --resource-group $RESOURCE_GROUP --name $CLUSTER_NAME --overwrite-existing 2>/dev/null; then
|
||||
echo "Warning: Could not connect to AKS cluster in $REGION. Continuing with current kubectl context."
|
||||
else
|
||||
echo "Successfully connected to AKS cluster in $REGION"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "Stopping application..."
|
||||
|
||||
# Delete Service first to stop incoming traffic
|
||||
kubectl delete -f service.yaml 2>/dev/null || echo "Service could not be deleted or does not exist."
|
||||
|
||||
# Delete StatefulSet and wait for pods to terminate
|
||||
kubectl delete -f statefulset.yaml 2>/dev/null || echo "StatefulSet could not be deleted or does not exist."
|
||||
|
||||
# Delete Deployment
|
||||
kubectl delete -f deployment.yaml 2>/dev/null || echo "Deployment could not be deleted or does not exist."
|
||||
|
||||
# Wait for pods to terminate
|
||||
echo "Waiting for pods to terminate..."
|
||||
kubectl wait --for=delete pod --selector=app=web-app -n my-app --timeout=60s 2>/dev/null || true
|
||||
kubectl wait --for=delete pod --selector=app=stateful-app -n my-app --timeout=60s 2>/dev/null || true
|
||||
|
||||
# Delete PersistentVolume and PersistentVolumeClaim
|
||||
kubectl delete -f persistent-storage.yaml 2>/dev/null || echo "PersistentVolume and PersistentVolumeClaim could not be deleted or do not exist."
|
||||
|
||||
# Delete Namespace (this will delete all resources in the namespace)
|
||||
kubectl delete -f namespace.yaml 2>/dev/null || echo "Namespace could not be deleted or does not exist."
|
||||
|
||||
echo "Application stopped in region $REGION: All Kubernetes objects have been deleted."
|
Loading…
Reference in New Issue
Block a user