Tests de brokers

Mettre en place des environnements MQTT dev, staging et production

Séparer les environnements MQTT dev, staging et production : un broker par environnement, préfixes de topic, identifiants et ACL dédiés, smoke tests.

Mis à jour · 8 min de lecture

Mettre en place des environnements MQTT dev, staging et production

La façon la plus propre d'utiliser MQTT en dev, staging et production tient en une phrase : donnez à chaque environnement son propre broker (ou une instance de broker isolée) et ses propres identifiants, gardez des noms de topic identiques d'un environnement à l'autre et ne changez que la configuration (URL, nom d'utilisateur, mot de passe) quand le code passe de l'un à l'autre. Tout le reste de ce guide vise à rendre cela ennuyeux et sûr.

À quoi sert chaque environnement

DevStagingProduction
ObjectifItération rapide, expériencesRelease candidate, tests d'intégration et de chargeAppareils et clients réels
BrokerDocker local ou endpoint de test partagéMême type de broker et mêmes réglages qu'en productionDédié, supervisé
DonnéesFactices, jetablesRéalistes, générées ou anonymiséesRéelles
Qui se connecteDéveloppeurs, simulateursCI, appareils de test, QAParc d'appareils et services backend
IdentifiantsPar développeur ou utilisateur dev partagéPar service, propres au stagingPar service et par appareil
TLSOptionnelObligatoire, comme en productionObligatoire

Le staging n'est utile que s'il se comporte comme la production. Un broker de staging sans TLS, sans authentification ou avec des ACL grandes ouvertes laissera passer des releases qui échouent dès le premier jour en production.

Trois façons de séparer les environnements

ModèleFonctionnementIdéal pourAttention
Un broker par environnementHôtes ou instances séparés ; topics identiquesLa production, et toute équipe qui peut se le permettrePlus d'endpoints à gérer
Broker partagé, topics préfixésdev/..., stg/... sur un broker, ACL par environnementDev et staging avec un petit budgetUne ACL mal configurée fait fuiter des données entre environnements ; capacité partagée
HybrideProduction isolée ; dev et staging partagent un brokerLa plupart des petites et moyennes équipesGardez les identifiants de production totalement hors du broker partagé

Quel que soit votre choix, la production ne partage jamais un broker avec les tests. Un test de charge, un simulateur emballé ou un message retenu publié par erreur ne doivent pas pouvoir atteindre de vrais appareils.

Bien utiliser les préfixes de topic

Avec un broker par environnement, ne mettez pas du tout l'environnement dans le topic. factory/line1/temp est le même topic en staging et en production, si bien que les abonnements, dashboards, règles et fichiers d'ACL peuvent être promus sans modification.

Quand des environnements partagent un broker, un préfixe est indispensable, et il doit être ajouté à un seul endroit. Le pire schéma consiste à disperser des tests d'environnement dans le code (if env == "prod": topic = ...). Construisez chaque topic avec un helper qui ajoute le préfixe configuré, et laissez ce préfixe vide quand l'environnement a son propre broker :

mqtt-config.mjs
import mqtt from "mqtt";

export const ENV = process.env.APP_ENV ?? "dev";
const url = process.env.MQTT_URL;
const prefix = process.env.MQTT_TOPIC_PREFIX ?? "";   // "" quand l'environnement a son propre broker

if (!url) throw new Error("MQTT_URL is not set");

export const topic = (t) => (prefix ? `${prefix}/${t}` : t);

export function connect(name) {
  return mqtt.connect(url, {
    clientId: `${name}-${ENV}-${Math.random().toString(16).slice(2, 8)}`,
    username: process.env.MQTT_USER,
    password: process.env.MQTT_PASS,
    protocolVersion: 5,
    reconnectPeriod: 2000,
  });
}
utilisation
import { connect, topic } from "./mqtt-config.mjs";

const client = connect("ingest");
client.on("connect", () => client.subscribe(topic("factory/+/temp"), { qos: 1 }));

Inclure l'environnement dans le client ID rend aussi les logs du broker lisibles : vous voyez tout de suite que ingest-stg-3fa9c1 est un service de staging. Sur un broker de dev partagé, donnez à chaque développeur un préfixe personnel comme testmqtt/dev-alice pour que les expériences n'entrent jamais en collision ; les détails sont dans nommer les topics de test pour qu'ils n'entrent jamais en collision.

Identifiants et ACL par environnement

Chaque environnement a ses propres utilisateurs, et un identifiant d'un environnement doit être inutilisable dans un autre. Nommez les utilisateurs d'après le service et l'environnement (ingest-stg, ingest-prod) pour qu'un identifiant divulgué ou mal utilisé saute aux yeux dans les logs.

Sur un broker partagé entre dev et staging, ce sont les ACL qui séparent réellement les environnements. Un fichier d'ACL Mosquitto pour ce cas :

acl (broker partagé dev + staging)
user ingest-dev
topic read dev/telemetry/#
topic write dev/cmd/#

user ingest-stg
topic read stg/telemetry/#
topic write stg/cmd/#

Vérifiez la séparation au lieu de lui faire confiance : connectez-vous avec l'utilisateur de dev et essayez de vous abonner à stg/#. En MQTT 5, le broker renvoie un code d'échec dans le SUBACK (0x87 Not authorized) ; en MQTT 3.1.1, vous obtenez 0x80 ou simplement aucun message. Tester les permissions ACL MQTT montre comment automatiser ces tests négatifs.

