# TP DevOps 

...et en cours de restructuration par Oualim (commentaires pour le futur pour tester une autre création à l'IA d'un tp, en tenant compte des manquements et des erreurs pour ne pas laisser l'IA les reproduire)

Original pourrit: https://cloud.bonisco.fr/s/Cb2bFdLXeFEFcLm 

> Commentaire de Oualim (Celui de Deepseek est juste un peu moins pourri mais c'était aussi de la merde)
> Ce TP contient encore beaucoup de fautes sur les manips et fichiers et aucun sens sur la structure car l'IA a juste pris les mots clefs de "devops" sur internet, copié des fichiers et mélangé des concepts sans aucune présentation ni introduction ou explications (ex : correctif de l'ancien tp tout en bas :-/ ; terraform sur des docker ??? ; kub en faisant un doublon d'appli potentiellement conflictuel, aucune explication ni structure d'étape pour comprendre les logiques de docker et k8')


#📚 RÉCAPITULATIF DES CORRECTIONS APPORTÉES

## ✅ Corrections majeures :
* Python : Ajout de Response dans les imports FastAPI
* Docker Compose : Chemins corrigés, versions spécifiées
* Kubernetes : Changé LoadBalancer → NodePort pour Windows
* Image Docker : Tag :local au lieu de :latest pour K8s
* Terraform : Suppression du provider Docker problématique
* Scripts : Ajout de gestion d'erreurs robuste
* Tests : Ajout de tests unitaires Python
* Health checks : Ajout dans Docker Compose et K8s


## 🎯 Améliorations :
* Structure de projet logique et organisée
* Scripts PowerShell avec paramètres et gestion d'erreurs
* Configuration monitoring fonctionnelle
* Documentation claire étape par étape
* Solutions de dépannage incluses
* Versionning spécifique des images Docker


> Commentaire de Oualim 
> Ce TP permet pour l'instant, de réaliser le paysage de docker pour un débutant, de comprendre une organisation de dossiers et fichiers, de créer qq containeurs docker : 
> - http://localhost:8000 (App)
> - http://localhost:8000/docs (Documentation API)
> - http://localhost:9090 (Prometheus)
> - http://localhost:3000 (Grafana - admin/admin123)
> - http://localhost:9000 (Portainer)


# 🚀 TP DevOps Windows - Stack Complète Locale (Corrigé & Optimisé)

## 📋 PRÉ-REQUIS WINDOWS - Installation Pas à Pas

### 🔧 1. Préparation du système (PowerShell Administrateur)

Ouvrir PowerShell en tant qu'ADMINISTRATEUR (clic droit → Exécuter en tant qu'administrateur)

#🚀 TP DevOps Windows - Stack Complète Locale (Corrigé & Optimisé)


#📋 PRÉ-REQUIS WINDOWS - Installation Pas à Pas

Avant tout, bien penser à ACTIVER la fonction de VIRTUALISATION dans le BIOS du systeme de votre pc

#🔧 1. Préparation du système (PowerShell Administrateur)

# Ouvrir PowerShell en tant qu'ADMINISTRATEUR (clic droit → Exécuter en tant qu'administrateur)

# 1. Activer les fonctionnalités Windows nécessaires
Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-Subsystem-Linux
Enable-WindowsOptionalFeature -Online -FeatureName VirtualMachinePlatform
```
2. Installer WSL2 et Ubuntu
```
wsl --install -d Ubuntu-22.04
```
3. Définir WSL2 comme version par défaut
```
wsl --set-default-version 2
```
4. Redémarrer l'ordinateur (OBLIGATOIRE)
```
Restart-Computer

#📥 2. Installation des outils (après redémarrage)

1. Installer Chocolatey (package manager Windows)
```
Set-ExecutionPolicy Bypass -Scope Process -Force
[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072
iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))
```
2. Installer tous les outils
```
choco install -y git docker-desktop vscode kubernetes-cli minikube terraform helm postman

#🐧 3. Configuration WSL2 et Ubuntu

# Vérifier l'installation WSL
wsl --list --verbose
# Doit afficher : Ubuntu-22.04 Running 2
```

Entrer dans WSL pour configurer
```
wsl
```
Dans le terminal Ubuntu WSL :
```
sudo apt update
sudo apt upgrade -y
sudo apt install -y python3 python3-pip python3-venv nodejs npm curl wget
```
Créer un alias pratique
```
echo "alias k=kubectl" >> ~/.bashrc
source ~/.bashrc
exit  # Retour à PowerShell


#🐋 ÉTAPE 1 : CONFIGURATION DOCKER DESKTOP

#⚙️ Configuration manuelle obligatoire :
Ouvrir Docker Desktop après installation

Settings → General :
* ✅ Use WSL 2 based engine
* ✅ Expose daemon on tcp://localhost:2375 without TLS
Settings → Resources → WSL Integration :
* ✅ Enable integration with my default WSL distro
* ✅ Ubuntu-22.04
Settings → Kubernetes :
* ✅ Enable Kubernetes
* ✅ Deploy Docker Stacks to Kubernetes by default

Settings → Advanced :
* CPUs : 4 (ou 50% de vos CPUs)
* Memory : 4.0 GB (ou 50% de votre RAM)
* Swap : 1.0 GB
Appliquer et redémarrer Docker Desktop

### ✅ Vérification Docker/WSL2 :
```
# Dans PowerShell
docker --version
# Doit afficher : Docker version 24.0.x
docker run hello-world
# Doit afficher "Hello from Docker!"
```
Vérifier l'intégration WSL2
```
wsl -d Ubuntu-22.04 -e docker version
# Doit fonctionner sans erreur


#📁 ÉTAPE 2 : STRUCTURE DU PROJET OPTIMISÉE

#🗂️ Création de la structure :

Créer le dossier principal
```
mkdir C:\DevOpsProject
cd C:\DevOpsProject
```

Structure ORGANISÉE et LOGIQUE
```
New-Item -ItemType Directory -Path @(
    "src\app",
    "src\app\tests",
    "docker",
    "kubernetes\manifests",
    "kubernetes\helm",
    "terraform",
    ".github\workflows",
    "monitoring",
    "scripts",
    "docs",
    "config"
)
```

Structure finale :
```
C:\DevOpsProject\
├── src\                    # Code source
│   ├── app\               # Application Python
│   └── app\tests\         # Tests unitaires
├── docker\                # Dockerfiles & Compose
├── kubernetes\            # K8s manifests
├── terraform\             # Infrastructure as Code
├── monitoring\            # Prometheus, Grafana
├── scripts\               # Scripts PowerShell
├── .github\workflows\     # CI/CD
├── config\                # Fichiers de config
└── docs\                  # Documentation


#🐍 ÉTAPE 3 : APPLICATION PYTHON CORRIGÉE

#3.1 Fichier : src\app\requirements.txt

# commentaire de Oualim : Ce fichier installera les dépendances dans le conteneur, pas sur votre machine

fastapi==0.104.1
uvicorn[standard]==0.24.0
pydantic==2.5.0
prometheus-client==0.19.0
python-dotenv==1.0.0
pytest==7.4.3
httpx==0.25.1

#3.2 Fichier : src\app\main.py (CORRIGÉ)

from fastapi import FastAPI, Request, Response, HTTPException
from prometheus_client import Counter, Histogram, generate_latest, CONTENT_TYPE_LATEST
import time
import os
import logging
from typing import Dict

# Configuration du logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

app = FastAPI(
    title="DevOps Windows API",
    description="Application de démonstration DevOps sous Windows",
    version="1.0.0"
)

# Métriques Prometheus - CORRIGÉ
REQUEST_COUNT = Counter(
    'http_requests_total',
    'Total HTTP Requests',
    ['method', 'endpoint', 'status']
)
REQUEST_LATENCY = Histogram(
    'http_request_duration_seconds',
    'HTTP Request latency',
    ['method', 'endpoint']
)

@app.middleware("http")
async def monitor_requests(request: Request, call_next):
    """Middleware pour monitorer les requêtes"""
    start_time = time.time()
    
    try:
        response = await call_next(request)
        status_code = str(response.status_code)
    except Exception as e:
        status_code = "500"
        response = Response(
            content=f"Internal Server Error: {str(e)}",
            status_code=500
        )
    
    process_time = time.time() - start_time
    
    # Enregistrement des métriques - CORRIGÉ
    REQUEST_COUNT.labels(
        method=request.method,
        endpoint=request.url.path,
        status=status_code
    ).inc()
    
    REQUEST_LATENCY.labels(
        method=request.method,
        endpoint=request.url.path
    ).observe(process_time)
    
    # Ajouter le temps de traitement dans les headers
    response.headers["X-Process-Time"] = f"{process_time:.3f}s"
    
    return response

@app.get("/")
async def home():
    """Endpoint principal"""
    return {
        "message": "🚀 DevOps Stack Windows - Fonctionnel !",
        "environment": os.getenv("ENV", "development"),
        "status": "running",
        "hostname": os.getenv("HOSTNAME", "windows-devops"),
        "version": "1.0.0"
    }

@app.get("/health")
async def health():
    """Health check pour Kubernetes et load balancers"""
    return {
        "status": "healthy",
        "timestamp": time.time(),
        "service": "devops-app"
    }

@app.get("/metrics")
async def metrics():
    """Endpoint Prometheus - CORRIGÉ"""
    try:
        data = generate_latest()
        return Response(
            content=data,
            media_type=CONTENT_TYPE_LATEST,
            headers={"Cache-Control": "no-cache"}
        )
    except Exception as e:
        logger.error(f"Error generating metrics: {e}")
        raise HTTPException(status_code=500, detail="Metrics generation failed")

@app.get("/info")
async def info():
    """Informations système"""
    return {
        "python_version": "3.11",
        "platform": "windows",
        "service": "FastAPI DevOps",
        "features": ["docker", "kubernetes", "monitoring", "ci-cd"]
    }

@app.get("/env")
async def show_env():
    """Afficher les variables d'environnement (sécurisé)"""
    safe_env = {
        "ENV": os.getenv("ENV", "not-set"),
        "HOSTNAME": os.getenv("HOSTNAME", "not-set"),
        "PYTHON_VERSION": os.getenv("PYTHON_VERSION", "not-set")
    }
    return safe_env

# Pour le développement local
if __name__ == "__main__":
    import uvicorn
    logger.info("Starting FastAPI server...")
    uvicorn.run(
        app,
        host="0.0.0.0",
        port=8000,
        log_level="info",
        reload=True  # Auto-reload en développement
    )
```

### 3.3 Fichier : `src\app\tests\test_main.py` (NOUVEAU)
```
import pytest
from fastapi.testclient import TestClient
from main import app

client = TestClient(app)

def test_home_endpoint():
    """Test de l'endpoint principal"""
    response = client.get("/")
    assert response.status_code == 200
    data = response.json()
    assert "message" in data
    assert "status" in data
    assert data["status"] == "running"

def test_health_endpoint():
    """Test du health check"""
    response = client.get("/health")
    assert response.status_code == 200
    data = response.json()
    assert data["status"] == "healthy"
    assert "service" in data

def test_metrics_endpoint():
    """Test des métriques Prometheus"""
    response = client.get("/metrics")
    assert response.status_code == 200
    assert "text/plain" in response.headers["content-type"]
    assert "http_requests_total" in response.text

def test_info_endpoint():
    """Test des informations"""
    response = client.get("/info")
    assert response.status_code == 200
    data = response.json()
    assert "python_version" in data
    assert "platform" in data

def test_env_endpoint():
    """Test des variables d'environnement"""
    response = client.get("/env")
    assert response.status_code == 200
    data = response.json()
    assert "ENV" in data
```

### 3.4 Fichier : `src\app\Dockerfile` (OPTIMISÉ)

Étape 1 : Build
```
FROM python:3.11-slim AS builder

WORKDIR /app

# Copier les requirements d'abord (cache Docker)
COPY requirements.txt .
RUN pip install --user --no-cache-dir -r requirements.txt

# Étape 2 : Runtime
FROM python:3.11-slim

WORKDIR /app

# Copier les packages depuis le builder
COPY --from=builder /root/.local /root/.local
COPY . .

# Ajouter au PATH
ENV PATH=/root/.local/bin:$PATH
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1
ENV ENV=production
ENV HOSTNAME=devops-container

# Exposer le port
EXPOSE 8000

# Utiliser un user non-root pour la sécurité
RUN useradd -m -u 1000 devopsuser && chown -R devopsuser:devopsuser /app
USER devopsuser

# Commande de démarrage
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000", "--no-access-log"]



#🐳 ÉTAPE 4 : DOCKER COMPOSE CORRIGÉ

#4.1 Fichier : docker\docker-compose.yml (RECOMMENCÉ)

## 🐳 ÉTAPE 4 : DOCKER COMPOSE CORRIGÉ
### 4.1 Fichier : `docker\docker-compose.yml` (RECOMMENCÉ)
```
version: '3.8'

services:
  # Application principale
  app:
    build:
      context: ../src/app
      dockerfile: Dockerfile
    container_name: devops-app
    ports:
      - "8000:8000"
    environment:
      - ENV=development
      - HOSTNAME=devops-local
    volumes:
      - ../src/app:/app
      - app-logs:/app/logs
    networks:
      - devops-network
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s
    labels:
      - "com.devops.description=Application FastAPI"
      - "prometheus.scrape=true"
      - "prometheus.port=8000"
      - "prometheus.path=/metrics"

  # Prometheus pour le monitoring
  prometheus:
    image: prom/prometheus:v2.47.2
    container_name: devops-prometheus
    ports:
      - "9090:9090"
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml:ro
      - prometheus-data:/prometheus
    command:
      - '--config.file=/etc/prometheus/prometheus.yml'
      - '--storage.tsdb.path=/prometheus'
      - '--web.console.libraries=/etc/prometheus/console_libraries'
      - '--web.console.templates=/etc/prometheus/consoles'
      - '--storage.tsdb.retention.time=200h'
      - '--web.enable-lifecycle'
    networks:
      - devops-network
    restart: unless-stopped
    depends_on:
      - app

  # Grafana pour les dashboards
  grafana:
    image: grafana/grafana:10.2.0
    container_name: devops-grafana
    ports:
      - "3000:3000"
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=admin123
      - GF_SECURITY_ADMIN_USER=admin
      - GF_INSTALL_PLUGINS=grafana-piechart-panel,grafana-clock-panel
      - GF_USERS_ALLOW_SIGN_UP=false
      - GF_SERVER_DOMAIN=localhost
    volumes:
      - grafana-data:/var/lib/grafana
      - ./grafana-dashboards:/etc/grafana/provisioning/dashboards
      - ./grafana-datasources:/etc/grafana/provisioning/datasources
    networks:
      - devops-network
    restart: unless-stopped
    depends_on:
      - prometheus

  # Portainer pour gérer Docker
  portainer:
    image: portainer/portainer-ce:2.19.3
    container_name: devops-portainer
    ports:
      - "9000:9000"
      - "9443:9443"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - portainer-data:/data
    networks:
      - devops-network
    restart: unless-stopped
    command: -H unix:///var/run/docker.sock

  # Nginx comme reverse proxy (optionnel)
  nginx:
    image: nginx:alpine
    container_name: devops-nginx
    ports:
      - "8080:80"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
    networks:
      - devops-network
    restart: unless-stopped
    depends_on:
      - app

networks:
  devops-network:
    driver: bridge
    name: devops-network

volumes:
  prometheus-data:
    name: prometheus-data
  grafana-data:
    name: grafana-data
  portainer-data:
    name: portainer-data
  app-logs:
    name: app-logs

#4.2 Fichier : docker\prometheus.yml (À CRÉER)

global:
  scrape_interval: 15s
  evaluation_interval: 15s
  external_labels:
    cluster: 'windows-devops'
    environment: 'development'

# Règles d'alerte
rule_files:
  # - "alerts.yml"

# Configuration de scraping
scrape_configs:
  # Scraper l'application FastAPI
  - job_name: 'fastapi-app'
    static_configs:
      - targets: ['app:8000']
        labels:
          app: 'devops-app'
          component: 'backend'
          tier: 'application'

  # Scraper Prometheus lui-même
  - job_name: 'prometheus'
    static_configs:
      - targets: ['localhost:9090']
        labels:
          component: 'monitoring'

  # Découverte de service Docker
  - job_name: 'docker'
    static_configs:
      - targets: ['host.docker.internal:9323']
    metrics_path: /metrics
    scheme: http

# Alerting (exemple)
# alerting:
#   alertmanagers:
#     - static_configs:
#         - targets: []

#4.3 Exécution Docker Compose :

## 4.3 Exécution Docker Compose :

Se placer dans le dossier docker
```
cd C:\DevOpsProject\docker
```
Lancer en arrière-plan
```
docker-compose up -d
```
Vérifier que tout fonctionne
```
docker-compose ps
```
Voir les logs de l'application
```
docker-compose logs app
```
Tester l'application
```
curl http://localhost:8000
```
ou dans PowerShell
```
Invoke-WebRequest -Uri "http://localhost:8000" -UseBasicParsing


#☸️ ÉTAPE 5 : KUBERNETES SIMPLIFIÉ POUR WINDOWS

#5.1 Activer Kubernetes dans Docker Desktop :

1. Docker Desktop → Settings (roue crantée)
2. Onglet Kubernetes
3. ✅ Enable Kubernetes
4. ✅ Show system containers (optional)
5. Apply & Restart (patienter 2-3 minutes)

#5.2 Vérification :

### 5.2 Vérification :

Vérifier que Kubernetes fonctionne
```
kubectl cluster-info
# Doit afficher : Kubernetes control plane is running at https://...
kubectl get nodes
# Doit afficher : docker-desktop Ready
kubectl get pods --all-namespaces
# Doit montrer les pods système

#5.3 Fichier : kubernetes\manifests\namespace.yaml

#### 5.3 Fichier : kubernetes\manifests\namespace.yaml
```
apiVersion: v1
kind: Namespace
metadata:
  name: devops-demo
  labels:
    name: devops-demo
    environment: development

#5.4 Fichier : kubernetes\manifests\configmap.yaml (NOUVEAU)

### 5.4 Fichier : kubernetes\manifests\configmap.yaml (NOUVEAU)
```
apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
  namespace: devops-demo
data:
  ENV: "production"
  APP_NAME: "devops-windows-app"
  LOG_LEVEL: "INFO"
  PYTHONUNBUFFERED: "1"

#5.5 Fichier : kubernetes\manifests\deployment.yaml (SIMPLIFIÉ)

### 5.5 Fichier : kubernetes\manifests\deployment.yaml (SIMPLIFIÉ)
```
apiVersion: apps/v1
kind: Deployment
metadata:
  name: devops-app
  namespace: devops-demo
  labels:
    app: devops-app
    version: v1
spec:
  replicas: 2
  selector:
    matchLabels:
      app: devops-app
  template:
    metadata:
      labels:
        app: devops-app
      annotations:
        prometheus.io/scrape: "true"
        prometheus.io/port: "8000"
        prometheus.io/path: "/metrics"
    spec:
      containers:
      - name: app
        image: devops-app:local  # IMPORTANT : Utiliser l'image locale
        imagePullPolicy: IfNotPresent  # Ne pas pull depuis Docker Hub
        ports:
        - containerPort: 8000
          name: http
        envFrom:
        - configMapRef:
            name: app-config
        env:
        - name: HOSTNAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.name
        resources:
          requests:
            memory: "128Mi"
            cpu: "100m"
          limits:
            memory: "256Mi"
            cpu: "200m"
        readinessProbe:
          httpGet:
            path: /health
            port: 8000
          initialDelaySeconds: 10
          periodSeconds: 15
        livenessProbe:
          httpGet:
            path: /health
            port: 8000
          initialDelaySeconds: 30
          periodSeconds: 30
---
apiVersion: v1
kind: Service
metadata:
  name: devops-app-service
  namespace: devops-demo
spec:
  selector:
    app: devops-app
  ports:
  - port: 80
    targetPort: 8000
    protocol: TCP
    name: http
  type: NodePort  # CORRIGÉ : NodePort au lieu de LoadBalancer pour Windows

#5.6 Déploiement Kubernetes :

# 1. Build l'image Docker avec un tag spécifique
cd C:\DevOpsProject\src\app
docker build -t devops-app:local .

# 2. Appliquer les manifests Kubernetes
cd C:\DevOpsProject\kubernetes\manifests

# Dans l'ordre :
kubectl apply -f namespace.yaml
kubectl apply -f configmap.yaml
kubectl apply -f deployment.yaml

# 3. Vérifier le déploiement
kubectl get all -n devops-demo

# 4. Obtenir l'URL d'accès
kubectl get svc -n devops-demo devops-app-service -o jsonpath='{.spec.ports[0].nodePort}'
# Notez le numéro de port (ex: 30456)

# 5. Accéder à l'application
# http://localhost:[PORT]  (ex: http://localhost:30456)


#🏗️ ÉTAPE 6 : TERRAFORM SIMPLIFIÉ (OPTIONNEL)

#⚠️ IMPORTANT : Terraform + Docker sur Windows/WSL2 est complexe.
#Je recommande de SAUTER cette étape pour les débutants.
Si vous voulez quand même essayer :

#6.1 Fichier : terraform\main.tf (VERSION SIMPLIFIÉE)

# Terraform minimal pour Windows - KUBERNETES UNIQUEMENT
terraform {
  required_version = ">= 1.5.0"
  
  required_providers {
    kubernetes = {
      source  = "hashicorp/kubernetes"
      version = "~> 2.23"
    }
  }
}

# Provider Kubernetes avec Docker Desktop
provider "kubernetes" {
  config_path    = "~/.kube/config"
  config_context = "docker-desktop"
}

# Namespace
resource "kubernetes_namespace" "devops" {
  metadata {
    name = "terraform-ns"
    
    labels = {
      created-by = "terraform"
      environment = "development"
    }
  }
}

# Déploiement simple
resource "kubernetes_deployment" "app" {
  metadata {
    name      = "terraform-app"
    namespace = kubernetes_namespace.devops.metadata[0].name
    
    labels = {
      app = "terraform-app"
    }
  }

  spec {
    replicas = 1

    selector {
      match_labels = {
        app = "terraform-app"
      }
    }

    template {
      metadata {
        labels = {
          app = "terraform-app"
        }
      }

      spec {
        container {
          name  = "app"
          image = "nginx:alpine"  # Image simple pour test
          
          port {
            container_port = 80
          }
          
          resources {
            limits = {
              cpu    = "100m"
              memory = "128Mi"
            }
            requests = {
              cpu    = "50m"
              memory = "64Mi"
            }
          }
        }
      }
    }
  }
  
  depends_on = [kubernetes_namespace.devops]
}

# Service
resource "kubernetes_service" "app" {
  metadata {
    name      = "terraform-service"
    namespace = kubernetes_namespace.devops.metadata[0].name
  }

  spec {
    selector = {
      app = kubernetes_deployment.app.spec[0].template[0].metadata[0].labels.app
    }

    port {
      port        = 8080
      target_port = 80
    }

    type = "NodePort"
  }
}

# Output utile
output "application_url" {
  value = "http://localhost:${kubernetes_service.app.spec[0].port[0].node_port}"
}

output "namespace" {
  value = kubernetes_namespace.devops.metadata[0].name
}

#6.2 Utilisation Terraform :

cd C:\DevOpsProject\terraform

# Initialiser
terraform init

# Voir ce qui va être créé
terraform plan

# Appliquer (tapez 'yes' quand demandé)
terraform apply

# Pour détruire
terraform destroy


#📦 ÉTAPE 7 : HELM SIMPLE POUR WINDOWS

#7.1 Création d'un chart Helm simple :

cd C:\DevOpsProject\kubernetes\helm

# Créer un chart Helm
helm create devops-chart

# Supprimer les fichiers inutiles créés par défaut
Remove-Item devops-chart\templates\* -Recurse -Force

# Créer une structure propre
New-Item -ItemType Directory -Path @(
    "devops-chart\templates",
    "devops-chart\charts"
)
#7.2 Fichier : kubernetes\helm\devops-chart\Chart.yaml

apiVersion: v2
name: devops-chart
description: Chart Helm pour DevOps Windows
type: application
version: 0.1.0
appVersion: "1.0.0"
#7.3 Fichier : kubernetes\helm\devops-chart\values.yaml

# Configuration de l'application
app:
  name: "devops-helm-app"
  image:
    repository: "devops-app"
    tag: "local"
    pullPolicy: "IfNotPresent"
  
  replicaCount: 2
  
  service:
    type: NodePort
    port: 80
    targetPort: 8000
  
  resources:
    requests:
      memory: "128Mi"
      cpu: "100m"
    limits:
      memory: "256Mi"
      cpu: "200m"
  
  env:
    ENV: "production"
    LOG_LEVEL: "INFO"

# Monitoring
monitoring:
  enabled: true
  prometheusScrape: true

#7.4 Fichier : kubernetes\helm\devops-chart\templates\deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ .Values.app.name }}
  labels:
    app: {{ .Values.app.name }}
    chart: {{ .Chart.Name }}
    version: {{ .Chart.Version }}
spec:
  replicas: {{ .Values.app.replicaCount }}
  selector:
    matchLabels:
      app: {{ .Values.app.name }}
  template:
    metadata:
      labels:
        app: {{ .Values.app.name }}
      {{- if .Values.monitoring.enabled }}
      annotations:
        prometheus.io/scrape: "{{ .Values.monitoring.prometheusScrape }}"
        prometheus.io/port: "{{ .Values.app.service.targetPort }}"
        prometheus.io/path: "/metrics"
      {{- end }}
    spec:
      containers:
      - name: {{ .Values.app.name }}
        image: "{{ .Values.app.image.repository }}:{{ .Values.app.image.tag }}"
        imagePullPolicy: {{ .Values.app.image.pullPolicy }}
        ports:
        - containerPort: {{ .Values.app.service.targetPort }}
          name: http
        env:
        {{- range $key, $value := .Values.app.env }}
        - name: {{ $key }}
          value: {{ $value | quote }}
        {{- end }}
        resources:
          {{- toYaml .Values.app.resources | nindent 10 }}
        readinessProbe:
          httpGet:
            path: /health
            port: {{ .Values.app.service.targetPort }}
          initialDelaySeconds: 10
          periodSeconds: 15

#7.5 Installation avec Helm :

cd C:\DevOpsProject\kubernetes\helm

# Installer le chart
helm install devops-release ./devops-chart -n devops-demo --create-namespace

# Vérifier l'installation
helm list -n devops-demo

# Mettre à jour
helm upgrade devops-release ./devops-chart -n devops-demo

# Désinstaller
helm uninstall devops-release -n devops-demo


#🔄 ÉTAPE 8 : GITHUB ACTIONS CORRIGÉ

#8.1 Fichier : .github\workflows\ci-cd.yml (PRATIQUE)

name: CI/CD DevOps Windows

on:
  push:
    branches: [ main, master ]
  pull_request:
    branches: [ main, master ]
  workflow_dispatch:  # Permet de déclencher manuellement

jobs:
  # Job 1 : Tests de l'application
  test:
    runs-on: windows-latest
    
    steps:
    - name: Checkout du code
      uses: actions/checkout@v3
    
    - name: Setup Python
      uses: actions/setup-python@v4
      with:
        python-version: '3.11'
    
    - name: Install dependencies
      run: |
        python -m pip install --upgrade pip
        pip install -r src/app/requirements.txt
    
    - name: Run tests
      run: |
        cd src/app
        python -m pytest tests/ -v --cov=. --cov-report=xml
    
    - name: Upload coverage
      uses: codecov/codecov-action@v3
      with:
        file: ./src/app/coverage.xml
        flags: unittests

  # Job 2 : Build Docker
  build:
    needs: test
    runs-on: windows-latest
    
    steps:
    - uses: actions/checkout@v3
    
    - name: Setup Docker Buildx
      uses: docker/setup-buildx-action@v2
    
    - name: Login to DockerHub (seulement sur main)
      if: github.ref == 'refs/heads/main'
      uses: docker/login-action@v2
      with:
        username: ${{ secrets.DOCKER_USERNAME }}
        password: ${{ secrets.DOCKER_PASSWORD }}
    
    - name: Build Docker image
      uses: docker/build-push-action@v4
      with:
        context: ./src/app
        push: ${{ github.ref == 'refs/heads/main' }}
        tags: |
          ${{ secrets.DOCKER_USERNAME }}/devops-app:latest
          ${{ secrets.DOCKER_USERNAME }}/devops-app:${{ github.sha }}
        cache-from: type=gha
        cache-to: type=gha,mode=max

  # Job 3 : Déploiement (optionnel pour Windows)
  deploy:
    needs: build
    runs-on: windows-latest
    if: github.ref == 'refs/heads/main'
    
    steps:
    - uses: actions/checkout@v3
    
    - name: Setup kubectl
      uses: azure/setup-kubectl@v3
      with:
        version: 'latest'
    
    - name: Configure kubeconfig
      run: |
        # Pour une vraie installation, configurez votre kubeconfig ici
        # Pour la démo, on affiche juste un message
        echo "Dans un environnement réel, vous configureriez kubeconfig ici"
        echo "Utilisez des secrets GitHub pour stocker votre configuration"
    
    - name: Deploy to Kubernetes
      run: |
        echo "Déploiement simulé"
        echo "En production, vous utiliseriez :"
        echo "kubectl apply -f kubernetes/manifests/"
        echo "ou"
        echo "helm upgrade --install ..."


#📊 ÉTAPE 9 : MONITORING FONCTIONNEL

#9.1 Dashboard Grafana prêt à l'emploi :

# Créer un dossier pour les dashboards Grafana
mkdir C:\DevOpsProject\docker\grafana-dashboards

# Créer un fichier de dashboard simple
@'
{
  "dashboard": {
    "title": "DevOps Windows Dashboard",
    "panels": [
      {
        "title": "Requêtes HTTP",
        "type": "graph",
        "targets": [{
          "expr": "rate(http_requests_total[5m])",
          "legendFormat": "{{method}} {{endpoint}}"
        }]
      },
      {
        "title": "Latence",
        "type": "graph",
        "targets": [{
          "expr": "histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m]))",
          "legendFormat": "p95"
        }]
      }
    ]
  }
}
'@ | Out-File -FilePath "C:\DevOpsProject\docker\grafana-dashboards\dashboard.json" -Encoding UTF8

#9.2 Accès aux interfaces :

✅ Après docker-compose up -d :

1. Application FastAPI : http://localhost:8000
2. Documentation API : http://localhost:8000/docs (Swagger UI)
3. Métriques Prometheus : http://localhost:9090
4. Grafana Dashboard : http://localhost:3000
   → Login : admin / admin123
5. Portainer : http://localhost:9000

✅ Après déploiement Kubernetes :

1. Application K8s : http://localhost:[NODE_PORT]
   (trouvez le port avec : kubectl get svc -n devops-demo)


#🛠️ ÉTAPE 10 : SCRIPTS PRATIQUES

#10.1 Script : scripts\setup.ps1 (AMÉLIORÉ)

# DevOps Windows Setup Script
# PowerShell 5.1+ requis

param(
    [switch]$SkipDocker = $false,
    [switch]$SkipK8s = $false,
    [switch]$Help = $false
)

if ($Help) {
    Write-Host "Usage: .\setup.ps1 [-SkipDocker] [-SkipK8s] [-Help]" -ForegroundColor Cyan
    Write-Host "  -SkipDocker   : Ne pas démarrer Docker Compose" -ForegroundColor Gray
    Write-Host "  -SkipK8s      : Ne pas déployer sur Kubernetes" -ForegroundColor Gray
    Write-Host "  -Help         : Afficher cette aide" -ForegroundColor Gray
    exit 0
}

Write-Host "========================================" -ForegroundColor Green
Write-Host "       DEVOPS WINDOWS SETUP" -ForegroundColor Green
Write-Host "========================================" -ForegroundColor Green
Write-Host ""

# 1. VÃ©rification des outils
Write-Host "[1/5] Vérification des outils..." -ForegroundColor Yellow

$tools = @(
    @{Name="Docker"; Cmd="docker --version"},
    @{Name="kubectl"; Cmd="kubectl version --client"},
    @{Name="helm"; Cmd="helm version"},
    @{Name="python"; Cmd="python --version"}
)

foreach ($tool in $tools) {
    try {
        $output = Invoke-Expression $tool.Cmd 2>&1
        Write-Host "  ✅ $($tool.Name) : OK" -ForegroundColor Green
    } catch {
        Write-Host "  ❌ $($tool.Name) : MANQUANT" -ForegroundColor Red
        Write-Host "     Installez avec: choco install $($tool.Name.ToLower())" -ForegroundColor Gray
    }
}

Write-Host ""

# 2. Build de l'application
Write-Host "[2/5] Build de l'application Docker..." -ForegroundColor Yellow
try {
    Set-Location "$PSScriptRoot\..\src\app"
    docker build -t devops-app:local .
    Write-Host "  ✅ Image Docker construite" -ForegroundColor Green
    Set-Location $PSScriptRoot
} catch {
    Write-Host "  ❌ Erreur lors du build Docker" -ForegroundColor Red
    Write-Host $_.Exception.Message
    exit 1
}

# 3. Docker Compose
if (-not $SkipDocker) {
    Write-Host "[3/5] DÃ©marrage Docker Compose..." -ForegroundColor Yellow
    try {
        Set-Location "$PSScriptRoot\..\docker"
        
        # ArrÃªter si dÃ©jÃ  en cours
        docker-compose down 2>$null
        
        # DÃ©marrer
        docker-compose up -d
        
        # Attendre que les services soient prÃªts
        Start-Sleep -Seconds 10
        
        Write-Host "  ✅ Docker Compose dÃ©marrÃ©" -ForegroundColor Green
        Write-Host "     Attente supplÃ©mentaire pour les services..." -ForegroundColor Gray
        
        # VÃ©rifier l'application
        $attempts = 0
        $maxAttempts = 6
        while ($attempts -lt $maxAttempts) {
            try {
                $response = Invoke-WebRequest -Uri "http://localhost:8000/health" -UseBasicParsing -TimeoutSec 5
                if ($response.StatusCode -eq 200) {
                    Write-Host " ✅ Application accessible" -ForegroundColor Green
                    break
                }
            } catch {
                $attempts++
                Write-Host "  ⏳ Tentative $attempts/$maxAttempts..." -ForegroundColor Yellow
                Start-Sleep -Seconds 10
                
                if ($attempts -eq $maxAttempts) {
                    Write-Host "  ⚠️ Application lente Ã  dÃ©marrer" -ForegroundColor Yellow
                }
            }
        }
        
        Set-Location $PSScriptRoot
    } catch {
        Write-Host "  ⚠️  Erreur Docker Compose : $($_.Exception.Message)" -ForegroundColor Yellow
        Set-Location $PSScriptRoot
    }
}

# 4. Kubernetes
if (-not $SkipK8s) {
    Write-Host "[4/5] DÃ©ploiement Kubernetes..." -ForegroundColor Yellow
    try {
        Set-Location "$PSScriptRoot\..\kubernetes\manifests"
        
        # VÃ©rifier que Kubernetes est activÃ©
        $k8sContext = kubectl config current-context 2>$null
        if ($LASTEXITCODE -ne 0) {
            Write-Host " ℹ️ Kubernetes non configuré, passage au mode Docker uniquement" -ForegroundColor Yellow
        } else {
            # Appliquer les manifests
            kubectl apply -f namespace.yaml
            kubectl apply -f configmap.yaml
            kubectl apply -f deployment.yaml
            
            Write-Host " ✅ DÃ©ploiement Kubernetes terminÃ©" -ForegroundColor Green
            
            # Obtenir les infos
            $service = kubectl get svc -n devops-demo devops-app-service -o json | ConvertFrom-Json
            $nodePort = $service.spec.ports[0].nodePort
            Write-Host "     Accès K8s : http://localhost:$nodePort" -ForegroundColor Cyan
        }
        
        Set-Location $PSScriptRoot
    } catch {
        Write-Host "  ⚠️  Erreur Kubernetes : $($_.Exception.Message)" -ForegroundColor Yellow
        Set-Location $PSScriptRoot
    }
}

Write-Host "[5/5] RÃ©capitulatif..." -ForegroundColor Yellow
Write-Host ""
Write-Host "========================================" -ForegroundColor Green
Write-Host "           SETUP TERMINÃ‰ !" -ForegroundColor Green
Write-Host "========================================" -ForegroundColor Green
Write-Host ""
Write-Host "🌐 ACCèS AUX SERVICES :" -ForegroundColor White
Write-Host ""
Write-Host "  📊 Application FastAPI :" -ForegroundColor Cyan
Write-Host "     â€¢ Interface : http://localhost:8000" -ForegroundColor Gray
Write-Host "     â€¢ Health : http://localhost:8000/health" -ForegroundColor Gray
Write-Host "     â€¢ Docs : http://localhost:8000/docs" -ForegroundColor Gray
Write-Host "     â€¢ MÃ©triques : http://localhost:8000/metrics" -ForegroundColor Gray
Write-Host ""
Write-Host "  📈 Monitoring :" -ForegroundColor Cyan
Write-Host "     â€¢ Prometheus : http://localhost:9090" -ForegroundColor Gray
Write-Host "     â€¢ Grafana : http://localhost:3000" -ForegroundColor Gray
Write-Host "        Login : admin / admin123" -ForegroundColor DarkGray
Write-Host ""
Write-Host "  🐳 Gestion Docker :" -ForegroundColor Cyan
Write-Host "     â€¢ Portainer : http://localhost:9000" -ForegroundColor Gray
Write-Host ""
if (-not $SkipK8s) {
    Write-Host "  ☸️  Kubernetes :" -ForegroundColor Cyan
    Write-Host "     â€¢ VÃ©rifier : kubectl get pods -n devops-demo" -ForegroundColor Gray
    Write-Host "     â€¢ Logs : kubectl logs -f -n devops-demo -l app=devops-app" -ForegroundColor Gray
}
Write-Host ""
Write-Host "🔧 COMMANDES UTILES :" -ForegroundColor White
Write-Host "   * Voir les logs : docker-compose logs -f [app|prometheus|grafana]" -ForegroundColor DarkGray
Write-Host "   * ArrÃªter tout : docker-compose down" -ForegroundColor DarkGray
Write-Host "   * Nettoyer : docker system prune -a -f --volumes" -ForegroundColor DarkGray
Write-Host ""
Write-Host "💡 CONSEIL :" -ForegroundColor Yellow
Write-Host "   Testez avec : curl http://localhost:8000 ou dans PowerShell :" -ForegroundColor Gray
Write-Host "   Invoke-WebRequest -Uri 'http://localhost:8000' -UseBasicParsing" -ForegroundColor DarkGray
Write-Host ""

# Test automatique
try {
    $test = Invoke-WebRequest -Uri "http://localhost:8000/health" -UseBasicParsing -TimeoutSec 5
    if ($test.StatusCode -eq 200) {
        Write-Host "âœ… Test de santÃ© rÃ©ussi !" -ForegroundColor Green
    }
} catch {
    Write-Host "⚠️  L'application n'est pas encore accessible, patientez..." -ForegroundColor Yellow
}
#10.2 Script : scripts\cleanup.ps1 (COMPLET)

# Nettoyage complet DevOps Stack
Write-Host "🧹 NETTOYAGE DEVOPS STACK" -ForegroundColor Yellow
Write-Host "==========================" -ForegroundColor Yellow
Write-Host ""

# 1. Arrêter Docker Compose
Write-Host "[1/4] Arrêt Docker Compose..." -ForegroundColor Yellow
try {
    if (Test-Path "..\docker\docker-compose.yml") {
        Set-Location "..\docker"
        docker-compose down -v  # -v pour supprimer les volumes
        Write-Host "  ✅ Docker Compose arrêté" -ForegroundColor Green
        Set-Location $PSScriptRoot
    }
} catch {
    Write-Host "  ⚠️  Aucun service Docker Compose en cours" -ForegroundColor Gray
}

# 2. Nettoyer Kubernetes
Write-Host "[2/4] Nettoyage Kubernetes..." -ForegroundColor Yellow
try {
    # Supprimer le namespace (supprime tout à l'intérieur)
    kubectl delete namespace devops-demo --ignore-not-found=true
    kubectl delete namespace terraform-ns --ignore-not-found=true
    
    # Désinstaller Helm
    helm uninstall devops-release --namespace devops-demo --ignore-not-found=true
    
    Write-Host "  ✅ Kubernetes nettoyé" -ForegroundColor Green
} catch {
    Write-Host "  ⚠️  Kubernetes non configuré ou déjà nettoyé" -ForegroundColor Gray
}

# 3. Nettoyer Docker
Write-Host "[3/4] Nettoyage Docker..." -ForegroundColor Yellow
try {
    # Arrêter tous les conteneurs
    docker stop $(docker ps -aq) 2>$null
    
    # Supprimer tous les conteneurs
    docker rm $(docker ps -aq) 2>$null
    
    # Supprimer toutes les images
    docker rmi $(docker images -q) -f 2>$null
    
    # Nettoyer le système
    docker system prune -a -f --volumes
    
    Write-Host "  ✅ Docker nettoyé" -ForegroundColor Green
} catch {
    Write-Host "  ℹ️  Aucune ressource Docker à nettoyer" -ForegroundColor Gray
}

# 4. Nettoyer Terraform
Write-Host "[4/4] Nettoyage Terraform..." -ForegroundColor Yellow
try {
    if (Test-Path "..\terraform") {
        Set-Location "..\terraform"
        
        # Détruire l'infrastructure si terraform.tfstate existe
        if (Test-Path "terraform.tfstate") {
            terraform destroy -auto-approve 2>$null
        }
        
        # Supprimer les fichiers temporaires
        Remove-Item -Path "*.tfstate*", ".terraform*", "terraform.tfstate.backup" -Force -ErrorAction SilentlyContinue
        
        Write-Host "  ✅ Terraform nettoyé" -ForegroundColor Green
        Set-Location $PSScriptRoot
    }
} catch {
    Write-Host "  ℹ️  Terraform non configuré" -ForegroundColor Gray
}

Write-Host ""
Write-Host "========================================" -ForegroundColor Green
Write-Host "        NETTOYAGE TERMINÉ !" -ForegroundColor Green
Write-Host "========================================" -ForegroundColor Green
Write-Host ""
Write-Host "✅ Toutes les ressources ont été nettoyées" -ForegroundColor Green
Write-Host ""
Write-Host "Pour recommencer :" -ForegroundColor Cyan
Write-Host "  .\setup.ps1" -ForegroundColor Gray


#🎯 VALIDATION FINALE

#Testez votre installation :

# 1. Lancer le setup
cd C:\DevOpsProject\scripts
.\setup.ps1

# 2. Tester chaque service
# Application
curl http://localhost:8000
# ou
Invoke-WebRequest -Uri "http://localhost:8000" -UseBasicParsing | Select-Object -ExpandProperty Content

# Health check
curl http://localhost:8000/health

# Métriques Prometheus
curl http://localhost:8000/metrics

# 3. Vérifier Docker
docker ps
docker-compose ps

# 4. Vérifier Kubernetes
kubectl get pods --all-namespaces
kubectl get svc -n devops-demo

# 5. Accéder aux interfaces web
# Ouvrez votre navigateur et allez sur :
# - http://localhost:8000 (App)
# - http://localhost:8000/docs (Documentation API)
# - http://localhost:9090 (Prometheus)
# - http://localhost:3000 (Grafana - admin/admin123)
# - http://localhost:9000 (Portainer)

#Problèmes courants et solutions :
Problème
Solution
WSL2 ne démarre pas
wsl --shutdown puis redémarrer Docker Desktop
Docker Desktop ne démarre pas
Vérifier Hyper-V dans "Fonctionnalités Windows"
Port déjà utilisé
`netstat -ano
kubectl non reconnu
Réinstaller : choco install kubernetes-cli -y
Prometheus ne scrape pas
Vérifier docker\prometheus.yml et les labels
Application inaccessible
Vérifier les logs : docker-compose logs app

Votre stack DevOps Windows est maintenant fonctionnelle et prête à l'emploi ! 🎉





# Commentaire de Oualim
# Avant d'aller plus loin, les #6, #7 et #8 n'étant pas encore abordée à cette étape, en lançant uniquement les commandes de le #3 on se retrouvera avec des containeurs orphelins :  

PS C:\DevOpsProject\docker> docker-compose ps
"....Found orphan containers ([gitea logstash kibana filebeat elasticsearch devops-db])...." 




# 6. Pour aller plus loin :

- Ajouter une base de données (PostgreSQL)


- Implémenter un vrai pipeline CI/CD avec déploiement automatique


- Ajouter du logging centralisé (ELK Stack)


- Configurer des alertes Prometheus



# Commentaire de Oualim 
# 7. Basculer sur kubernets (découverte nodes & pods)

- Mettre en place du GitOps avec ArgoCD

- Configurer HTTPS avec un reverse proxy



# Commentaire de Oualim
# 8. Simuler une prod qu'on pourrait relancer en 5mn from scratch après un reset du cluster k8' ou une réinstall de docker desktop
- Créer des scripts regroupant tous les démarrages pour :
- les dockers (Deploy.ctnrs.ps1) 
- les pods (deploy.Pods.ps1)
- créer le dashbord et générer le kubeconfig (dashboard.kubeconfig) pour s'y connecter (GnrkbCfOpnDash.ps1)
- créer argo et lancer la page web de connexion avec id et mdp (start-argocd.ps1)











