Aller au contenu

Mises à jour OTA

Une mise à jour OTA (Over-The-Air) qui échoue à 3h du matin sur 200 gateways de terrain est le cauchemar de tout ingénieur IoT — les stratégies robustes éliminent ce scénario.


Pourquoi l'OTA est critique

Dans un parc de devices IoT, le firmware n'est jamais "terminé". Les patches de sécurité sont inévitables — les CVE touchant le kernel Linux, OpenSSL, ou BusyBox affectent toutes les gateways déployées. Les correctifs de bugs émergent après les premières semaines d'usage terrain. Les nouvelles fonctionnalités doivent être déployées sans intervention physique.

Sur un parc de 500 devices, une mise à jour manuelle nécessitant 15 minutes par device représente 125 heures de travail. Hors de question. L'OTA n'est pas un luxe — c'est une nécessité opérationnelle.

La contrainte absolue : un device qui ne démarre plus après une mise à jour ratée est un device perdu jusqu'à intervention physique. Sur un capteur installé à 6 mètres de hauteur dans une usine, cette intervention peut coûter des centaines d'euros. Sur une bouée offshore, elle peut être impossible pendant des semaines.


Stratégies de mise à jour

Mise à jour full image

La stratégie la plus simple : télécharger une image système complète et remplacer intégralement la partition système. Simple à implémenter, garantit un état connu après mise à jour.

Avantages : Pas de gestion de dépendances, état final prévisible. Inconvénients : Taille de téléchargement élevée (souvent 100 MB à 1 GB), temps de mise à jour long, usage de bande passante important.

Mise à jour delta (différentielle)

Plutôt que de transmettre l'image complète, on calcule et transmet uniquement le différentiel entre la version courante et la version cible. Sur un delta de 500 MB vers 510 MB, la mise à jour peut peser 5 à 20 MB.

Outils : bsdiff/bspatch, xdelta3, courgette (Chrome). Le serveur OTA précalcule les deltas pour toutes les paires de versions (N-1→N, N-2→N, N-3→N). Le device télécharge le delta depuis sa version courante.

Avantages : Réduction drastique de la bande passante (÷10 à ÷50 typiquement). Inconvénients : Complexité de génération et de gestion des deltas, impossible si la version source est inconnue ou corrompue.

Partitionnement A/B

La stratégie A/B est le standard de facto pour les systèmes Linux embarqués et Android. Le système maintient deux partitions racine identiques (slot A et slot B). Le système actif tourne sur le slot A. La mise à jour s'installe sur le slot B pendant que le système continue de fonctionner normalement. Au redémarrage, le bootloader bascule sur le slot B.

