Lab 08 : Challenge Final d'Intégration en Autonomie¶
Objectif :¶
Mettre en œuvre l'ensemble des compétences acquises durant ces 2 journées pour concevoir, déployer et valider en autonomie l'architecture complète, résiliente et sécurisée du Campus Summer Portal de A à Z dans un environnement isolé de production.
📌 Note Compatibilité OS :
L'ensemble du déploiement s'exécute de façon déclarative viakubectl apply -f ...: c'est rigoureusement identique sous Windows (PowerShell), Linux (Ubuntu) et macOS (Terminal).💡 Modalités d'exécution (Au choix) :
Option A (Recommandée - Confort DevOps Local) : Éditez et appliquez vos manifests directement depuis votre machine locale avec votre IDE et votrekubectl. Zéro transfert de fichiers !
Option B (Secours - PC d'entreprise verrouillé) : Connectez-vous en SSH surk8s-control: les répertoires et manifests sont pré-déployés dans~/labs/.
Architecture Cible de Production¶
Vous devez livrer un namespace isolé nommé campus-prod hébergeant la pile complète à trois niveaux (Three-Tier Architecture) :
flowchart TD
User["Navigateur Stagiaire / Client"] -->|Port NodePort 30089| FESvc["Service Frontend<br/>(NodePort 30089)"]
FESvc --> FE1["Frontend Pod 1"]
FESvc --> FE2["Frontend Pod 2"]
FE1 & FE2 -->|HTTP 8080| BESvc["Service Backend<br/>(ClusterIP 8080)"]
BESvc --> BE1["Backend Pod 1<br/>(Probes + Non-Root)"]
BESvc --> BE2["Backend Pod 2<br/>(Probes + Non-Root)"]
BE1 & BE2 -->|TCP 5432| DBSvc["Service Database<br/>(ClusterIP 5432)"]
DBSvc --> DB["PostgreSQL 16<br/>(Disque Persistant 1Gi)"]
subgraph Socle de Gouvernance & Sécurité
CM["ConfigMap: prod-backend-config"] -.-> BE1 & BE2
SEC["Secret: prod-db-credentials"] -.-> BE1 & BE2 & DB
NP["NetworkPolicy: allow-backend-to-db"] === DBSvc
RQ["ResourceQuota: prod-quota"]
end
Cahier des Charges & Déroulé en 4 Jalons¶
Pour réussir ce challenge avancez méthode par méthode en respectant les 4 jalons ci-dessous.
Jalon 1 : Isolation du Namespace & Socle de Configuration¶
Spécifications :¶
- Créez un namespace dédié nommé
campus-prod. - Créez un Secret nommé
prod-db-credentialsdanscampus-prodavec : POSTGRES_DB:campusPOSTGRES_USER:campusPOSTGRES_PASSWORD:ProductionSecretKey2026!- Créez un ConfigMap nommé
prod-backend-configdanscampus-prodavec : SPRING_PROFILES_ACTIVE:productionSERVER_PORT:8080LOGGING_LEVEL_COM_OKDLAB:INFO- Créez un
ResourceQuotanomméprod-quotaplafonnant le namespace à : - Maximum 8 Pods
- Requests CPU :
2/ Requests RAM :2Gi - Limits CPU :
4/ Limits RAM :4Gi - Créez un
LimitRangenomméprod-limitspour fournir les requêtes et limites par défaut exigées par leResourceQuota(default: cpu 200m, memory 256Mi/defaultRequest: cpu 50m, memory 64Mi).
💡 Indice 1 : Solution Jalon 1 (Namespace, ConfigMap, Secret, Quota, LimitRange)
Créez le fichier `01-socle-config.yaml` :apiVersion: v1
kind: Namespace
metadata:
name: campus-prod
---
apiVersion: v1
kind: Secret
metadata:
name: prod-db-credentials
namespace: campus-prod
type: Opaque
stringData:
POSTGRES_DB: "campus"
POSTGRES_USER: "campus"
POSTGRES_PASSWORD: "ProductionSecretKey2026!"
---
apiVersion: v1
kind: ConfigMap
metadata:
name: prod-backend-config
namespace: campus-prod
data:
SPRING_PROFILES_ACTIVE: "production"
SERVER_PORT: "8080"
LOGGING_LEVEL_COM_OKDLAB: "INFO"
---
apiVersion: v1
kind: ResourceQuota
metadata:
name: prod-quota
namespace: campus-prod
spec:
hard:
pods: "8"
requests.cpu: "2"
requests.memory: 2Gi
limits.cpu: "4"
limits.memory: 4Gi
---
apiVersion: v1
kind: LimitRange
metadata:
name: prod-limits
namespace: campus-prod
spec:
limits:
- default:
cpu: 200m
memory: 256Mi
defaultRequest:
cpu: 50m
memory: 64Mi
type: Container
Jalon 2 : Persistance & Base de Données Sécurisée¶
Spécifications :¶
- Créez un PVC nommé
prod-postgres-pvcde1GiavecReadWriteOnce. - Déployez PostgreSQL 16 (
image: postgres:16-alpine,replicas: 1) : - Consommez les variables
POSTGRES_DB,POSTGRES_USER,POSTGRES_PASSWORDdepuis le Secret. - Spécifiez
PGDATAvers/var/lib/postgresql/data/pgdata. - Montez le volume sur
/var/lib/postgresql/data. - Définissez des limites de ressources raisonnables (
requests: cpu 100m, memory 256Mi/limits: cpu 250m, memory 512Mi). - Créez un Service de type
ClusterIPnommépostgresécoutant sur le port5432. - Créez une
NetworkPolicynomméeallow-backend-to-postgresautorisant exclusivement les pods portant le labelapp: campus-backendà atteindre le port TCP5432du podapp: campus-postgres.
💡 Indice 2 : Solution Jalon 2 (PVC, Deployment PostgreSQL, Service, NetworkPolicy)
Créez le fichier `02-database.yaml` :apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: prod-postgres-pvc
namespace: campus-prod
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: campus-postgres
namespace: campus-prod
spec:
replicas: 1
selector:
matchLabels:
app: campus-postgres
template:
metadata:
labels:
app: campus-postgres
spec:
containers:
- name: postgres
image: postgres:16-alpine
ports:
- containerPort: 5432
resources:
requests:
cpu: 100m
memory: 256Mi
limits:
cpu: 250m
memory: 512Mi
env:
- name: POSTGRES_DB
valueFrom:
secretKeyRef:
name: prod-db-credentials
key: POSTGRES_DB
- name: POSTGRES_USER
valueFrom:
secretKeyRef:
name: prod-db-credentials
key: POSTGRES_USER
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: prod-db-credentials
key: POSTGRES_PASSWORD
- name: PGDATA
value: /var/lib/postgresql/data/pgdata
volumeMounts:
- name: db-data
mountPath: /var/lib/postgresql/data
volumes:
- name: db-data
persistentVolumeClaim:
claimName: prod-postgres-pvc
---
apiVersion: v1
kind: Service
metadata:
name: postgres
namespace: campus-prod
spec:
type: ClusterIP
selector:
app: campus-postgres
ports:
- port: 5432
targetPort: 5432
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-backend-to-postgres
namespace: campus-prod
spec:
podSelector:
matchLabels:
app: campus-postgres
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: campus-backend
ports:
- protocol: TCP
port: 5432
Jalon 3 : API Backend Microservices & Sondes de Santé¶
Spécifications :¶
- Créez un déploiement
campus-backendavec2 réplicas(imageregaieg/campus:backend-1.0.0, port conteneur8080). - Injectez la variable
SPRING_DATASOURCE_URLavec la chaîne JDBC :jdbc:postgresql://postgres:5432/campus. - Injectez
SPRING_DATASOURCE_USERNAMEetSPRING_DATASOURCE_PASSWORDdepuis le secretprod-db-credentials. - Chargez les variables non sensibles via
envFrom: configMapRef: prod-backend-config. - Configurez une Liveness Probe et une Readiness Probe sur
/actuator/health(port8080). - Ajoutez le durcissement de sécurité NSA :
runAsNonRoot: true,runAsUser: 10001. - Allouez les ressources :
requests: cpu 150m, memory 256Mi/limits: cpu 400m, memory 512Mi. - Exposez ce backend via un Service
ClusterIPnommécampus-backend-svcsur le port8080. - Créez un Service alias nommé
campus-backend(typeExternalName) pour permettre au reverse proxy NGINX du frontend de résoudre le nom d'hôte amont attenduhttp://campus-backend:8080/api/.
💡 Indice 3 : Solution Jalon 3 (Deployment Backend avec Probes, SecurityContext, Service et Alias)
Créez le fichier `03-backend.yaml` :apiVersion: apps/v1
kind: Deployment
metadata:
name: campus-backend
namespace: campus-prod
spec:
replicas: 2
selector:
matchLabels:
app: campus-backend
template:
metadata:
labels:
app: campus-backend
spec:
securityContext:
runAsNonRoot: true
runAsUser: 10001
runAsGroup: 10001
containers:
- name: backend
image: regaieg/campus:backend-1.0.0
ports:
- containerPort: 8080
resources:
requests:
cpu: 150m
memory: 256Mi
limits:
cpu: 400m
memory: 512Mi
env:
- name: SPRING_DATASOURCE_URL
value: "jdbc:postgresql://postgres:5432/campus"
- name: SPRING_DATASOURCE_USERNAME
valueFrom:
secretKeyRef:
name: prod-db-credentials
key: POSTGRES_USER
- name: SPRING_DATASOURCE_PASSWORD
valueFrom:
secretKeyRef:
name: prod-db-credentials
key: POSTGRES_PASSWORD
envFrom:
- configMapRef:
name: prod-backend-config
livenessProbe:
httpGet:
path: /actuator/health
port: 8080
initialDelaySeconds: 60
periodSeconds: 10
readinessProbe:
httpGet:
path: /actuator/health
port: 8080
initialDelaySeconds: 25
periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
name: campus-backend-svc
namespace: campus-prod
spec:
type: ClusterIP
selector:
app: campus-backend
ports:
- port: 8080
targetPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: campus-backend
namespace: campus-prod
spec:
type: ExternalName
externalName: campus-backend-svc.campus-prod.svc.cluster.local
Jalon 4 : Frontend Web, Exposition NodePort & Validation Automatisée¶
Spécifications :¶
- Créez le déploiement
campus-frontend(2 réplicas, imageregaieg/campus:frontend-1.0.0, port8080). - Définissez les ressources :
requests: cpu 50m, memory 64Mi/limits: cpu 150m, memory 128Mi. - Exposez le frontend avec un Service NodePort nommé
campus-frontend-svcsur le port80,targetPort: 8080etnodePort: 30089.
(Note : le port standard30080est alloué au contrôleur Ingress NGINX et30088au frontend du Lab 04).
💡 Indice 4 : Solution Jalon 4 (Frontend & Service NodePort)
Créez le fichier `04-frontend.yaml` :apiVersion: apps/v1
kind: Deployment
metadata:
name: campus-frontend
namespace: campus-prod
spec:
replicas: 2
selector:
matchLabels:
app: campus-frontend
template:
metadata:
labels:
app: campus-frontend
spec:
containers:
- name: frontend
image: regaieg/campus:frontend-1.0.0
ports:
- containerPort: 8080
resources:
requests:
cpu: 50m
memory: 64Mi
limits:
cpu: 150m
memory: 128Mi
---
apiVersion: v1
kind: Service
metadata:
name: campus-frontend-svc
namespace: campus-prod
spec:
type: NodePort
selector:
app: campus-frontend
ports:
- port: 80
targetPort: 8080
nodePort: 30089
Script d'Auto-Évaluation Automatisée de Conformité¶
Pour valider l'intégralité de votre travail comme lors d'un audit de conformité de production, exécutez ce script de vérification :
cat << 'EOF' > test-challenge.sh
#!/bin/bash
set -e
echo "=== 1. VERIFICATION DU NAMESPACE & QUOTA ==="
kubectl get ns campus-prod
kubectl describe resourcequota prod-quota -n campus-prod
echo -e "\n=== 2. VERIFICATION DU VOLUME PERSISTANT ==="
kubectl get pvc prod-postgres-pvc -n campus-prod | grep "Bound" && echo "-> PVC Bound OK"
echo -e "\n=== 3. ETAT DE SANTE DES PODS (PROBES & REPLICAS) ==="
kubectl get pods -n campus-prod -o wide
echo -e "\n=== 4. TEST DU DNS INTERNE ET DES ENDPOINTS ==="
kubectl get endpoints campus-backend-svc postgres campus-frontend-svc -n campus-prod
echo -e "\n=== 5. VALIDATION DU PROCESSUS NON-ROOT BACKEND ==="
kubectl exec -n campus-prod deployment/campus-backend -- id
echo -e "\n=== 6. TEST DE CONNECTIVITE METIER API ET BASE ==="
kubectl run audit-curl --image=curlimages/curl -n campus-prod --rm -i --restart=Never -- \
curl -s http://campus-backend-svc:8080/actuator/health
echo ""
echo "=== TOUS LES VOYANTS SONT AU VERT ! CHALLENGE REUSSI ==="
EOF
chmod +x test-challenge.sh
./test-challenge.sh
Crash-Test Final en Direct : Démonstration de Résilience¶
- Récupérez l'adresse IP publique de l'un de vos nœuds workers (ex.
k8s-worker-1). - Ouvrez directement votre navigateur web sur le port NodePort exposé :
http://<IP_PUBLIQUE_WORKER>:30089 - Saisissez une nouvelle candidature pour un stage dans le formulaire et cliquez sur Envoyer la candidature :
- Constatez que la candidature s'affiche immédiatement dans la liste et que le compteur s'incrémente en direct.
- Dans votre terminal, simulez une panne totale en supprimant brutalement le pod de base de données :
- Attendez que le contrôleur recrée le pod sainement :
- Rafraîchissez votre navigateur sur
http://<IP_PUBLIQUE_WORKER>:30089: - La candidature est intacte et toujours affichée !
- Le backend a géré la reconnexion à la base de données.
- Le stockage persistant a protégé les données d'entreprise.
Critères d'Évaluation Finale (Checklist)¶
- L'infrastructure complète est déployée dans le namespace isolé
campus-prod. - Le
ResourceQuotaet leLimitRangeencadrent strictement l'empreinte mémoire et CPU. - PostgreSQL tourne avec son volume persistant
Boundde1Gi. - La
NetworkPolicyprotège la base contre toute tentative d'intrusion hors du backend. - Les 2 réplicas backend sont sains (
1/1 Running) et validés par leurslivenessetreadinessprobes. - Le backend fonctionne avec un utilisateur non-root (
UID 10001). - Le frontend est exposé en
NodePort 30089et accessible directement depuis le navigateur. - Le crash-test de suppression du pod de base de données a été exécuté avec succès sans perte de données.
Clôture de la Formation : Libération Complète des Ressources AWS (Teardown)¶
Félicitations ! Vous avez conçu, déployé, sécurisé, supervisé et validé l'intégralité de l'architecture microservices de bout en bout sur Kubernetes.
À l'issue de ces 2 journées de formation, il est impératif de détruire l'ensemble de l'infrastructure cloud afin de libérer 100% des ressources et de ne laisser aucun coût résiduel sur votre compte AWS :
Destruction de l'infrastructure via Terraform¶
Depuis votre machine locale (poste apprenant), placez-vous dans le répertoire Terraform et déclenchez la suppression complète :
Option A : Via la commande Terraform directe¶
-
Sous Windows (PowerShell) :
-
Sous Linux / macOS (Bash) :
Option B : Via les scripts de clôture fournis¶
-
Sous Windows (PowerShell) :
-
Sous Linux / macOS (Bash) :
💡 Que se passe-t-il lors de la destruction ?
Terraform libère l'IP élastique fixe (EIP), résilie les 3 instances EC2, supprime tous les volumes de stockage EBS attachés ainsi que le groupe de sécurité réseau. Votre compte AWS retombe ainsi à une facturation strictement nulle.