## Introduction

La planification de capacité Bash consiste à estimer et gérer le CPU, la mémoire, les E/S, les descripteurs de fichiers (FD) et le nombre de processus afin que vos scripts s'exécutent de façon prévisible sous charge. Ce guide vous fournit :

- Un flux de travail étape par étape pour dimensionner et faire évoluer en toute sécurité

- Des sondes et formules pratiques exécutables localement

- Un petit plan pilote pour réduire les risques au déploiement

Public cible : développeurs, consultants DevOps et équipes de démarrage qui livrent des tâches basées sur Bash (ETL, étapes CI, traitement de données, tâches de publication) sur ordinateurs portables, serveurs, Docker ou Kubernetes.

## Vue d'ensemble du flux de travail

Utilisez une séquence simple et reproductible. Garder les étapes distinctes réduit le retravail et clarifie les décisions.

### 1. Définir la charge de travail et les SLO

- Profil de travail : nombre d'entrées par unité de temps, taille moyenne, taille de pointe et critères d'acceptation.

- Exemples de SLO : latence P95 par élément, débit horaire et budget d'erreur.

- Modes de défaillance à éviter : OOM kills, forks incontrôlés, épuisement des FD et latences à queue longue.

### 2. Sonder l'environnement

Créez un inventaire rapide des capacités et limites.

#!/usr/bin/env bash
set -Eeuo pipefail

printf "Sonde environnement\n"
cores=$(getconf _NPROCESSORS_ONLN || echo 1)
mem_kb=$(grep -i MemTotal /proc/meminfo | awk '{print $2}')
mem_mb=$((mem_kb/1024))
fd_limit=$(ulimit -n || true)
proc_limit=$(ulimit -u || true)
avail_disk=$(df -h . | awk 'NR==2{print $4}')

printf "cores=%s\nmem_mb=%s\nfd_limit=%s\nproc_limit=%s\navail_disk=%s\n" \
 "$cores" "$mem_mb" "$fd_limit" "$proc_limit" "$avail_disk"

# Utilisation FD actuelle (utilise $$ pour le PID du shell courant)
printf "fds_now=%s\n" "$(ls -1 /proc/$$/fd | wc -l)" 

 Note : Dans les conteneurs, les valeurs ulimit peuvent différer de l'hôte. Définissez-les explicitement au besoin (Docker : --ulimit , Kubernetes : ulimit au niveau conteneur via securityContext si votre distribution le supporte).

</div>

### 3. Mesurer le coût unitaire en ressources

Choisissez un travail représentatif et mesurez le temps CPU, le RSS (mémoire résidente) et l'usage des FD.

#!/usr/bin/env bash
# sample_job.sh : remplacez le corps par votre véritable unité de travail
set -Eeuo pipefail
in="$1"
# Charge exemple : hachage puis compression pour simuler CPU + E/S
sha256sum "$in" >/dev/null
gzip -c "$in" >/dev/null 
 #!/usr/bin/env bash
# measure_cost.sh : exécute le travail et capture CPU et mémoire approximatifs
set -Eeuo pipefail
item="$1"
# Préférez /usr/bin/time -v si disponible pour le Max RSS
if command -v /usr/bin/time >/dev/null; then
 /usr/bin/time -v bash sample_job.sh "$item" 2>_time.txt 1>/dev/null || true
 cpu_user=$(awk -F: '/User time/ {gsub(/ /,""); print $2}' _time.txt)
 cpu_sys=$(awk -F: '/System time/ {gsub(/ /,""); print $2}' _time.txt)
 rss_kb=$(awk -F: '/Maximum resident set size/ {gsub(/ /,""); print $2}' _time.txt)
 printf "cpu_user=%s cpu_sys=%s rss_kb=%s\n" "$cpu_user" "$cpu_sys" "$rss_kb"
