Bonnes pratiques d’ingénierie HexVM

Simuler un réseau dégradé sur un Mac cloud

Simuler un réseau dégradé sur un Mac cloud

Lors du débogage à distance d’un client iOS, le cas le plus difficile à reproduire n’est généralement pas une coupure totale, mais une connexion qui reste disponible tout en devenant très lente : établissement de connexion plus long, débit montant réduit et perte occasionnelle de requêtes. Ces conditions finissent par provoquer des soumissions en double, des boucles de nouvelle tentative ou une interface qui reste indéfiniment en attente. Sur un Mac cloud HexVM, la priorité n’est pas de limiter immédiatement le débit, mais de séparer le trafic de test de l’application des flux d’administration comme SSH et VNC. Une règle trop large pourrait sinon couper votre propre accès à la machine.

Définir des scénarios de réseau dégradé vérifiables

N’utilisez pas « réseau de mauvaise qualité » comme condition de test. Cette formulation ne permet ni de reproduire le scénario ni de déterminer si le correctif fonctionne. Commencez par définir des paramètres fixes et le comportement attendu du client pour chaque scénario.

Scénario Limite en réception et en envoi Latence ajoutée Taux de perte Principaux critères de validation
Référence Aucune limite 0 ms 0 Durée et taux de réussite des requêtes normales
Latence élevée 8 Mbit/s 120 ms 0 État de chargement, annulation et expiration
Faible bande passante 1 Mbit/s 80 ms 0 Progression des envois et passage en arrière-plan
Liaison instable 4 Mbit/s 100 ms 2% Limite des nouvelles tentatives et gestion de l’idempotence

Ces paramètres constituent des entrées de test et ne décrivent pas la qualité réseau du nœud. L’équipe doit ajuster les valeurs en fonction des délais d’expiration des API, de la taille des fichiers et des réseaux utilisés par les utilisateurs. En revanche, elles doivent rester identiques pendant une même campagne de régression.

L’objectif d’un test en réseau dégradé n’est pas de démontrer que la requête finit par aboutir, mais de vérifier qu’en cas d’échec, le client met rapidement fin à l’attente, affiche un état compréhensible et ne crée aucune donnée en double.

Établir une référence sans limitation

Avant de charger la moindre règle de mise en forme du trafic, relevez le DNS, la route et les temps de réponse du point de terminaison. Celui-ci doit être contrôlé par l’équipe et proposer un chemin de sonde qui n’écrit aucune donnée en production.

export TEST_URL="https://test-endpoint.invalid/health"
scutil --dns | grep 'nameserver\[[0-9]*\]'
route -n get default
networkQuality
curl -sS -o /dev/null \
  -w 'code=%{http_code} connect=%{time_connect} tls=%{time_appconnect} total=%{time_total}
' \
  "$TEST_URL"

Le domaine d’exemple doit être remplacé par une véritable adresse de test sous votre contrôle. Consignez la médiane d’au moins trois requêtes au lieu de ne conserver que la meilleure mesure. Si des erreurs DNS, des problèmes de certificat ou des anomalies de routage apparaissent déjà à ce stade, corrigez-les d’abord : l’ajout de nouvelles tentatives ne doit pas servir à les masquer.

Vérifiez également si l’adresse cible est servie par plusieurs IP. Si une seule d’entre elles est limitée, la requête peut être résolue vers une autre adresse et présenter des performances variables d’un essai à l’autre. Il est plus fiable de prévoir une entrée fixe pour les validations en réseau dégradé et d’enregistrer le résultat de la résolution avant l’exécution.

Limiter uniquement le trafic du point de terminaison cible

Sous macOS, dnctl permet de créer des canaux imposant une limite de débit et une latence, tandis que pf dirige le trafic correspondant vers ces canaux. La règle doit restreindre simultanément l’IP cible, le protocole et le port. Elle ne doit jamais utiliser un critère générique couvrant toutes les connexions sortantes.

Le script ci-dessous crée un canal de test limité à 8 Mbit/s, avec 120 ms de latence et 2% de perte. TARGET_IP doit contenir l’adresse fixe du point de terminaison contrôlé.

set -euo pipefail

TARGET_IP="${TARGET_IP:?set TARGET_IP first}"
PIPE_ID=310
ANCHOR="com.apple/hexvm-nettest"

cleanup() {
  sudo pfctl -a "$ANCHOR" -F all >/dev/null 2>&1 || true
  sudo dnctl delete "$PIPE_ID" >/dev/null 2>&1 || true
}

trap cleanup EXIT INT TERM

sudo dnctl pipe "$PIPE_ID" config bw 8Mbit/s delay 120 plr 0.02
printf 'dummynet out quick proto tcp from any to %s port 443 pipe %s
' \
  "$TARGET_IP" "$PIPE_ID" |
  sudo pfctl -a "$ANCHOR" -f -

sudo pfctl -E >/dev/null
sudo pfctl -a "$ANCHOR" -sr -v

curl -sS -o /dev/null \
  -w 'code=%{http_code} connect=%{time_connect} total=%{time_total}
' \
  "$TEST_URL"

