DevOpsFacile
AccueilParcours de formationCertificationsModulesCheat SheetÀ Propos
DevOpsFacile

Une plateforme d'apprentissage complète pour maîtriser les pratiques DevOps modernes, du débutant à l'expert.

contact@devopsfacile.fr

Formation

  • Parcours de formation
  • Modules

Informations

  • À Propos
  • Conditions d'utilisation
  • Confidentialité
  • Mentions légales

© 2026 DevOps Facile. Tous droits réservés.

Fait avec pour la communauté DevOps

ModulesKubernetes Intermédiaire : Deployments, Services et Volumes07 - Capstone : déployer une application multi-tiers

Détails

  • 60 minutes
  • Avancé

Objectifs

  • Assembler tous les concepts intermédiaires en un déploiement complet
  • Déployer une application multi-tiers (frontend + API + base de données)
  • Configurer la communication entre services via les DNS Kubernetes
  • Mettre en place persistence, configuration et health checks
Module Kubernetes Intermédiaire : Deployments, Services et Volumes

Exercice 07 : Capstone - Déployer une application multi-tiers

🎯 Objectifs

À la fin de cet exercice, vous serez capable de :

  • ✅ Déployer une architecture 3 tiers complète : frontend (Nginx) + API (Node.js) + base de données (PostgreSQL)
  • ✅ Utiliser des ConfigMaps pour configurer les applications
  • ✅ Utiliser des PVC pour persister les données PostgreSQL
  • ✅ Connecter les tiers via des Services Kubernetes
  • ✅ Vérifier que l'application est fonctionnelle et résiliente

Durée estimée : 60 minutes

Difficulté : ⭐⭐⭐☆☆ (Avancé)

Prérequis : Exercices 01 à 05 complétés


📖 Contexte

Jusqu'ici, vous avez travaillé sur chaque concept séparément. Dans cet exercice, vous allez les combiner pour déployer une vraie stack applicative comme on le fait en production. Chaque composant sera isolé dans son propre Deployment et communiquera avec les autres via des Services.

Architecture cible :

[Browser] → [NodePort 30090] → [Nginx Frontend] → [ClusterIP] → [API Node.js] → [ClusterIP] → [PostgreSQL + PVC]

📋 Énoncé

Déployez les 3 composants de l'application, configurez leur communication et vérifiez l'ensemble.


🧭 Déroulement de l'exercice

Tâche 1 : Préparer le namespace et la base de données

bash
# Créer un namespace dédié au capstone
kubectl create namespace capstone

# PVC pour PostgreSQL
cat > 01-postgres-pvc.yaml << 'EOF'
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: postgres-pvc
  namespace: capstone
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: standard
  resources:
    requests:
      storage: 2Gi
EOF

# ConfigMap pour PostgreSQL
cat > 02-postgres-config.yaml << 'EOF'
apiVersion: v1
kind: ConfigMap
metadata:
  name: postgres-config
  namespace: capstone
data:
  POSTGRES_DB: "appdb"
  POSTGRES_USER: "appuser"
  POSTGRES_PASSWORD: "apppassword123"
  PGDATA: "/var/lib/postgresql/data/pgdata"
EOF

# Deployment PostgreSQL
cat > 03-postgres-deployment.yaml << 'EOF'
apiVersion: apps/v1
kind: Deployment
metadata:
  name: postgres
  namespace: capstone
spec:
  replicas: 1
  selector:
    matchLabels:
      app: postgres
  template:
    metadata:
      labels:
        app: postgres
    spec:
      volumes:
        - name: postgres-storage
          persistentVolumeClaim:
            claimName: postgres-pvc
      containers:
        - name: postgres
          image: postgres:16-alpine
          envFrom:
            - configMapRef:
                name: postgres-config
          ports:
            - containerPort: 5432
          volumeMounts:
            - name: postgres-storage
              mountPath: /var/lib/postgresql/data
          livenessProbe:
            exec:
              command: ["pg_isready", "-U", "appuser", "-d", "appdb"]
            initialDelaySeconds: 30
            periodSeconds: 10
          resources:
            requests:
              cpu: 100m
              memory: 256Mi
            limits:
              cpu: 500m
              memory: 512Mi
EOF

# Service PostgreSQL (ClusterIP - interne seulement)
cat > 04-postgres-service.yaml << 'EOF'
apiVersion: v1
kind: Service
metadata:
  name: postgres
  namespace: capstone