else
 # Repli : time grossier et instantané RSS pour le shell courant
 t0=$(date +%s)
 bash sample_job.sh "$item"
 t1=$(date +%s)
 rss_kb=$(awk '/VmRSS/ {print $2}' /proc/$$/status 2>/dev/null || echo 0)
 printf "elapsed_s=%s rss_kb=%s\n" "$((t1-t0))" "$rss_kb"
fi 
 Exécutez le script de mesure sur 20 à 50 éléments représentatifs et calculez médianes, P95 et maximums. Utilisez des nombres conservateurs (P95 ou max) plus marges de sécurité.

### 4. Construire un modèle de capacité simple

### Dimensionnement mémoire

- Soit rss_per_job_kb le RSS P95 en KB.

- Soit avail_mem_kb le total moins réserve OS/applications.

- Marge de sécurité : gardez 25 % de mémoire libre.

- concurrency_mem = floor((avail_mem_kb * 0.75) / rss_per_job_kb)

Exemple :

- Mémoire totale : 8 Go → 8 × 1024 × 1024 = 8 388 608 KB

- Réserve 25 % → 6 291 456 KB utilisables

- rss_per_job_kb = 80 000

- concurrency_mem = floor(6 291 456 / 80 000) = 78

### Dimensionnement CPU

- Soit cores = cœurs logiques.

- Soit cpu_sec_per_item = secondes utilisateur + système par élément à concurrence 1×.

- Budget 70 % CPU pour laisser de la marge.

- max_items_per_sec_cpu = (cores * 0.7) / cpu_sec_per_item

- concurrency_cpu = floor(cores * 0.7) si chaque travail occupe un cœur.

Exemple :

- cores = 8 , cpu_sec_per_item = 0,2 s

- max_items_per_sec_cpu = (8 * 0,7) / 0,2 = 28 éléments/s

### Dimensionnement descripteurs de fichiers

- fd_limit depuis ulimit -n .

- Chaque étape de pipeline peut consommer des FD (stdin/stdout/stderr, pipes, fichiers ouverts).

- Mesure : avant et pendant le travail, lancez ls /proc/$$/fd | wc -l .

- concurrency_fd = floor((fd_limit * 0.8) / fds_per_job)

### Limites processus et threads

- proc_limit depuis ulimit -u . Comptez les enfants avec ps --ppid .

- Gardez 30 % de marge : enfants <= proc_limit * 0.7 .

### E/S disque et réseau

dd if=/dev/zero of=./_io_test.bin bs=1M count=512 oflag=direct 2>&1 | tail -1 
 Utilisez dd pour vérifier grossièrement la vitesse d'écriture. Pour les lectures, chronométrez cat ou sha256sum sur un gros fichier. Assurez-vous que votre débit cible reste sous 70 % de la bande passante observée en régime permanent.

### 5. Décider le parallélisme et la montée en charge

- Pour le parallélisme local : utilisez xargs -P ou GNU parallel pour plafonner la concurrence.

- Pour le partitionnement : divisez l'entrée en N parties et traitez avec N workers.

- Préférez le parallélisme à gros grain (niveau processus) au lancement d'un sous-shell pour chaque jeton.

Exemple de harnais avec xargs :

#!/usr/bin/env bash
set -Eeuo pipefail
P="${P:-8}" # plafond de parallélisme
LIST="${1:-worklist.txt}
export -f sample_job.sh || true

# Chaque ligne dans worklist.txt est un chemin à traiter
cat "$LIST" | xargs -r -P "$P" -n 1 -I{} bash sample_job.sh {} 

### 6. Ajouter garde-fous et marges de sécurité

- set -Eeuo pipefail pour échouer vite et réduire le travail partiel.

