Aller au contenu principal
pipelinebrut.fr — CI/CD · automatisation · terrain
Scripting

Scripts d'automatisation : éviter les pannes silencieuses

Par Julien Marchand

Scripts d’automatisation : éviter les pannes silencieuses

Dans beaucoup d’équipes DevOps, les scripts restent la couche invisible qui fait tenir la production au quotidien. Un script Bash qui déploie un service, un script Python qui transfère des fichiers, un PowerShell qui crée des comptes, un cron qui archive des logs : ce sont souvent quelques dizaines de lignes, écrites vite, puis conservées pendant des mois. Le problème n’est pas qu’ils échouent franchement. Le vrai risque, ce sont les pannes silencieuses : un script termine “sans erreur”, mais n’a traité qu’une partie des données, a écrasé un fichier, a exécuté deux fois une opération sensible ou a laissé un système dans un état incohérent.

En production, un script fiable ne se juge pas seulement à sa capacité à “tourner”. Il doit être observable, idempotent et sûr. Cette triade réduit fortement les incidents difficiles à diagnostiquer. L’objectif de cet article est simple : proposer une méthode concrète, applicable tout de suite, pour rendre vos scripts plus robustes sans transformer chaque automatisation en projet de six semaines.

Pourquoi les pannes silencieuses coûtent si cher

Une panne visible déclenche une alerte. Une panne silencieuse, elle, peut rester en place des heures ou des jours. C’est typiquement ce qui arrive lorsqu’un script :

  • ignore un code retour non nul dans un pipeline shell ;
  • considère qu’un transfert SFTP est réussi parce que la connexion a abouti, sans vérifier la taille ou le hash du fichier ;
  • relance une création d’utilisateur ou de ressource cloud sans vérifier si elle existe déjà ;
  • écrit des logs incomplets, impossibles à corréler avec une exécution précise ;
  • supprime des fichiers temporaires avant validation complète du traitement.

Dans l’édition 2024 du rapport State of DevOps, Google Cloud rappelle l’importance des pratiques de fiabilité, de visibilité et de réduction du travail manuel risqué dans la performance opérationnelle. Même si le rapport ne parle pas uniquement de scripts, le constat est clair : les opérations peu observables et fortement dépendantes de bricolages locaux augmentent les délais de résolution et la variabilité des déploiements.

Sur le terrain, le coût est concret. Une exécution incorrecte d’un script de synchronisation peut générer des doublons, des retards de facturation, des imports incomplets ou des incidents de conformité. Plus le problème est détecté tard, plus le temps de remédiation explose. Une erreur repérée à l’exécution se corrige en minutes ; la même erreur découverte après 48 heures peut exiger audit, reprise sur historique et communication client.

Premier pilier : rendre le script observable

Un script observable permet de répondre rapidement à quatre questions : quand a-t-il tourné, quoi a-t-il fait, sur quoi a-t-il agi, et avec quel résultat. Sans cela, vous ne faites pas de l’automatisation industrielle, vous faites de la loterie assistée par terminal.

Journaliser de façon structurée

Le minimum viable n’est pas “echo début” et “echo fin”. Il faut des logs structurés, horodatés et corrélables. Dans un script Bash, cela peut passer par des lignes JSON simples envoyées vers stdout, puis récupérées par journald, Docker logs, Fluent Bit ou Vector. Dans un script Python, le module logging permet facilement d’ajouter niveau, timestamp, identifiant d’exécution et contexte métier.

Exemple d’informations utiles à loguer :

  • un identifiant unique d’exécution ;
  • l’environnement ciblé : production, préproduction, région, cluster ;
  • le nombre d’éléments attendus et le nombre réellement traités ;
  • les durées par étape ;
  • le code retour des commandes externes ;
  • les chemins, URL, buckets ou destinations touchés ;
  • un statut final explicite : success, partial, failed.

Des outils comme Grafana Loki, Elastic ou Datadog Logs permettent ensuite de rechercher les exécutions anormales. Si votre script traite 10 000 fichiers par nuit et n’en traite soudain que 8 742, cette variation doit être visible immédiatement.

Mesurer, pas seulement raconter

Les logs sont utiles, mais les métriques permettent d’alerter. Exposez par exemple :

  • le nombre d’exécutions réussies par heure ;
  • le nombre d’échecs ;
  • la durée d’exécution ;
  • le nombre d’objets traités ;
  • le retard accumulé sur une file ou un répertoire d’entrée.

