## Introduction

La planification de capacité Helm est la discipline qui consiste à prévoir et à contrôler les besoins en CPU, mémoire et replicas de vos charges de travail Kubernetes, puis à encoder ces décisions dans vos charts. Bien faite, elle réduit les coûts, prévient les incidents et rend le scaling prévisible. Ce guide vous donne un workflow orienté praticien, des extraits concrets de values.yaml et de HPA, ainsi qu'un pilote sécurisé que vous pouvez exécuter dans un bac à sable avant de déployer plus largement.

## Aperçu du workflow

Utilisez cette séquence reproductible pour chaque service :

- Définir les cibles : RPS de pointe attendus, concurrence, SLO de latence et budget d'erreur.

- Établir une référence localement : exécutez le service dans Docker ou un namespace de test et mesurez le CPU%, la mémoire RSS et la latence P95 sous une charge représentative.

- Estimer le coût par requête : déduisez les ms CPU/requête et la mémoire en régime permanent par worker.

- Choisir la forme du pod : définissez requests et limits pour qu'elles s'adaptent au dimensionnement des nœuds et aux objectifs de QoS.

- Dimensionner les replicas : calculez les replicas pour la pointe avec marge, puis définissez les cibles HPA.

- Instrumenter : exposez les métriques de l'application et de Nginx ; scrappez avec Prometheus.

- Valider : lancez une charge contrôlée et vérifiez le throttling, les OOM et la latence.

- Déployer progressivement : augmentez le trafic tout en surveillant les signaux clés.

## Concepts clés et signaux

Définitions de base :

- CPU request et limit : millicœurs réservés et plafond. Le risque de throttling augmente près de la limite.

- Memory request et limit : MiB réservés et plafond. Un OOMKill survient quand l'usage dépasse la limite.

- Classes QoS : Guaranteed (requests=limits), Burstable (requests<limits), BestEffort (pas de requests). Guaranteed réduit le risque d'éviction ; Burstable permet le burst CPU.

Signaux principaux à surveiller :

- Utilisation CPU par pod (cible 50-70 % en pointe pour de la marge)

- Mémoire RSS et comportement du GC (cible <80 % de la limite en pointe)

- Latence P95 et taux d'erreur

- Profondeur de file ou backlog pour les workers

- Connexions actives Nginx et taux de 5xx

Formules approximatives :

Choisissez un safety_factor entre 1,3 et 2,0 selon le caractère bursty.

- rps_per_pod ≈ concurrency_per_pod / p95_service_time_sec

- replicas_needed = ceil((peak_rps / rps_per_pod) * safety_factor)

- cpu_request_per_pod ≈ p95_cpu_ms_per_req * rps_per_pod / 1000 (en cœurs)

## Exemples pratiques

### Exemple A : Nginx devant un site statique ou une API en amont

Hypothèses : p95 10 ms par requête sur cache hit, pointe cible 2000 rps, Nginx utilise environ 60-100 Mi de mémoire stable par worker.

- rps_per_pod : avec worker_processes=2 , concurrency_per_pod ≈ 2 × 1024 via epoll. La concurrence est souvent limitée par le CPU ; on suppose un rps_per_pod sûr de 1500.

- replicas_needed = ceil((2000 / 1500) * 1,4) = 2

- cpu_request_per_pod : on suppose 0,2 CPU à la cible ; memory_request 128Mi.

replicaCount: 2
resources:
 requests:
 cpu: 200m
 memory: 128Mi
 limits:
 cpu: 500m
 memory: 256Mi
nginx:
 workerProcesses: 2
 workerConnections: 2048
hpa:
 enabled: true
 minReplicas: 2
 maxReplicas: 10
 targetCPUUtilizationPercentage: 60 
 Template HPA (autoscaling/v2) :

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
 name: {{ include "chart.fullname" . }}
spec:
 scaleTargetRef:
 apiVersion: apps/v1
 kind: Deployment
 name: {{ include "chart.fullname" . }}
 minReplicas: {{ .Values.hpa.minReplicas }}
 maxReplicas: {{ .Values.hpa.maxReplicas }}
 metrics:
 - type: Resource
 resource:
 name: cpu
 target:
 type: Utilization
 averageUtilization: {{ .Values.hpa.targetCPUUtilizationPercentage }} 

### Exemple B : API CPU-intensive (Go ou Node.js)

 Hypothèses : p95 50 ms/requête à saturation 1 vCPU ; pointe cible 600 rps.

Pour réduire les replicas, augmentez le CPU par pod : essayez 2 vCPU en request.

- rps_per_pod à 60 % CPU cible : 0,6 CPU × (1 / 0,050) = 12 rps si mono-thread. Go peut utiliser plusieurs threads ; on suppose 4 workers → ~48 rps.

- replicas_needed = ceil((600 / 48) * 1,5) = 19

- Avec 2 vCPU request : nouveau rps_per_pod ≈ 2 × 0,6 / 0,050 = 24 rps par worker ; avec 4 workers ~96 rps.

- replicas_needed = ceil((600 / 96) * 1,4) = 9

