E-NO Logo
EN FR
Bash capacity planning 10 Min Read

Planification de capacité Bash avec exemples pratiques : guide d'implémentation

calendar_today Published: 2026-07-13
update Last Updated: 2026-07-13
analytics SEO Efficiency: 97%
Technical guide illustration for Planification de capacité Bash avec exemples pratiques : guide d'implémentation.

Introduction

La planification de capacité Bash (Bash capacity planning) consiste à estimer et gérer CPU, mémoire, IO, descripteurs de fichiers (FDs) et nombre de processus afin que vos scripts s'exécutent de manière prévisible sous charge. Ce guide fournit :

  • Un workflow pas à pas pour dimensionner et scaler en sécurité
  • Des sondes et formules pratiques exécutables en local
  • Un petit plan pilote pour réduire les risques avant déploiement

Lecteurs cibles : développeurs, consultants DevOps et équipes startup qui livrent des jobs Bash (ETL, étapes de CI, data munging, tâches de release) sur laptops, serveurs, Docker ou Kubernetes.

Vue d'ensemble du workflow

Utilisez une séquence simple et répétable. Garder les étapes distinctes réduit le rework et clarifie les décisions.

  1. Définir le workload et les SLOs
  • Profil de travail : nombre d'entrées par unité de temps, taille moyenne, taille de pic, critères d'acceptation.
  • Exemples de SLO : latence P95 par item, débit horaire, budget d'erreurs.
  • Modes de panne à éviter : OOM kill, forks incontrôlés, épuisement de FDs, grandes queues de latence.

Dressez un inventaire rapide des capacités et limites.

  1. Sonder l'environnement
#!/usr/bin/env bash
set -Eeuo pipefail

printf "Env probe\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 actuelle des FDs
printf "fds_now=%s\n" "$(ls -1 /proc/$/fd | wc -l)"

Notes :

  • En conteneur, les valeurs ulimit peuvent différer de l'hôte. Définissez-les explicitement si besoin (Docker : --ulimit, Kubernetes : ulimit au niveau conteneur via securityContext si la distribution le permet).

Choisissez un job représentatif et mesurez temps CPU, RSS (mémoire résidente) et usage FD.

  1. Mesurer le coût ressource par item
#!/usr/bin/env bash
# sample_job.sh : remplacez le corps par votre vrai travail unitaire
set -Eeuo pipefail
in="$1"
# Charge d'exemple : hachage puis compression (CPU + IO)
sha256sum "$in" >/dev/null
gzip -c "$in" >/dev/null
#!/usr/bin/env bash
# measure_cost.sh : exécuter le job et capturer CPU et mémoire
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 : chrono grossier et snapshot RSS du 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 la mesure sur 20 à 50 items représentatifs et calculez médianes, P95 et maximums.

Utilisez des valeurs conservatrices (P95 ou max) plus des marges de sécurité.

  1. Construire un modèle de capacité simple
  • Dimensionnement mémoire
  • rss_per_job_kb = RSS P95 en KB.
  • avail_mem_kb = total moins réserve OS/app.
  • Marge : garder 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
  • cores = cœurs logiques.
  • cpu_sec_per_item = secondes user + system par item à 1× concurrence.
  • Allouez 70 % de CPU pour garder une marge.
  • max_items_per_sec_cpu = (cores × 0.7) / cpu_sec_per_item
  • concurrency_cpu = floor((cores × 0.7)) si chaque job 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 items/s
  • Dimensionnement des FDs
  • fd_limit via ulimit -n.
  • Chaque étape de pipeline consomme des FDs (stdin/stdout/stderr, pipes, fichiers ouverts).
  • Mesure : avant et pendant le job, exécutez ls /proc/$/fd | wc -l.
  • concurrency_fd = floor((fd_limit × 0.8) / fds_per_job)
  • Limites de processus et threads
  • proc_limit via ulimit -u. Comptez les enfants avec ps --ppid.
  • Gardez 30 % de marge : enfants <= proc_limit × 0,7.

dd if=/dev/zero of=./_io_test.bin bs=1M count=512 oflag=direct 2>&1 | tail -1

  • Disque et réseau
  • Vérifiez grossièrement l'écriture :
  • Pour la lecture, chronométrez cat ou sha256sum sur un gros fichier. Visez < 70 % du débit soutenu observé.
  1. Décider du scaling et du parallélisme
  • En local, limitez le parallélisme avec xargs -P ou GNU parallel.
  • Pour sharder, découpez l'entrée en N parts et lancez N workers.
  • Préférez un parallélisme grossier (niveau processus) plutôt que des sous-shells par token.