Avec Prometheus Pushgateway pour des jobs batch, ou via une sortie vers StatsD, vous pouvez créer des alertes simples dans Grafana : “aucune exécution réussie depuis 30 minutes”, “durée supérieure au 95e percentile habituel”, “volume traité inférieur de 20 % à la médiane des 7 derniers jours”. Ce type d’alerte détecte précisément les pannes silencieuses.

Faire échouer explicitement

Un script sûr ne masque pas ses erreurs. En Bash, l’usage de set -euo pipefail est une base utile, à condition de comprendre ses limites. pipefail évite qu’une commande en amont échoue sans faire tomber tout le pipeline. -u empêche l’usage de variables non définies. Mais cela ne remplace pas des vérifications explicites sur le résultat métier.

Exemple réel : un rsync peut retourner un code de sortie à interpréter finement. Un transfert “partiellement réussi” n’est pas équivalent à un succès complet. De même, une API REST qui renvoie HTTP 200 n’indique pas forcément que l’opération demandée a été appliquée ; il faut parfois inspecter un champ de statut dans la réponse JSON.

Deuxième pilier : rendre le script idempotent

L’idempotence consiste à pouvoir rejouer un script sans produire d’effets indésirables supplémentaires. En production, c’est une propriété centrale. Un orchestrateur comme Jenkins, GitLab CI, Cron, systemd timer ou Kubernetes CronJob peut relancer un job après incident. Si votre script n’est pas idempotent, un simple retry peut aggraver la situation.

Concevoir des opérations rejouables

Quelques principes concrets :

  • Vérifier avant de créer : ne créez pas un utilisateur, un dossier, un bucket ou une règle si l’objet existe déjà.
  • Utiliser des upserts quand c’est possible : en base de données, préférez des mécanismes comme INSERT ... ON CONFLICT dans PostgreSQL.
  • Travailler avec des marqueurs d’état : fichier de checkpoint, table de suivi, identifiant de lot traité.
  • Éviter les suppressions irréversibles en début de traitement : archivez, versionnez ou déplacez d’abord vers une zone tampon.
  • Nommer les artefacts de façon déterministe : date, hash, identifiant métier, pour éviter les collisions.

Exemple classique : un script d’import CSV. Mauvaise approche : lire le fichier, insérer toutes les lignes, puis déplacer le fichier en archive. Si le script tombe au milieu, un rerun peut doubler une partie des enregistrements. Meilleure approche : attribuer un identifiant de lot, stocker le hash du fichier, rejeter un lot déjà traité, et utiliser une contrainte d’unicité sur la clé métier.

Traiter les fichiers avec une stratégie atomique

Dans les transferts de fichiers, les pannes silencieuses viennent souvent d’écritures incomplètes. Une bonne pratique consiste à :

  • téléverser vers un nom temporaire ;
  • vérifier taille, checksum ou hash SHA-256 ;
  • renommer ensuite de façon atomique vers le nom final ;
  • ne déclencher le traitement aval qu’après validation.

Cette méthode est courante avec SFTP, rsync, des partages réseau ou des buckets objet compatibles S3. Pour les flux sensibles, le calcul d’un checksum côté source et côté destination évite de considérer comme “réussi” un fichier tronqué. Sur de gros volumes, cela coûte un peu de CPU, mais le gain en fiabilité est généralement très supérieur.

Troisième pilier : rendre le script sûr en production

Un script sûr limite son rayon d’explosion. Il ne fait pas confiance à son environnement, protège les secrets, valide ses entrées et prévoit des garde-fous avant les opérations destructrices.

Valider les prérequis et les entrées

Avant d’agir, le script doit vérifier :

  • la présence des binaires nécessaires : jq, curl, aws, psql ;
  • les variables d’environnement obligatoires ;
  • les permissions sur les répertoires et fichiers ;
  • la connectivité vers les services critiques ;
  • le format des paramètres reçus.

Un script qui prend un chemin de répertoire ou un identifiant de client ne devrait jamais l’utiliser sans validation. Les erreurs de quoting ou de globbing en shell sont encore une cause fréquente de comportements inattendus. Une règle simple : toujours citer les variables, et refuser explicitement les valeurs vides ou ambiguës.

Sécuriser les secrets et les permissions

Les scripts de production ne devraient pas embarquer de mots de passe en clair. Utilisez un gestionnaire de secrets comme HashiCorp Vault, AWS Secrets Manager, Azure Key Vault ou Google Secret Manager. Les permissions doivent suivre le principe du moindre privilège : un script de lecture n’a pas besoin d’un accès en suppression.

Autre point concret : évitez de loguer les tokens, les chaînes de connexion ou le contenu complet des payloads si ceux-ci contiennent des données sensibles. L’observabilité ne doit pas devenir une fuite de secrets.