spec:
  type: ClusterIP
  selector:
    app: postgres
  ports:
    - port: 5432
      targetPort: 5432
EOF

kubectl apply -f 01-postgres-pvc.yaml
kubectl apply -f 02-postgres-config.yaml
kubectl apply -f 03-postgres-deployment.yaml
kubectl apply -f 04-postgres-service.yaml

# Attendre que PostgreSQL soit prêt
kubectl rollout status deployment/postgres -n capstone

Tâche 2 : Déployer l'API

Nous simulons une API simple avec un conteneur qui expose un endpoint de health check.

bash
# ConfigMap pour l'API
cat > 05-api-config.yaml << 'EOF'
apiVersion: v1
kind: ConfigMap
metadata:
  name: api-config
  namespace: capstone
data:
  NODE_ENV: "production"
  PORT: "3000"
  LOG_LEVEL: "info"
  # L'URL de la base utilise le DNS Kubernetes : <service>.<namespace>.svc.cluster.local
  DATABASE_URL: "postgresql://appuser:apppassword123@postgres.capstone.svc.cluster.local:5432/appdb"
EOF

# Deployment API (on simule avec nginx qui répond en JSON)
cat > 06-api-deployment.yaml << 'EOF'
apiVersion: apps/v1
kind: Deployment
metadata:
  name: api
  namespace: capstone
spec:
  replicas: 2
  selector:
    matchLabels:
      app: api
  template:
    metadata:
      labels:
        app: api
        tier: backend
    spec:
      containers:
        - name: api
          image: nginx:1.27-alpine
          ports:
            - containerPort: 80
          envFrom:
            - configMapRef:
                name: api-config
          livenessProbe:
            httpGet:
              path: /
              port: 80
            initialDelaySeconds: 10
            periodSeconds: 15
          readinessProbe:
            httpGet:
              path: /
              port: 80
            initialDelaySeconds: 5
            periodSeconds: 5
          resources:
            requests:
              cpu: 50m
              memory: 64Mi
            limits:
              cpu: 200m
              memory: 128Mi
EOF

# Service API (ClusterIP - accessible depuis le frontend seulement)
cat > 07-api-service.yaml << 'EOF'
apiVersion: v1
kind: Service
metadata:
  name: api
  namespace: capstone
spec:
  type: ClusterIP
  selector:
    app: api
  ports:
    - port: 3000
      targetPort: 80
EOF

kubectl apply -f 05-api-config.yaml
kubectl apply -f 06-api-deployment.yaml
kubectl apply -f 07-api-service.yaml

kubectl rollout status deployment/api -n capstone

Tâche 3 : Déployer le frontend

bash
# ConfigMap avec la config Nginx du frontend (proxy vers l'API)
cat > 08-frontend-config.yaml << 'EOF'
apiVersion: v1
kind: ConfigMap
metadata:
  name: frontend-nginx-conf
  namespace: capstone
data:
  default.conf: |
    server {
        listen 80;

        location / {
            root /usr/share/nginx/html;
            index index.html;
        }

        location /api/ {
            proxy_pass http://api.capstone.svc.cluster.local:3000/;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
        }

        location /health {
            return 200 '{"status":"ok","service":"frontend"}';
            add_header Content-Type application/json;
        }
    }
EOF

# Deployment Frontend
cat > 09-frontend-deployment.yaml << 'EOF'
apiVersion: apps/v1
kind: Deployment
metadata:
  name: frontend
  namespace: capstone
spec:
  replicas: 2
  selector:
    matchLabels:
      app: frontend
  template:
    metadata:
      labels:
        app: frontend
        tier: frontend
    spec:
      volumes:
        - name: nginx-config
          configMap:
            name: frontend-nginx-conf
            items:
              - key: default.conf
                path: default.conf
      containers:
        - name: frontend
          image: nginx:1.27-alpine
          ports:
            - containerPort: 80
          volumeMounts:
            - name: nginx-config
              mountPath: /etc/nginx/conf.d/
              readOnly: true
          livenessProbe:
            httpGet:
              path: /health
              port: 80
            initialDelaySeconds: 5
            periodSeconds: 10
          readinessProbe:
            httpGet:
              path: /health
              port: 80
            initialDelaySeconds: 3
            periodSeconds: 5
          resources:
            requests:
              cpu: 50m
              memory: 64Mi
            limits:
              cpu: 200m
              memory: 128Mi
EOF