Commencez par vérifier que la règle s’applique à l’aide d’un contrôle d’intégrité en lecture seule, puis exécutez les tests métier qui écrivent des données. N’utilisez pas pfctl -d pour le nettoyage, car d’autres règles de pare-feu peuvent déjà être actives sur la machine. Le script vide uniquement sa propre anchor et supprime uniquement son propre pipe afin de ne pas perturber les tâches exécutées en parallèle.

Protéger la session distante contre les règles involontaires

Avant l’exécution, conservez un second terminal d’administration ouvert et vérifiez que les règles ne couvrent ni les ports d’administration, ni la passerelle par défaut, ni un caractère générique correspondant à n’importe quelle destination. Si la cible de test et la connexion d’administration utilisent la même IP, prévoyez une entrée de test distincte au lieu de compter sur l’ordre des règles.

Transformer le comportement du client en assertions automatisées

La validation en réseau dégradé ne doit pas se limiter à vérifier si une requête « finit par revenir ». Le code de test doit consigner le nombre de tentatives, le type d’erreur final, la durée totale d’attente et l’identifiant de la requête. Pour les opérations d’écriture, le serveur comme le client doivent utiliser une clé d’idempotence stable. Après une expiration, le client doit d’abord vérifier le résultat avant de décider s’il faut réessayer.

La couche réseau peut être remplacée par une enveloppe observable afin de contrôler les limites suivantes :

  1. Une connexion qui dépasse le seuil défini peut être annulée au lieu d’attendre indéfiniment.
  2. Le nombre de nouvelles tentatives est plafonné et les intervalles incluent une temporisation progressive afin d’éviter une tempête de requêtes.
  3. Après une annulation explicite par l’utilisateur, aucune tâche en arrière-plan ne renvoie la requête à son insu.
  4. Une interruption d’envoi conserve un état explicite et n’est pas affichée comme une opération terminée.
  5. Une fois le réseau rétabli, l’opération peut reprendre sans créer d’enregistrement en double.

Les tests sur simulateur peuvent cibler un identifiant d’appareil explicite afin de ne pas dépendre d’un nom d’appareil qui se trouverait par hasard sur la machine.

export SIMULATOR_UDID="replace-with-booted-simulator-udid"

xcodebuild test \
  -scheme NetworkBehaviorTests \
  -destination "platform=iOS Simulator,id=${SIMULATOR_UDID}" \
  -resultBundlePath build/NetworkBehavior.xcresult

Enregistrez un paquet de résultats distinct pour chaque scénario réseau et inscrivez les paramètres du scénario dans les journaux de test. En cas d’échec, les résultats pourront ainsi indiquer sous quelles valeurs de latence et de perte le problème survient, au lieu de ne laisser qu’un état rouge impossible à reproduire.

Nettoyer les règles et terminer la régression

Le script effectue le nettoyage lorsqu’il se termine normalement ou reçoit un signal d’interruption. Une vérification manuelle reste néanmoins nécessaire si la session distante se ferme de manière inattendue. Après vous être reconnecté, exécutez :

sudo pfctl -a com.apple/hexvm-nettest -sr
sudo dnctl list
curl -sS -o /dev/null \
  -w 'code=%{http_code} connect=%{time_connect} total=%{time_total}
' \
  "$TEST_URL"

L’anchor dédiée doit être vide et la liste ne doit plus contenir le pipe numéro 310. Relancez ensuite la requête de référence et vérifiez que le temps de connexion comme la durée totale sont revenus dans leur plage normale d’avant le test.

Pour finir, effectuez une régression sans mise en forme du trafic. Vérifiez en priorité que les correctifs liés au réseau dégradé n’ont pas modifié l’ordre des requêtes, l’utilisation du cache ou la réactivité de l’interface sur un réseau normal. Regroupez dans un même compte rendu les paramètres du scénario, la version du client, la version du point de terminaison cible, le chemin du paquet de résultats et le résultat du nettoyage. Le test en réseau dégradé devient ainsi un processus d’ingénierie auditable, reproductible et réversible en toute sécurité, plutôt qu’une démonstration manuelle ponctuelle.

Questions fréquentes

Pourquoi ne faut-il pas limiter tout le trafic sortant du Mac distant ?

Une règle globale affecte aussi SSH, VNC et les téléchargements. Elle peut donc couper la session d’administration. Il faut cibler l’adresse IP, le protocole et le port du service testé.

Comment vérifier que les règles temporaires ont bien été supprimées ?

Videz l’ancre pf dédiée, supprimez le pipe dnctl associé, vérifiez que l’ancre ne contient plus de règle, puis relancez la mesure de référence.

Quels comportements faut-il valider sur le client iOS ?

Vérifiez les délais d’expiration, les tentatives limitées, l’annulation, le message hors ligne, la prévention des doublons et la reprise après retour du réseau.

Apple Silicon dédié

Choisissez un Mac dans le cloud dédié pour votre prochaine compilation

Choisissez le modèle de Mac mini, la durée de location et la région selon votre charge de travail. Chaque commande correspond à un nœud physique dédié ; la disponibilité réelle est indiquée en temps réel dans la console.

Choisir une solution Mac dans le cloud