Prévoir un mode dry-run et des confirmations

Pour toute opération destructive ou coûteuse, un dry-run est presque indispensable. Il permet de visualiser ce qui sera fait sans l’exécuter. Des outils comme Terraform ont popularisé ce principe avec le plan avant apply. Vos scripts peuvent faire pareil, même simplement. Un mode simulation qui liste les fichiers supprimés, les comptes modifiés ou les objets déplacés réduit fortement les erreurs humaines.

Pour les actions les plus sensibles, ajoutez des garde-fous : refus d’exécuter en production sans variable explicite, confirmation manuelle, ou vérification d’un périmètre maximum. Exemple : “refuser de supprimer plus de 500 fichiers en une exécution sans option --force”.

Une méthode simple en 7 étapes

Voici une méthode concrète, applicable à la plupart des scripts de production :

  • 1. Définir le contrat du script : entrées, sorties, effets attendus, codes retour, limites.
  • 2. Ajouter un identifiant d’exécution et des logs structurés à chaque étape critique.
  • 3. Mesurer : durée, volume traité, nombre d’erreurs, dernier succès.
  • 4. Rendre les opérations rejouables avec vérifications d’existence, checkpoints ou clés d’idempotence.
  • 5. Sécuriser : validation des paramètres, secrets externes, permissions minimales, dry-run.
  • 6. Tester les cas dégradés : réseau coupé, fichier manquant, API lente, disque plein, exécution en double.
  • 7. Brancher des alertes sur l’absence de succès, les volumes anormaux et les durées aberrantes.

Exemple concret : script de dépôt de fichiers partenaire

Prenons un cas fréquent : un script dépose chaque heure des fichiers CSV chez un partenaire via SFTP.

Version fragile : le script boucle sur un répertoire, envoie les fichiers, affiche “OK”, puis les supprime localement. Si la connexion coupe pendant l’upload, ou si le partenaire lit un fichier encore incomplet, vous avez une panne silencieuse.

Version industrialisée :

  • génération d’un identifiant de lot ;
  • écriture d’un manifeste local avec nom, taille, hash SHA-256 ;
  • upload vers un nom temporaire .part ;
  • vérification distante si possible ;
  • renommage atomique vers le nom final ;
  • archivage local seulement après confirmation ;
  • publication d’une métrique “fichiers envoyés” et “octets envoyés” ;
  • alerte si aucun fichier n’a été déposé sur deux créneaux consécutifs.

Avec cette approche, un rerun ne renvoie pas n’importe quoi, la traçabilité est claire, et l’équipe peut diagnostiquer rapidement si le problème vient du réseau, du partenaire ou du lot lui-même.

Les outils qui aident vraiment

Pas besoin d’une plateforme gigantesque pour progresser. Quelques outils éprouvés suffisent souvent :

  • ShellCheck pour détecter les erreurs fréquentes dans les scripts shell ;
  • shfmt pour normaliser le formatage Bash ;
  • pytest ou bats-core pour tester les scripts ;
  • Prometheus et Grafana pour les métriques et alertes ;
  • Loki ou Elastic pour centraliser les logs ;
  • Vault ou les gestionnaires cloud pour les secrets ;
  • systemd, Jenkins, GitLab CI ou Kubernetes CronJob pour l’exécution contrôlée.

Si vous avez déjà un pipeline CI/CD, intégrez au minimum lint, tests de non-régression et vérification du mode dry-run sur des données factices. Même pour un “petit script”, c’est rarement du luxe.

Ce qu’il faut retenir

Les pannes silencieuses ne viennent pas seulement de mauvais scripts. Elles viennent surtout de scripts opaques, non rejouables et trop confiants. La bonne nouvelle, c’est qu’on peut fortement réduire le risque avec des pratiques simples : logs structurés, métriques, validations explicites, opérations atomiques, checkpoints et garde-fous.

Dans un contexte de production, un script n’est pas un détail. C’est un composant d’exploitation à part entière. Si vous pouvez l’observer, le rejouer sans dégâts et limiter ses effets de bord, vous passez d’une automatisation fragile à une automatisation industrielle.

La vraie maturité d’un script de production ne se voit pas quand tout va bien. Elle se voit au premier retry, à la première coupure réseau et au premier incident à diagnostiquer à 3 heures du matin.

Pour aller plus loin sur l’industrialisation pragmatique des automatisations, vous pouvez aussi explorer d’autres sujets proches sur la catégorie Scripting de Pipeline Brut.