Exemple 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 de worklist.txt est un chemin à traiter
cat "$LIST" | xargs -r -P "$P" -n 1 -I{} bash sample_job.sh {}
  1. Ajouter des garde-fous et marges de sécurité
  • set -Eeuo pipefail pour échouer vite et limiter le travail partiel.
  • ulimit (exemples, à ajuster selon l'hôte) :
# Plafonds souples pour éviter l'impact global
ulimit -S -n 4096   # FDs
ulimit -S -u 2048   # processus
ulimit -S -m $((6*1024*1024)) || true  # mémoire KB, si supporté
  • Back-pressure : plafonnez la profondeur de file et les workers parallèles.
  • Politique de retry : backoff exponentiel pour les erreurs IO transitoires.
  1. Observer les signaux qui pilotent le scaling
  • Latence : moyenne mobile et P95 par item qui montent dans le temps.
  • Profondeur de file : la liste de travail grossit plus vite que les achèvements.
  • Saturation ressource : vmstat r > cores sur de longues périodes, CPU > 90 %, IO wait > 20 % soutenu.
  • Erreurs : codes de retour non nuls, OOM kills, broken pipes.

Suivi simple de latence :

#!/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
  1. Notes conteneurs et cluster (Docker, Compose, Kubernetes)
  • Docker : fixez CPU et mémoire pour que le modèle colle à la réalité (ex. --cpus, --memory, et --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 ; attention au throttling CPU si les limits sont serrées.
  • Kubernetes Ingress et Secrets ne touchent pas aux limites compute Bash, mais vos jobs peuvent tirer des entrées HTTP ou lire des secrets ; évitez de récupérer des secrets dans des boucles serrées.

Exemples pratiques : formules et extraits

  1. Calculateur de concurrence limitée par la mémoire
#!/usr/bin/env bash
set -Eeuo pipefail
rss_kb=${1:?"rss_kb requis par job"}
avail_kb=$(grep -i MemAvailable /proc/meminfo | awk '{print $2}')
# Marge de 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"
  1. Estimateur de throughput CPU
#!/usr/bin/env bash
set -Eeuo pipefail
cores=$(getconf _NPROCESSORS_ONLN || echo 1)
cpu_sec_per_item=${1:?"secondes CPU par item"}
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.

  1. Vérification de sécurité des FDs dans une pipeline
#!/usr/bin/env bash
set -Eeuo pipefail
before=$(ls /proc/$/fd | wc -l)
# Simuler 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"
  1. Patron xargs sûr
find input/ -type f -print0 | xargs -0 -P "$P" -n 1 -I{} bash -c '
  set -Eeuo pipefail
  f="$1"
  # Éviter de charger tout le fichier en mémoire
  sha256sum "$f" | awk "{print \$1}" >> hashes.txt
' _ {}
  1. Checklist des risques de croissance
  • Évitez les boucles O(n^2) sur des répertoires en croissance ; préférez l'indexation et les scans en un seul passage.
  • Ne chargez pas des 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 et réduire les bugs de parsing.
  • Cappez les retries et le parallélisme pour éviter les troupeaux tonitruants (thundering herds).

Plan pilote local

Faites un premier pilote étroit, mesurable et facile à inspecter en local avant tout déploiement.

Périmètre

  • Un type de job critique, 200 items représentatifs, taille max 64 Mo.
  • Parallélisme P fixé dans {1, 4, 8}.

Critères de succès/échec

  • Latence P95 par item <= 300 ms à P=4.
  • Zéro OOM, zéro épuisement de FDs, sorties non nulles <= 0,5 %.
  • CPU en régime stationnaire <= 70 %, mémoire libre >= 25 %.

Fichiers du pilote

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

pilot.sh : orchestre le run 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 de tuning

  • Si p95_ms est élevé : baissez P ou réduisez le CPU par item (optimisez le job).
  • Si free_mem_mb est faible : réduisez P ou la mémoire par item.
  • Si les erreurs montent : inspectez _pilot/errors.log et ajoutez des retries ou de la validation d'entrée.

Sécurité et rollback

  • Par défaut, revenez à P=1 si un check échoue.
  • Gardez des ulimits souples dans le pilote pour borner l'impact sur des hôtes partagés.

Conclusion

Vous disposez maintenant d'une méthode concrète pour dimensionner des workloads Bash :

  • Mesurez le coût CPU, mémoire, FDs et IO par item.
  • Modelez la concurrence avec des formules simples et 25-30 % de marge.
  • Appliquez des garde-fous avec ulimit et un parallélisme borné.
  • Surveillez latence, profondeur de file, saturation et erreurs pour décider quand scaler.

Prochaines étapes

  • Exécutez le pilote local, enregistrez les latences P95 et l'usage ressources, et fixez votre P par défaut.
  • Documentez les budgets de régime et de pic pour CPU, mémoire, FDs et processus.
  • En migrant vers Docker ou Kubernetes, reportez les mêmes limites et marges dans les spécifications de conteneur.

Article Quality Score

Reader usefulness 97%
  • check_circle Reader-ready guide
  • check_circle Practical examples included
  • check_circle Clean SEO article URL