flowchart LR
    subgraph BEFORE ["Avant mise à jour"]
        A1[Slot A\nv2.3.0\n✓ ACTIF] 
        B1[Slot B\nv2.3.0\nvide / ancienne version]
        BL1[Bootloader\n→ boot sur A]
    end

    subgraph UPDATE ["Pendant la mise à jour"]
        A2[Slot A\nv2.3.0\n✓ ACTIF en cours d'exécution]
        B2[Slot B\nv2.4.0\n⬇ Installation en arrière-plan]
        BL2[Bootloader\n→ toujours A]
    end

    subgraph AFTER ["Après redémarrage"]
        A3[Slot A\nv2.3.0\nen attente]
        B3[Slot B\nv2.4.0\n✓ ACTIF nouveau]
        BL3[Bootloader\n→ boot sur B\nsi B valide]
    end

    subgraph ROLLBACK ["Rollback si échec"]
        A4[Slot A\nv2.3.0\n✓ ACTIF restauré]
        B4[Slot B\nv2.4.0\n✗ MARQUÉ INVALIDE]
        BL4[Bootloader\n→ retour sur A\naprès N échecs]
    end

    BEFORE --> UPDATE --> AFTER
    AFTER -. échec de boot .-> ROLLBACK

Avantages : Rollback instantané et fiable, mise à jour sans coupure de service. Inconvénients : Nécessite deux fois la taille de la partition système en stockage flash.


Rollback automatique

Le rollback automatique est la protection fondamentale contre les mises à jour défaillantes. Il s'appuie sur une coopération entre le bootloader et le système.

Mécanisme watchdog au boot

Au premier démarrage après une mise à jour A/B, le bootloader marque le slot nouvellement activé comme "en période d'essai" (tries_remaining = 3). À chaque tentative de boot échouée, le compteur décrémente. À zéro, le bootloader rebascule sur l'ancien slot.

# Exemple : gestion des slots avec u-boot + libubootenv
# Vérifier l'état des slots
fw_printenv upgrade_available
# → upgrade_available=1 (mise à jour en attente de validation)
fw_printenv bootcount
# → bootcount=1 (premier boot post-mise à jour)

# Valider la mise à jour depuis userspace (à appeler après checks applicatifs)
fw_setenv upgrade_available 0
fw_setenv bootcount 0

# Si non validée dans les 3 boots → retour automatique sur l'ancien slot

Validation applicative avant confirmation

La validation ne doit pas se contenter de vérifier que le système a booté. Elle doit tester les fonctions critiques de l'application :

import sys
import subprocess
import paho.mqtt.client as mqtt
import time

def validate_update() -> bool:
    """
    Vérifie que tous les composants critiques fonctionnent
    après une mise à jour. Retourne True si le système est sain.
    """
    checks = []

    # 1. Services système démarrés
    for service in ["mosquitto", "zigbee2mqtt", "nodered"]:
        result = subprocess.run(["systemctl", "is-active", service],
                                capture_output=True, text=True)
        ok = result.stdout.strip() == "active"
        checks.append(("service:" + service, ok))

    # 2. Broker MQTT joignable
    try:
        client = mqtt.Client()
        client.connect("127.0.0.1", 1883, keepalive=5)
        client.disconnect()
        checks.append(("mqtt:local", True))
    except Exception:
        checks.append(("mqtt:local", False))

    # 3. Connectivité northbound
    try:
        result = subprocess.run(
            ["ping", "-c", "1", "-W", "3", "mqtt-prod.industrial.local"],
            capture_output=True
        )
        checks.append(("network:northbound", result.returncode == 0))
    except Exception:
        checks.append(("network:northbound", False))

    # Rapport
    all_ok = all(ok for _, ok in checks)
    for name, ok in checks:
        status = "OK" if ok else "FAIL"
        print(f"  [{status}] {name}")

    return all_ok

if validate_update():
    # Confirmer la mise à jour — le bootloader ne rollback plus
    subprocess.run(["fw_setenv", "upgrade_available", "0"])
    print("Mise à jour validée.")
else:
    print("Validation échouée — rollback au prochain redémarrage.")
    sys.exit(1)

Déploiement canary

Le déploiement canary consiste à exposer la nouvelle version à un petit pourcentage de la flotte avant un rollout complet. Cela permet de détecter les régressions sur une base restreinte avant qu'elles ne touchent toute la flotte.

Progression typique

Étape Pourcentage Durée d'observation Critères de passage
Canary initial 1 % 24 h Taux d'erreur < 0,1 %, pas de rollback automatique
Groupe pilote 10 % 48 h Taux d'erreur < 0,5 %, métriques stables
Déploiement partiel 30 % 24 h Pas de régression observée
Déploiement général 100 %

La sélection du groupe canary doit être représentative : mélanger différents sites, différentes révisions hardware, différentes conditions réseau. Un canary constitué uniquement des devices les plus récents de l'usine pilote ne représente pas la diversité réelle de la flotte.


Outils OTA

Mender

Mender est la solution open-source la plus utilisée pour les mises à jour OTA sur systèmes Linux embarqués. Il supporte les stratégies A/B (Mender block device update) et les mises à jour d'artefacts individuels (fichiers de configuration, conteneurs Docker).

# Génération d'un artefact Mender depuis une image Yocto/BuildRoot
mender-artifact write rootfs-image \
  --artifact-name "gateway-firmware-2.4.0" \
  --device-type "raspberry-pi-4" \
  --file rootfs.ext4 \
  --output gateway-firmware-2.4.0.mender

# Déploiement via CLI Mender Server
mender-cli artifacts upload gateway-firmware-2.4.0.mender
mender-cli deployments create \
  --name "Deploy 2.4.0 — Canary Usine Nord" \
  --artifact "gateway-firmware-2.4.0" \
  --device-group "canary-usine-nord"

L'interface web Mender Server affiche en temps réel la progression du déploiement : nombre de devices downloading, installing, rebooting, success, failure.

RAUC (Robust Auto-Update Controller)

RAUC est une alternative à Mender, davantage orientée systèmes Yocto. Il s'intègre profondément avec u-boot et barebox pour la gestion des slots A/B, et supporte la signature cryptographique des bundles de mise à jour.

# /etc/rauc/system.conf — configuration RAUC sur la gateway
[system]
compatible=raspberry-pi-4-industrial
bootloader=uboot
bundle-formats=plain verity

[keyring]
path=/etc/rauc/keyring.pem

[slot.rootfs.0]
device=/dev/mmcblk0p2
type=ext4
bootname=A

[slot.rootfs.1]
device=/dev/mmcblk0p3
type=ext4
bootname=B

SWUpdate

SWUpdate cible davantage les systèmes embarqués "bare metal" ou RTOS légers. Il supporte Hawkbit comme serveur de management OTA, et peut mettre à jour des partitions flash, des fichiers de configuration, des noyaux et des device trees indépendamment.

Comparatif

Outil Cible principale Stratégie A/B Delta update Interface web Licence
Mender Linux embarqué Natif Oui (v3+) Oui Apache 2 / Mender Enterprise
RAUC Linux + Yocto Natif Via plugin Non (tiers) LGPL
SWUpdate Linux + bare metal Oui Non natif Non (Hawkbit) GPL v2
Balena Conteneurs Linux Via resin Non Oui Propriétaire
Android OTA Android / AOSP Natif (A/B) Oui Via MDM Apache 2

Flux OTA complet

sequenceDiagram
    participant DEV as Device terrain
    participant MGR as Fleet Manager
    participant STORE as Artefact Store
    participant OPS as Opérateur

    OPS->>STORE: Upload firmware 2.4.0
    OPS->>MGR: Créer déploiement — canary 1 %
    MGR->>DEV: Notification OTA disponible (MQTT)
    DEV->>MGR: Accusé de réception
    DEV->>STORE: Téléchargement artefact (HTTPS + TLS)
    STORE-->>DEV: Firmware chiffré + signé
    DEV->>DEV: Vérification signature (Ed25519)
    DEV->>DEV: Installation sur slot B
    DEV->>MGR: Status: installed, rebooting
    DEV->>DEV: Redémarrage sur slot B
    DEV->>DEV: Tests de validation (30 s)
    alt Validation OK
        DEV->>DEV: fw_setenv upgrade_available 0
        DEV->>MGR: Status: success, version 2.4.0
        MGR->>OPS: Canary OK — rollout à 10 % ?
    else Validation échouée
        DEV->>DEV: Redémarrage sur slot A (rollback)
        DEV->>MGR: Status: failure, rolled back to 2.3.0
        MGR->>OPS: ALERTE — rollback détecté sur device xyz
    end

Ce qu'il faut retenir

  • Le partitionnement A/B avec rollback automatique est le seul mécanisme qui garantit qu'une mise à jour ratée ne laisse pas un device inutilisable.
  • La validation applicative (pas seulement le boot) doit être effectuée avant de confirmer la mise à jour au bootloader.
  • Le déploiement canary (1 % → 10 % → 100 %) limite l'impact des régressions à une fraction de la flotte.
  • Mender, RAUC et SWUpdate sont les trois outils open-source de référence selon le type de système cible.

Chapitre suivant : Monitoring de flotte — superviser la santé de centaines de devices depuis un dashboard centralisé.