Chapitre 1
Prérequis et architecture cible
Outils requis
Avant de commencer, assurez-vous d'avoir installé et configuré les outils suivants :
kubectl ≥ 1.28
Docker ≥ 24
Helm ≥ 3.12
k9s (optionnel)
kubeconfig configuré
Architecture cible
Le schéma ci-dessous représente l'architecture que nous allons construire :
# Architecture cible
Internet
│
▼
[ LoadBalancer / Ingress Controller (Nginx) ]
│
├── /api → Service: app-service → Pod(s): app-deployment
├── / → Service: front-service → Pod(s): front-deployment
│
[ PersistentVolumeClaim ] → PostgreSQL StatefulSet
[ ConfigMap + Secret ] → variables d'environnement
[ HPA ] → scaling automatique des pods
[ Prometheus + Grafana ] → monitoring
Initialisation du namespace
# Créer un namespace dédié à votre application kubectl create namespace mon-app # Définir ce namespace par défaut kubectl config set-context --current --namespace=mon-app
Chapitre 2
Conteneurisation avec Docker
Dockerfile optimisé (multi-stage)
Utilisez un build multi-stage pour réduire la taille de l'image finale et éviter d'inclure les outils de build en production.
# ── Stage 1 : build ────────────────────────────── FROM node:20-alpine AS builder WORKDIR /app COPY package*.json ./ RUN npm ci --only=production COPY . . RUN npm run build # ── Stage 2 : production ───────────────────────── FROM node:20-alpine AS production WORKDIR /app ENV NODE_ENV=production COPY --from=builder /app/dist ./dist COPY --from=builder /app/node_modules ./node_modules EXPOSE 3000 USER node CMD ["node", "dist/main.js"]
💡 Bonne pratique : Toujours spécifier
USER node pour ne pas exécuter le container en root.Build et push vers le registry
# Build l'image docker build -t registry.example.com/mon-app:v1.0.0 . # Push vers votre registry (Docker Hub, GHCR, ECR...) docker push registry.example.com/mon-app:v1.0.0
.dockerignore
node_modules .git .env .env.* *.log dist coverage README.md
Chapitre 3
Déploiement Kubernetes
Deployment
apiVersion: apps/v1 kind: Deployment metadata: name: mon-app namespace: mon-app labels: app: mon-app spec: replicas: 3 selector: matchLabels: app: mon-app template: metadata: labels: app: mon-app spec: containers: - name: mon-app image: registry.example.com/mon-app:v1.0.0 ports: - containerPort: 3000 envFrom: - configMapRef: name: mon-app-config - secretRef: name: mon-app-secrets resources: requests: memory: "128Mi" cpu: "100m" limits: memory: "512Mi" cpu: "500m" livenessProbe: httpGet: path: /health port: 3000 initialDelaySeconds: 10 periodSeconds: 15 readinessProbe: httpGet: path: /ready port: 3000 initialDelaySeconds: 5 periodSeconds: 10
Service et Ingress
--- Service --- apiVersion: v1 kind: Service metadata: name: mon-app-service spec: selector: app: mon-app ports: - port: 80 targetPort: 3000 --- --- Ingress (Nginx Controller) --- apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: mon-app-ingress annotations: nginx.ingress.kubernetes.io/rewrite-target: / cert-manager.io/cluster-issuer: "letsencrypt-prod" spec: tls: - hosts: [app.example.com] secretName: mon-app-tls rules: - host: app.example.com http: paths: - path: / pathType: Prefix backend: service: name: mon-app-service port: number: 80
Appliquer les manifests
# Appliquer tous les fichiers YAML kubectl apply -f k8s/ # Vérifier le déploiement kubectl get pods -n mon-app kubectl get ingress -n mon-app kubectl describe deployment mon-app -n mon-app
Chapitre 4
Gestion des secrets et ConfigMaps
ConfigMap (variables non sensibles)
apiVersion: v1 kind: ConfigMap metadata: name: mon-app-config data: APP_ENV: "production" APP_PORT: "3000" DB_HOST: "postgres-service" REDIS_HOST: "redis-service"
Secret (variables sensibles)
⚠️ Ne jamais committer les Secrets en clair dans votre dépôt Git. Utilisez Sealed Secrets ou Vault.
# Créer un secret via kubectl (valeurs en base64 automatique) kubectl create secret generic mon-app-secrets \ --from-literal=DB_PASSWORD='monMotDePasse' \ --from-literal=APP_KEY='base64:xxx...' \ --from-literal=JWT_SECRET='monSecretJWT' \ -n mon-app # Vérifier kubectl get secret mon-app-secrets -o yaml -n mon-app
Sealed Secrets (recommandé en production)
# Installation de Sealed Secrets Controller helm repo add sealed-secrets https://bitnami-labs.github.io/sealed-secrets helm install sealed-secrets sealed-secrets/sealed-secrets -n kube-system # Créer un SealedSecret (chiffré, safe to commit) kubectl create secret generic mon-secret --dry-run=client \ --from-literal=DB_PASSWORD='monMotDePasse' -o yaml | \ kubeseal --format yaml > k8s/sealed-secret.yaml
Chapitre 5
Scaling et haute disponibilité
Horizontal Pod Autoscaler (HPA)
Le HPA ajuste automatiquement le nombre de pods en fonction de la charge CPU/mémoire.
apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: name: mon-app-hpa spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: mon-app 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
PodDisruptionBudget
apiVersion: policy/v1 kind: PodDisruptionBudget metadata: name: mon-app-pdb spec: minAvailable: 2 selector: matchLabels: app: mon-app
💡 Le PDB garantit qu'au moins 2 pods restent disponibles lors d'une mise à jour ou maintenance du cluster.
Chapitre 6
Monitoring avec Prometheus & Grafana
Installation via Helm (kube-prometheus-stack)
# Ajouter le repo helm repo add prometheus-community \ https://prometheus-community.github.io/helm-charts helm repo update # Installer la stack complète (Prometheus + Grafana + AlertManager) helm install kube-prometheus prometheus-community/kube-prometheus-stack \ --namespace monitoring \ --create-namespace \ --set grafana.adminPassword='VotreMotDePasse' \ --set prometheus.prometheusSpec.retention=15d
Accéder à Grafana
# Port-forward (développement) kubectl port-forward svc/kube-prometheus-grafana 3000:80 -n monitoring # Identifiants par défaut : admin / VotreMotDePasse # Dashboard recommandé : ID 15661 (Kubernetes Cluster)
ServiceMonitor pour scraper votre application
apiVersion: monitoring.coreos.com/v1 kind: ServiceMonitor metadata: name: mon-app-monitor labels: release: kube-prometheus spec: selector: matchLabels: app: mon-app endpoints: - port: http path: /metrics interval: 30s
Chapitre 7
Pipeline CI/CD — GitHub Actions vers Kubernetes
Workflow complet (.github/workflows/deploy.yml)
name: Build & Deploy to Kubernetes on: push: branches: [main] env: REGISTRY: ghcr.io IMAGE_NAME: ${{ github.repository }} jobs: build-and-push: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Log in to GHCR uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Build and push Docker image uses: docker/build-push-action@v5 with: push: true tags: ghcr.io/${{ github.repository }}:${{ github.sha }} deploy: needs: build-and-push runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Configure kubectl uses: azure/k8s-set-context@v3 with: kubeconfig: ${{ secrets.KUBECONFIG }} - name: Update image tag & apply run: | sed -i "s|IMAGE_TAG|${{ github.sha }}|g" k8s/deployment.yaml kubectl apply -f k8s/ -n mon-app kubectl rollout status deployment/mon-app -n mon-app
💡 Stockez votre
KUBECONFIG comme secret GitHub chiffré. Ne le committez jamais.Chapitre 8
Checklist de mise en production
Avant de déployer
- Image Docker construite en multi-stage, tag spécifique (pas
:latest) - Variables d'environnement dans ConfigMap/Secret — jamais en dur dans l'image
requestsetlimitsdéfinis sur tous les containerslivenessProbeetreadinessProbeconfigurés- Secrets gérés via Sealed Secrets ou Vault
- TLS activé via cert-manager (Let's Encrypt)
- HPA configuré avec min ≥ 2 replicas
- PodDisruptionBudget créé
- Monitoring Prometheus + alertes configurées
- Rollback testé :
kubectl rollout undo deployment/mon-app
Commandes de vérification post-déploiement
# État des pods kubectl get pods -n mon-app -w # Logs en temps réel kubectl logs -f deployment/mon-app -n mon-app # Événements du cluster kubectl get events -n mon-app --sort-by='.lastTimestamp' # Rollback si problème kubectl rollout undo deployment/mon-app -n mon-app # Vérifier le HPA kubectl get hpa -n mon-app