replicaCount: 9
resources:
 requests:
 cpu: "2"
 memory: 512Mi
 limits:
 cpu: "3"
 memory: 1Gi
env:
 - name: GOMAXPROCS
 value: "4"
 - name: NODE_OPTIONS
 value: "--max-old-space-size=512"
hpa:
 enabled: true
 minReplicas: 6
 maxReplicas: 20
 targetCPUUtilizationPercentage: 65 

### Exemple C : Worker d'arrière-plan tirant d'une file

 Hypothèses : chaque job prend 200 ms CPU et 300 ms temps réel ; cible 500 jobs/s ; concurrence worker 10.

- jobs_per_pod = 10 / 0,300 ≈ 33 jobs/s (borné par le temps réel)

- cpu_request_per_pod = (0,200 s × 33) = 6,6 CPU secondes/s → 6,6 cœurs pour tenir 100 % CPU ; à 70 % cible, request ~4,6 cœurs. C'est trop gros ; réduisez la concurrence à 4 par pod ou utilisez plus de pods.

- Avec concurrence 4 : jobs_per_pod ≈ 13 ; cpu_request ≈ (0,200 × 13) = 2,6 cœurs ; request 2, limit 3.

- replicas_needed = ceil((500 / 13) * 1,3) = 50

replicaCount: 50
resources:
 requests:
 cpu: "2"
 memory: 512Mi
 limits:
 cpu: "3"
 memory: 768Mi
env:
 - name: WORKER_CONCURRENCY
 value: "4"
hpa:
 enabled: true
 minReplicas: 20
 maxReplicas: 120
 targetCPUUtilizationPercentage: 60 

## Requêtes Prometheus pour valider la capacité

- RPS : sum(rate(http_requests_total[5m])) by (pod)

- Latence : histogram_quantile(0.95, sum(rate(http_request_duration_seconds_bucket[5m])) by (le, pod))

- CPU : sum(rate(container_cpu_usage_seconds_total{image!=""}[5m])) by (pod)

- Mémoire : container_memory_working_set_bytes{image!=""}

## Exemple de job GitLab CI/CD pour déploiement Helm

 deploy:
 stage: deploy
 image: alpine/helm:3.14.0
 script:
 - helm upgrade --install api charts/api \
 --namespace prod \
 --set resources.requests.cpu=2 \
 --set resources.requests.memory=512Mi \
 --set hpa.targetCPUUtilizationPercentage=65
 rules:
 - if: '$CI_COMMIT_BRANCH == "main"' 

## Plan de pilote local

 Gardez le premier pilote étroit, mesurable et facile à inspecter localement :

- Sélectionnez un service avec trafic stable et SLO clairs.

- Ajoutez l'instrumentation : taux de requêtes, latence P95, CPU, RSS.

- Implémentez values.yaml avec requests/limits explicites et un HPA.

- Dans un namespace bac à sable, lancez une charge courte tout en surveillant Prometheus.

- Vérifiez l'absence de throttling, d'OOM et de régressions de latence.

- Figer la version du chart et documenter la forme de pod et la politique de scaling choisies.

- Augmentez le trafic progressivement et revérifiez les mêmes tableaux de bord.

## Marges de sécurité opérationnelles

### Défauts pratiques

- Marge CPU : ciblez 60-70 % d'utilisation en pointe ; cible HPA 60-65 %.

- Marge mémoire : dimensionnez pour que la pointe reste sous 70-80 % de la limite.

- N+1 : conservez au moins un replica de rechange au-dessus du minimum pour absorber les pannes.

- Portes de readiness : définissez readinessProbe pour protéger la latence pendant le warmup.

- QoS : pour les services critiques en latence, rapprochez requests de limits ; pour le trafic web bursty, autorisez une limite CPU 1,5x-2x au-dessus de la request.

### Risques courants

- OOMKill par trafic soudain ou fuites : surveillez le working set, évitez des limites mémoire trop proches de l'état stable.

- Throttling CPU : un CPU soutenu près de la limite induit des pics de latence ; baissez la cible HPA ou relevez les limites.

- Fragmentation de densité de pods : des pods surdimensionnés réduisent la flexibilité d'ordonnancement ; préférez des tailles de pods modérées.

- Surengagement des nœuds : alignez les requests avec la capacité des nœuds et le cluster autoscaler.

- I/O et stockage éphémère : définissez requests/limits si la charge écrit sur disque.

### Contrôles à ajouter

- PodDisruptionBudget pour maintenir les replicas minimum pendant la maintenance.

- ResourceQuota et LimitRange par namespace pour prévenir les pods hors de contrôle.

- Alertes : CPU élevé pendant 10 min, mémoire >80 % limite, burn de SLO latence, taux de 5xx.

## Conclusion

La planification de capacité avec Helm, c'est mesurer, modéliser et encoder les décisions de ressources comme du code. Commencez par des cibles claires, établissez une référence locale, choisissez une forme de pod sensée avec marge, définissez les cibles HPA, et validez sous charge. Gardez une marge de sécurité, surveillez les bons signaux, et progressez par itérations. Les exemples et extraits ici devraient vous permettre de lancer un petit pilote sûr, d'en tirer les leçons, et de monter en charge en confiance.