# Service Frontend (NodePort - accessible depuis l'hôte)
cat > 10-frontend-service.yaml << 'EOF'
apiVersion: v1
kind: Service
metadata:
  name: frontend
  namespace: capstone
spec:
  type: NodePort
  selector:
    app: frontend
  ports:
    - port: 80
      targetPort: 80
      nodePort: 30090
EOF

kubectl apply -f 08-frontend-config.yaml
kubectl apply -f 09-frontend-deployment.yaml
kubectl apply -f 10-frontend-service.yaml

kubectl rollout status deployment/frontend -n capstone

Tâche 4 : Valider la stack complète

bash
# Vue d'ensemble
kubectl get all -n capstone

# Tous les pods doivent être Running
kubectl get pods -n capstone

# Tester le frontend depuis l'hôte
MINIKUBE_IP=$(minikube ip)
curl http://$MINIKUBE_IP:30090/health
# Attendu : {"status":"ok","service":"frontend"}

# Tester la communication frontend → API via proxy
curl http://$MINIKUBE_IP:30090/api/

# Vérifier que PostgreSQL est accessible depuis l'API
API_POD=$(kubectl get pods -n capstone -l app=api -o jsonpath='{.items[0].metadata.name}')
kubectl exec "$API_POD" -n capstone -- \
  wget -qO- http://postgres.capstone.svc.cluster.local:5432 2>&1 || \
  echo "PostgreSQL répond (connexion TCP OK)"

# Tester la résilience : supprimer un pod frontend
FRONTEND_POD=$(kubectl get pods -n capstone -l app=frontend -o jsonpath='{.items[0].metadata.name}')
kubectl delete pod "$FRONTEND_POD" -n capstone
# Le Deployment recrée immédiatement un nouveau pod
kubectl get pods -n capstone -w

Tâche 5 : Observer les health checks

bash
# Les livenessProbe et readinessProbe sont visibles dans describe
kubectl describe deployment/frontend -n capstone | grep -A 10 "Liveness\|Readiness"

# Voir les events des health checks
kubectl get events -n capstone --sort-by='.lastTimestamp' | tail -20

# Scaler l'API à 3 réplicas sans downtime
kubectl scale deployment/api --replicas=3 -n capstone
kubectl rollout status deployment/api -n capstone

# Mettre à jour le frontend (simule une nouvelle version)
kubectl set image deployment/frontend frontend=nginx:1.26-alpine -n capstone
kubectl rollout status deployment/frontend -n capstone

✅ Vérification du résultat

  • kubectl get pods -n capstone affiche 5 pods Running (1 postgres + 2 api + 2 frontend)
  • curl http://$(minikube ip):30090/health retourne {"status":"ok"}
  • kubectl get pvc -n capstone affiche le PVC en état Bound
  • La suppression d'un pod frontend est suivie de sa recréation automatique
  • Le scaling de l'API à 3 réplicas se fait sans downtime

💡 À retenir

Une application multi-tiers dans Kubernetes suit ce schéma standard :

[Trafic externe]
      ↓
[Service NodePort / LoadBalancer] → [Frontend Pods (Deployment x2)]
                                          ↓
                                [Service ClusterIP] → [API Pods (Deployment x2)]
                                                             ↓
                                                [Service ClusterIP] → [DB Pod (Deployment x1 + PVC)]

Les DNS internes éliminent le besoin de coder les IPs en dur :

http://api.capstone.svc.cluster.local:3000
http://postgres.capstone.svc.cluster.local:5432

Les liveness/readiness probes garantissent :

  • Que le trafic n'est envoyé qu'aux pods prêts (readiness)
  • Que les pods bloqués sont redémarrés automatiquement (liveness)

✨ Solution Complète

bash
# Déployer la stack entière en une commande
kubectl apply -f . -n capstone  # Si tous les YAML sont dans le même dossier

# Vérifier
kubectl get all -n capstone
curl http://$(minikube ip):30090/health

# Nettoyer
kubectl delete namespace capstone
Retour au module

Sur cette page

  • 🎯 Objectifs
  • 📖 Contexte
  • 📋 Énoncé
  • 🧭 Déroulement de l'exercice
  • Tâche 1 : Préparer le namespace et la base de données
  • Tâche 2 : Déployer l'API
  • Tâche 3 : Déployer le frontend
  • Tâche 4 : Valider la stack complète
  • Tâche 5 : Observer les health checks
  • ✅ Vérification du résultat
  • 💡 À retenir
  • ✨ Solution Complète