- Réglages ulimit (valeurs d'exemple, ajustez pour votre hôte) :

 # Plafonds souples pour éviter l'impact sur l'hôte partagé
ulimit -S -n 4096 # FD
ulimit -S -u 2048 # processus
ulimit -S -m $((6*1024*1024)) || true # mémoire KB, si supporté 

- Contre-pression : plafonnez la profondeur de file et les workers parallèles.

- Politique de reprise : backoff exponentiel pour erreurs E/S transitoires.

### 7. Observer les signaux qui pilotent les décisions de mise à l'échelle

- Latence : moyenne mobile et P95 par élément qui augmentent dans le temps.

- Profondeur de file : la liste de travail grossit plus vite que la completion.

- Saturation ressources : vmstat r > cores sur longues périodes, CPU > 90 %, attente E/S > 20 % en continu.

- Erreurs : sorties non-nulles, OOM kills, pipes cassés.

 Traceur de latence simple :

#!/usr/bin/env bash
set -Eeuo pipefail
log=_latency.csv
echo "ts_s, elapsed_ms" > "$log"
while read -r item; do
 t0=$(date +%s%3N)
 bash sample_job.sh "$item"
 t1=$(date +%s%3N)
 echo "$((t0/1000)),$((t1 - t0))" >> "$log"
done < worklist.txt 

### 8. Notes conteneurs et clusters (Docker, Compose, Kubernetes)

- Docker : définissez plafonds CPU et mémoire pour que vos calculs correspondent à la réalité, ex. --cpus , --memory , --ulimit nofile=4096 .

- Compose : répliquez ces limites dans la définition du service.

- Kubernetes : requests et limits doivent refléter vos budgets mesurés ; surveillez le throttling CPU si les limites sont serrées.

- Kubernetes Ingress et Secrets sont sans lien avec les limites de calcul Bash, mais vos travaux peuvent récupérer des entrées via HTTP ou lire des secrets ; évitez de récupérer des secrets dans des boucles serrées.

## Exemples pratiques : formules et extraits

### Calculateur de concurrence lié mémoire

 #!/usr/bin/env bash
set -Eeuo pipefail
rss_kb=${1:?"rss_kb par travail requis"}
avail_kb=$(grep -i MemAvailable /proc/meminfo | awk '{print $2}')
# Marge de sécurité 25 %
usable_kb=$(( (avail_kb * 75) / 100 ))
conc=$(( usable_kb / rss_kb ))
printf "avail_kb=%s usable_kb=%s rss_kb=%s concurrency=%s\n" \
 "$avail_kb" "$usable_kb" "$rss_kb" "$conc" 

### Estimateur de débit CPU

 #!/usr/bin/env bash
set -Eeuo pipefail
cores=$(getconf _NPROCESSORS_ONLN || echo 1)
cpu_sec_per_item=${1:?"secondes CPU par élément"}
budget=$(python3 - <<EOF
cores=$cores
cpi=$cpu_sec_per_item
print(round((cores*0.7)/cpi,2))
EOF
)
printf "cores=%s items_per_sec_cpu_budget=%s\n" "$cores" "$budget" 
 Si Python n'est pas disponible, calculez à la main ou avec bc .

### Vérification FD de sécurité dans un pipeline

#!/usr/bin/env bash
set -Eeuo pipefail
before=$(ls /proc/$$/fd | wc -l)
# Simule un petit fanout
{ find . -maxdepth 1 -type f -print0 | xargs -0 -P 8 -n 1 -I{} bash -c 'cat "{}" >/dev/null'; }
after=$(ls /proc/$$/fd | wc -l)
limit=$(ulimit -n)
printf "fd_before=%s fd_after=%s fd_limit=%s\n" "$before" "$after" "$limit" 

### Motif xargs sécurisé

 find input/ -type f -print0 | xargs -0 -P "$P" -n 1 -I{} bash -c '
 set -Eeuo pipefail
 f="$1"
 # Évite de lire tout le fichier en mémoire
 sha256sum "$f" | awk "{print \$1}" >> hashes.txt
' _ {} 

### Liste de contrôle des risques de croissance

- Évitez les boucles O(n²) sur des répertoires grandissants ; préférez l'indexation et les scans en un seul passage.

- N'engloutissez pas les fichiers entiers en mémoire ; streamez avec while read -r ou awk .

- Évitez le globbing non borné ; utilisez find avec limites de taille et profondeur.

- Utilisez -print0 avec xargs -0 pour gérer les espaces sans risque et réduire les bogues d'analyse.

- Plafonnez les reprises et le parallélisme pour éviter les effets de meute.

## Plan pilote local

 Rendez le premier pilote étroit, mesurable et facile à inspecter localement avant tout déploiement.

### Périmètre

- Un type de travail critique, 200 éléments représentatifs, taille max 64 Mo.

- Parallélisme fixe P dans {1, 4, 8}.

### Critères de réussite ou échec

- Latence P95 par élément ≤ 300 ms à P=4.

- Zéro OOM, zéro épuisement FD, sorties non-nulles ≤ 0,5 %.

- CPU régime permanent ≤ 70 %, mémoire libre ≥ 25 %.

### Fichiers pilotes

worklist.txt : liste des chemins d'entrée.

pilot.sh : orchestre l'exécution et collecte les métriques.

#!/usr/bin/env bash
set -Eeuo pipefail
P="${P:-4}
LIST="${1:-worklist.txt}
mkdir -p _pilot
: > _pilot/latency.csv
: > _pilot/errors.log

run_item() {
 local item="$1"
 local t0 t1 rc
 t0=$(date +%s%3N)
 if bash sample_job.sh "$item\); then
 t1=$(date +%s%3N)
 echo "$((t0/1000)),$((t1-t0))" >> _pilot/latency.csv
 else
 rc=$?
 echo "$(date -Is) item=$item rc=$rc" >> _pilot/errors.log
 fi
}
export -f run_item

cp "$LIST" _pilot/worklist.run
cat _pilot/worklist.run | xargs -r -P "$P" -n 1 -I{} bash -c 'run_item "$@"' _ {}

# Résumés
items=$(wc -l < _pilot/worklist.run)
errs=$(wc -l < _pilot/errors.log 2>/dev/null || echo 0)
lat_p95=$(awk -F, 'NR>1{a[NR-1]=$2} END{n=asort(a); idx=int(0.95*n); if(idx<1) idx=1; print a[idx]}' _pilot/latency.csv 2>/dev/null || echo 0)
free_mem_mb=$(awk '/MemAvailable/ {print int($2/1024)}' /proc/meminfo)

printf "items=%s errors=%s p95_ms=%s free_mem_mb=%s\n" "$items" "$errs" "$lat_p95" "$free_mem_mb" 

### Boucle d'ajustement

- Si p95_ms est élevé : diminuez P ou réduisez le CPU par élément (optimisez le travail).

- Si free_mem_mb est bas : réduisez P ou la mémoire par élément.

- Si les erreurs montent : inspectez _pilot/errors.log et ajoutez reprises ou validation d'entrée.

### Sécurité et retour arrière

- P=1 par défaut si un contrôle échoue.

- Gardez les ulimits souples dans le pilote pour borner l'impact sur les hôtes partagés.

## Conclusion

 Vous disposez maintenant d'une méthode concrète pour dimensionner les charges de travail Bash :

- Mesurez les coûts CPU, mémoire, FD et E/S par élément.

- Modélisez la concurrence avec formules simples et 25 % à 30 % de marge.

- Appliquez des garde-fous avec ulimit et parallélisme borné.

- Surveillez latence, profondeur de file, saturation et erreurs pour décider quand monter en charge.

### Prochaines étapes

- Lancez le pilote local, enregistrez latences P95 et usage ressources, et définissez votre P par défaut.

- Documentez les budgets régime permanent et pointe pour CPU, mémoire, FD et processus.

- Si vous passez à Docker ou Kubernetes, reportez les mêmes limites et marges dans les spécifications des conteneurs.