Configuration par environnement

Gardez un fichier de configuration par environnement et chargez-le au démarrage ; le code reste le même.

.env.dev
APP_ENV=dev
MQTT_URL=mqtt://broker.connectmq.com:1883
MQTT_USER=username
MQTT_PASS=password
MQTT_TOPIC_PREFIX=testmqtt/dev-alice
.env.staging
APP_ENV=staging
MQTT_URL=mqtts://<your-staging-host>:8883
MQTT_USER=ingest-stg
MQTT_PASS=<depuis votre coffre de secrets>
MQTT_TOPIC_PREFIX=

Les valeurs de production ne vivent que dans le coffre de secrets du déploiement de production, jamais dans le dépôt et jamais dans les jobs de test de la CI. Ajoutez un garde-fou au setup des tests pour qu'une erreur ne puisse pas diriger la suite vers la production :

test/setup.mjs
import { ENV } from "../mqtt-config.mjs";

if (ENV === "prod" || ENV === "production") {
  throw new Error("Refusing to run tests against production");
}

Les appareils exigent la même discipline. Le firmware ne doit pas coder le broker en dur ; l'environnement est attribué au provisioning en même temps que les identifiants de l'appareil, afin qu'un appareil de staging ne se retrouve pas dans le parc de production. Voir provisioning des appareils MQTT.

Promouvoir la configuration du broker

La configuration du broker (listeners, réglages TLS, limites, fichiers d'ACL) doit traverser les environnements de la même manière que le code :

  1. Conservez la configuration du broker dans git sous forme d'un template et de valeurs par environnement (noms d'hôte, chemins de certificats, limites). Le générateur de configuration Mosquitto permet d'obtenir rapidement un fichier de départ correct.
  2. Relisez les modifications d'ACL comme du code : un seul # sur la mauvaise ligne ouvre tout.
  3. Appliquez la modification en dev, puis en staging, lancez les tests, et seulement ensuite promouvez le même commit en production.
  4. Comparez le staging à la production avant chaque release. Toute différence qui n'est ni un nom d'hôte, ni un secret, ni une limite de capacité est un bug qui attend son heure.

Smoke test de chaque environnement

Après chaque déploiement, lancez un aller-retour complet avec les identifiants propres à cet environnement. Le même script fonctionne partout :

smoke.sh
#!/usr/bin/env bash
set -euo pipefail
: "${MQTT_HOST:?}" "${MQTT_USER:?}" "${MQTT_PASS:?}"
T="${MQTT_TOPIC_PREFIX:-testmqtt}/smoke/$(date +%s)"
OUT=$(mktemp)
mosquitto_sub -h "$MQTT_HOST" -p 8883 --capath /etc/ssl/certs \
  -u "$MQTT_USER" -P "$MQTT_PASS" -t "$T" -C 1 -W 10 > "$OUT" &
sleep 1
mosquitto_pub -h "$MQTT_HOST" -p 8883 --capath /etc/ssl/certs \
  -u "$MQTT_USER" -P "$MQTT_PASS" -t "$T" -m ok -q 1
wait
grep -qx ok "$OUT" && echo "smoke test passed on $MQTT_HOST"

-W 10 fait quitter le subscriber en erreur si rien n'arrive en dix secondes : un environnement cassé fait donc échouer le pipeline au lieu de le bloquer. Pour une version plus complète, voir un script de smoke test MQTT ; pour une vérification manuelle, connectez-vous avec le client MQTT en ligne en utilisant les identifiants de l'environnement.

Erreurs fréquentes

  • Les mêmes identifiants partout. Un mot de passe de dev divulgué devient un incident de production.
  • Un staging sans TLS ni authentification. Les bugs de certificats et de permissions apparaissent alors pour la première fois en production.
  • Des tests d'environnement dans la logique métier. Promouvez de la configuration, pas des branches de code.
  • Des données de test retenues sur un broker partagé. Effacez-les après les tests, sinon les exécutions suivantes lisent un état périmé.
  • Des appareils de staging oubliés. Du matériel de test pointé vers la production par une ancienne configuration pollue les données réelles.

Avant la mise en production, passez la checklist de production MQTT sur l'environnement de production lui-même, pas sur le staging.

Des brokers séparés, des identifiants séparés, des topics identiques et un seul endroit où la configuration change : avec ces quatre règles, faire passer une release de dev à staging puis en production n'est qu'un changement de configuration et un smoke test, pas une migration.

Questions fréquentes

Dev, staging et production doivent-ils partager un même broker MQTT ?

La production doit avoir son propre broker ou une instance isolée. Dev et staging peuvent partager un broker si chaque environnement a ses propres identifiants, son propre préfixe de topic et des ACL qui les séparent, mais des instances séparées sont plus simples à maîtriser.

Le nom de l'environnement doit-il figurer dans le topic MQTT ?

Seulement quand les environnements partagent un broker. Avec un broker par environnement, gardez des topics identiques partout pour promouvoir code et dashboards sans modification. Si un préfixe est nécessaire, ajoutez-le à un seul endroit du code client.

Comment empêcher les tests de publier en production ?

Réservez les identifiants de production aux services de production, tenez-les à l'écart des jobs de test de la CI et ajoutez dans le setup des tests un garde-fou qui refuse de s'exécuter si l'environnement ou l'URL du broker configurés sont ceux de la production.