commit dc9da64778f645ea3e4347ee9348acb9de268c47 Author: David Date: Sun Aug 3 02:44:38 2025 +0200 first commit diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..9b8a937 Binary files /dev/null and b/.DS_Store differ diff --git a/k8s/backend.yaml b/k8s/backend.yaml new file mode 100644 index 0000000..6ef866b --- /dev/null +++ b/k8s/backend.yaml @@ -0,0 +1,145 @@ +apiVersion: v1 +kind: Secret +metadata: + name: backend-secret + namespace: xpeditis +type: Opaque +data: + # Base64 encoded JWT secret + JWT_SECRET: eHBlZGl0aXMtcHJvZC1zZWNyZXQta2V5LWNoYW5nZS1pbi1wcm9kdWN0aW9u + +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: backend-config + namespace: xpeditis +data: + SPRING_PROFILES_ACTIVE: "prod" + SPRING_DATASOURCE_URL: "jdbc:postgresql://postgres-service:5432/xpeditis_prod" + CORS_ALLOWED_ORIGINS: "https://xpeditis.fr,https://www.xpeditis.fr" + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: backend + namespace: xpeditis + labels: + app: backend +spec: + replicas: 2 + selector: + matchLabels: + app: backend + template: + metadata: + labels: + app: backend + spec: + containers: + - name: backend + image: ghcr.io/your-username/xpeditis/backend:latest + ports: + - containerPort: 8080 + env: + - name: SPRING_PROFILES_ACTIVE + valueFrom: + configMapKeyRef: + name: backend-config + key: SPRING_PROFILES_ACTIVE + - name: SPRING_DATASOURCE_URL + valueFrom: + configMapKeyRef: + name: backend-config + key: SPRING_DATASOURCE_URL + - name: SPRING_DATASOURCE_USERNAME + valueFrom: + secretKeyRef: + name: postgres-secret + key: POSTGRES_USER + - name: SPRING_DATASOURCE_PASSWORD + valueFrom: + secretKeyRef: + name: postgres-secret + key: POSTGRES_PASSWORD + - name: JWT_SECRET + valueFrom: + secretKeyRef: + name: backend-secret + key: JWT_SECRET + - name: CORS_ALLOWED_ORIGINS + valueFrom: + configMapKeyRef: + name: backend-config + key: CORS_ALLOWED_ORIGINS + resources: + requests: + memory: "512Mi" + cpu: "250m" + limits: + memory: "1Gi" + cpu: "500m" + livenessProbe: + httpGet: + path: /api/actuator/health + port: 8080 + initialDelaySeconds: 60 + periodSeconds: 30 + timeoutSeconds: 10 + readinessProbe: + httpGet: + path: /api/actuator/health/readiness + port: 8080 + initialDelaySeconds: 30 + periodSeconds: 10 + timeoutSeconds: 5 + volumeMounts: + - name: logs-volume + mountPath: /app/logs + volumes: + - name: logs-volume + emptyDir: {} + +--- +apiVersion: v1 +kind: Service +metadata: + name: backend-service + namespace: xpeditis + labels: + app: backend +spec: + selector: + app: backend + ports: + - port: 8080 + targetPort: 8080 + type: ClusterIP + +--- +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: backend-hpa + namespace: xpeditis +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: backend + minReplicas: 2 + maxReplicas: 10 + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: 70 + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: 80 diff --git a/k8s/frontend.yaml b/k8s/frontend.yaml new file mode 100644 index 0000000..03f07c5 --- /dev/null +++ b/k8s/frontend.yaml @@ -0,0 +1,78 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: frontend + namespace: xpeditis + labels: + app: frontend +spec: + replicas: 2 + selector: + matchLabels: + app: frontend + template: + metadata: + labels: + app: frontend + spec: + containers: + - name: frontend + image: ghcr.io/your-username/xpeditis/frontend:latest + ports: + - containerPort: 80 + resources: + requests: + memory: "128Mi" + cpu: "100m" + limits: + memory: "256Mi" + cpu: "200m" + livenessProbe: + httpGet: + path: / + port: 80 + initialDelaySeconds: 30 + periodSeconds: 30 + readinessProbe: + httpGet: + path: / + port: 80 + initialDelaySeconds: 5 + periodSeconds: 10 + +--- +apiVersion: v1 +kind: Service +metadata: + name: frontend-service + namespace: xpeditis + labels: + app: frontend +spec: + selector: + app: frontend + ports: + - port: 80 + targetPort: 80 + type: ClusterIP + +--- +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: frontend-hpa + namespace: xpeditis +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: frontend + minReplicas: 2 + maxReplicas: 5 + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: 70 diff --git a/k8s/ingress.yaml b/k8s/ingress.yaml new file mode 100644 index 0000000..dc2cb47 --- /dev/null +++ b/k8s/ingress.yaml @@ -0,0 +1,73 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: xpeditis-ingress + namespace: xpeditis + annotations: + kubernetes.io/ingress.class: "nginx" + cert-manager.io/cluster-issuer: "letsencrypt-prod" + nginx.ingress.kubernetes.io/ssl-redirect: "true" + nginx.ingress.kubernetes.io/force-ssl-redirect: "true" + nginx.ingress.kubernetes.io/proxy-body-size: "50m" + nginx.ingress.kubernetes.io/rate-limit: "100" + nginx.ingress.kubernetes.io/rate-limit-window: "1m" +spec: + tls: + - hosts: + - xpeditis.fr + - www.xpeditis.fr + - api.xpeditis.fr + secretName: xpeditis-tls + rules: + # Frontend principal + - host: xpeditis.fr + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: frontend-service + port: + number: 80 + + # Redirection www vers domaine principal + - host: www.xpeditis.fr + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: frontend-service + port: + number: 80 + + # API Backend + - host: api.xpeditis.fr + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: backend-service + port: + number: 8080 + +--- +# Certificat SSL automatique avec cert-manager +apiVersion: cert-manager.io/v1 +kind: ClusterIssuer +metadata: + name: letsencrypt-prod +spec: + acme: + server: https://acme-v02.api.letsencrypt.org/directory + email: admin@xpeditis.fr + privateKeySecretRef: + name: letsencrypt-prod + solvers: + - http01: + ingress: + class: nginx diff --git a/k8s/namespace.yaml b/k8s/namespace.yaml new file mode 100644 index 0000000..2148272 --- /dev/null +++ b/k8s/namespace.yaml @@ -0,0 +1,7 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: xpeditis + labels: + name: xpeditis + environment: production diff --git a/k8s/postgres.yaml b/k8s/postgres.yaml new file mode 100644 index 0000000..2f44ea4 --- /dev/null +++ b/k8s/postgres.yaml @@ -0,0 +1,115 @@ +apiVersion: v1 +kind: Secret +metadata: + name: postgres-secret + namespace: xpeditis +type: Opaque +data: + # Base64 encoded values + POSTGRES_DB: eHBlZGl0aXNfcHJvZA== # xpeditis_prod + POSTGRES_USER: eHBlZGl0aXNfdXNlcg== # xpeditis_user + POSTGRES_PASSWORD: eHBlZGl0aXNfcGFzc3dvcmQ= # xpeditis_password + +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: postgres-pvc + namespace: xpeditis +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 10Gi + storageClassName: standard + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: postgres + namespace: xpeditis + labels: + app: postgres +spec: + replicas: 1 + selector: + matchLabels: + app: postgres + template: + metadata: + labels: + app: postgres + spec: + containers: + - name: postgres + image: postgres:15-alpine + ports: + - containerPort: 5432 + env: + - name: POSTGRES_DB + valueFrom: + secretKeyRef: + name: postgres-secret + key: POSTGRES_DB + - name: POSTGRES_USER + valueFrom: + secretKeyRef: + name: postgres-secret + key: POSTGRES_USER + - name: POSTGRES_PASSWORD + valueFrom: + secretKeyRef: + name: postgres-secret + key: POSTGRES_PASSWORD + volumeMounts: + - name: postgres-storage + mountPath: /var/lib/postgresql/data + resources: + requests: + memory: "256Mi" + cpu: "250m" + limits: + memory: "512Mi" + cpu: "500m" + livenessProbe: + exec: + command: + - pg_isready + - -U + - xpeditis_user + - -d + - xpeditis_prod + initialDelaySeconds: 30 + periodSeconds: 10 + readinessProbe: + exec: + command: + - pg_isready + - -U + - xpeditis_user + - -d + - xpeditis_prod + initialDelaySeconds: 5 + periodSeconds: 5 + volumes: + - name: postgres-storage + persistentVolumeClaim: + claimName: postgres-pvc + +--- +apiVersion: v1 +kind: Service +metadata: + name: postgres-service + namespace: xpeditis + labels: + app: postgres +spec: + selector: + app: postgres + ports: + - port: 5432 + targetPort: 5432 + type: ClusterIP diff --git a/scripts/deploy.sh b/scripts/deploy.sh new file mode 100755 index 0000000..708827b --- /dev/null +++ b/scripts/deploy.sh @@ -0,0 +1,242 @@ +#!/bin/bash + +# Script de déploiement Xpeditis +# Usage: ./deploy.sh [environment] [component] +# Environments: dev, staging, prod +# Components: all, backend, frontend, database + +set -e + +ENVIRONMENT=${1:-dev} +COMPONENT=${2:-all} +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(dirname "$SCRIPT_DIR")" + +# Couleurs pour les logs +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +log_info() { + echo -e "${BLUE}[INFO]${NC} $1" +} + +log_success() { + echo -e "${GREEN}[SUCCESS]${NC} $1" +} + +log_warning() { + echo -e "${YELLOW}[WARNING]${NC} $1" +} + +log_error() { + echo -e "${RED}[ERROR]${NC} $1" +} + +# Vérifier les prérequis +check_prerequisites() { + log_info "Vérification des prérequis..." + + if ! command -v docker &> /dev/null; then + log_error "Docker n'est pas installé" + exit 1 + fi + + if ! command -v docker-compose &> /dev/null; then + log_error "Docker Compose n'est pas installé" + exit 1 + fi + + if [[ "$ENVIRONMENT" == "prod" ]] && ! command -v kubectl &> /dev/null; then + log_error "kubectl n'est pas installé (requis pour la production)" + exit 1 + fi + + log_success "Prérequis vérifiés" +} + +# Déploiement en développement +deploy_dev() { + log_info "Déploiement en environnement de développement..." + + cd "$PROJECT_ROOT" + + case $COMPONENT in + "all") + docker-compose down + docker-compose up --build -d + ;; + "backend") + docker-compose up --build -d backend + ;; + "frontend") + docker-compose up --build -d frontend + ;; + "database") + docker-compose up -d postgres + ;; + *) + log_error "Composant inconnu: $COMPONENT" + exit 1 + ;; + esac + + log_success "Déploiement dev terminé" + log_info "Frontend: http://localhost:3000" + log_info "Backend: http://localhost:8080" + log_info "Base de données: localhost:5432" +} + +# Déploiement en staging +deploy_staging() { + log_info "Déploiement en environnement de staging..." + + # Build et push des images + build_and_push_images "staging" + + # Déploiement Kubernetes + kubectl apply -f "$PROJECT_ROOT/k8s/namespace.yaml" + kubectl apply -f "$PROJECT_ROOT/k8s/" -n xpeditis-staging + + log_success "Déploiement staging terminé" +} + +# Déploiement en production +deploy_prod() { + log_info "Déploiement en environnement de production..." + + # Confirmation utilisateur + read -p "Êtes-vous sûr de vouloir déployer en production? (y/N): " -n 1 -r + echo + if [[ ! $REPLY =~ ^[Yy]$ ]]; then + log_info "Déploiement annulé" + exit 0 + fi + + # Build et push des images + build_and_push_images "prod" + + # Déploiement Kubernetes + kubectl apply -f "$PROJECT_ROOT/k8s/namespace.yaml" + kubectl apply -f "$PROJECT_ROOT/k8s/" -n xpeditis + + # Attendre que les pods soient prêts + kubectl wait --for=condition=ready pod -l app=backend -n xpeditis --timeout=300s + kubectl wait --for=condition=ready pod -l app=frontend -n xpeditis --timeout=300s + + log_success "Déploiement production terminé" + log_info "Site web: https://xpeditis.fr" + log_info "API: https://api.xpeditis.fr" +} + +# Build et push des images Docker +build_and_push_images() { + local env=$1 + log_info "Build et push des images Docker pour $env..." + + # Backend + if [[ "$COMPONENT" == "all" || "$COMPONENT" == "backend" ]]; then + log_info "Build de l'image backend..." + docker build -t "xpeditis/backend:$env" "$PROJECT_ROOT/backend" + docker tag "xpeditis/backend:$env" "ghcr.io/your-username/xpeditis/backend:$env" + docker push "ghcr.io/your-username/xpeditis/backend:$env" + fi + + # Frontend + if [[ "$COMPONENT" == "all" || "$COMPONENT" == "frontend" ]]; then + log_info "Build de l'image frontend..." + docker build -t "xpeditis/frontend:$env" \ + --build-arg VITE_API_URL="https://api.xpeditis.fr" \ + --target production \ + "$PROJECT_ROOT/frontend" + docker tag "xpeditis/frontend:$env" "ghcr.io/your-username/xpeditis/frontend:$env" + docker push "ghcr.io/your-username/xpeditis/frontend:$env" + fi + + log_success "Images Docker buildées et pushées" +} + +# Fonction de rollback +rollback() { + log_warning "Rollback en cours..." + + if [[ "$ENVIRONMENT" == "dev" ]]; then + docker-compose down + docker-compose up -d + else + kubectl rollout undo deployment/backend -n xpeditis + kubectl rollout undo deployment/frontend -n xpeditis + fi + + log_success "Rollback terminé" +} + +# Afficher l'aide +show_help() { + echo "Usage: $0 [environment] [component]" + echo "" + echo "Environments:" + echo " dev - Environnement de développement (défaut)" + echo " staging - Environnement de staging" + echo " prod - Environnement de production" + echo "" + echo "Components:" + echo " all - Tous les composants (défaut)" + echo " backend - Backend Spring Boot uniquement" + echo " frontend - Frontend React uniquement" + echo " database - Base de données uniquement" + echo "" + echo "Options:" + echo " --rollback - Effectuer un rollback" + echo " --help - Afficher cette aide" + echo "" + echo "Exemples:" + echo " $0 # Déploiement dev complet" + echo " $0 prod all # Déploiement production complet" + echo " $0 staging backend # Déploiement staging backend uniquement" + echo " $0 --rollback # Rollback" +} + +# Point d'entrée principal +main() { + case "${1:-}" in + "--help"|"-h") + show_help + exit 0 + ;; + "--rollback") + rollback + exit 0 + ;; + esac + + log_info "Démarrage du déploiement Xpeditis" + log_info "Environnement: $ENVIRONMENT" + log_info "Composant: $COMPONENT" + + check_prerequisites + + case $ENVIRONMENT in + "dev") + deploy_dev + ;; + "staging") + deploy_staging + ;; + "prod") + deploy_prod + ;; + *) + log_error "Environnement inconnu: $ENVIRONMENT" + show_help + exit 1 + ;; + esac + + log_success "Déploiement terminé avec succès!" +} + +# Exécuter le script principal +main "$@"