E-NO
Planification de capacité Bash 10 min de lecture

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

calendar_today Publié : 2026-07-13
update Dernière mise à jour : 2026-07-13
analytics Efficacité SEO : 97%
Illustration du guide technique pour « Planification de capacité Bash avec exemples pratiques : guide d'implémentation ».

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).

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.

Score de qualité de l’article

Utilité pour le lecteur 97%
  • check_circle Guide prêt à lire
  • check_circle Exemples pratiques inclus
  • check_circle URL d’article optimisée pour le SEO