# 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) 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` ``` 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É) ``` 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 : 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 : 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 ``` apiVersion: v1 kind: Namespace metadata: name: devops-demo labels: name: devops-demo environment: development ``` ### 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É) ``` 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 partie 6,7 et 8 n'étant pas encore abordée à cette étape, en lançant uniquement les commandes de la partie 4.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)