#27 — htb-fq-codel
PLANIFIÉ
Priorité: 🔴 CRITIQUE · Type: TYPE E · Conteneur: rgz-gateway · Code: scripts/qos/htb_setup.sh
Dépendances: #7 rgz-gateway
Description
L'outil HTB + fq_codel implémente la classification et le contrôle de débit au niveau du noyau Linux via les primitives tc (Traffic Control) d'iproute2. Il s'exécute exclusivement dans le conteneur rgz-gateway et constitue le plan de données QoS de l'ensemble du réseau ACCESS.
HTB (Hierarchical Token Bucket) est la discipline de mise en file d'attente (qdisc) racine. Elle permet d'organiser le trafic en une hiérarchie de classes avec débit garanti (CIR — Committed Information Rate) et débit maximal autorisé (MIR). Les classes peuvent emprunter de la bande passante inutilisée par les autres classes de même niveau, garantissant ainsi une utilisation optimale de la capacité WAN disponible.
fq_codel (Fair Queue Controlled Delay) est l'AQM (Active Queue Management) attaché à chaque classe HTB feuille. Il implémente l'algorithme CoDel pour maintenir une latence faible même en conditions de charge élevée, en combattant activement le bufferbloat. L'aspect "Fair Queue" garantit que chaque flux TCP bénéficie d'un accès équitable à la bande passante de sa classe, empêchant un seul flux de monopoliser la capacité.
Le script htb_setup.sh est exécuté au démarrage du conteneur rgz-gateway, après la configuration nftables (#7, #32) et le marquage DSCP (#28). Il lit les variables d'environnement pour adapter les débits à la capacité WAN réelle de chaque déploiement sans modification du code.
Architecture Interne
Interface eth0 (WAN-side) — débit total : QOS_TOTAL_MBPS (ex: 100Mbit)
│
└──► qdisc HTB root handle 1: (default class 1:30 pour trafic non classifié)
│
└──► class 1:1 — Root HTB rate=100mbit (parent de toutes les sous-classes)
│
├──► class 1:10 MGMT rate=10mbit ceil=100mbit prio=1
│ Trafic : SSH(22), RADIUS(1812/1813/3799), Prometheus(9090),
│ Grafana(3000), Alertmanager, SNMP(161)
│ DSCP match : CS6 (48), CS7 (56)
│ qdisc : fq_codel handle 10: (latence cible 5ms)
│
├──► class 1:20 Interactive rate=5mbit ceil=80mbit prio=2
│ Trafic : HTTP(80), HTTPS(443), DNS(53), portail captif,
│ NTP(123), WhatsApp, KKiaPay, Letexto
│ DSCP match : CS3 (24), AF41 (34)
│ qdisc : fq_codel handle 20: (latence cible 20ms)
│
└──► class 1:30 Bulk rate=1mbit ceil=100mbit prio=3
Trafic : tout le reste — downloads, streaming, P2P,
trafic non marqué (default)
DSCP match : CS1 (8), BE (0)
qdisc : fq_codel handle 30: (latence cible 100ms)
Filtres tc (u32) : lecture DSCP → redirection vers classe correspondante
ip dscp CS6/CS7 → flowid 1:10
ip dscp CS3/AF41 → flowid 1:20
ip dscp CS1/BE → flowid 1:30
(défaut) → flowid 1:30 (class default du qdisc root)Configuration
Variables d'environnement
| Variable | Valeur exemple | Description |
|---|---|---|
QOS_INTERFACE | eth0 | Interface réseau WAN à contrôler |
QOS_TOTAL_MBPS | 100 | Débit WAN total disponible (Mbit/s) |
QOS_MGMT_RATE_MBPS | 10 | CIR classe MGMT garanti (Mbit/s) |
QOS_INTER_RATE_MBPS | 5 | CIR classe Interactive garanti (Mbit/s) |
QOS_BULK_RATE_MBPS | 1 | CIR classe Bulk garanti (Mbit/s) |
QOS_INTER_CEIL_MBPS | 80 | MIR classe Interactive (Mbit/s) |
QOS_FQCODEL_TARGET | 5ms | Latence cible fq_codel (classe MGMT) |
QOS_FQCODEL_INTERVAL | 100ms | Intervalle mesure fq_codel |
Script htb_setup.sh
#!/bin/sh
# scripts/qos/htb_setup.sh
# HTB + fq_codel QoS setup pour rgz-gateway
# Lancé par docker/gateway/entrypoint.sh au démarrage
set -e
IFACE="${QOS_INTERFACE:-eth0}"
TOTAL="${QOS_TOTAL_MBPS:-100}"
MGMT_RATE="${QOS_MGMT_RATE_MBPS:-10}"
INTER_RATE="${QOS_INTER_RATE_MBPS:-5}"
BULK_RATE="${QOS_BULK_RATE_MBPS:-1}"
INTER_CEIL="${QOS_INTER_CEIL_MBPS:-80}"
echo "[QoS] Suppression règles tc existantes sur ${IFACE}..."
tc qdisc del dev "${IFACE}" root 2>/dev/null || true
echo "[QoS] Configuration HTB root..."
tc qdisc add dev "${IFACE}" root handle 1: htb default 30
echo "[QoS] Classe root 1:1..."
tc class add dev "${IFACE}" parent 1: classid 1:1 \
htb rate "${TOTAL}mbit"
echo "[QoS] Classe MGMT 1:10 (prio=1, ${MGMT_RATE}mbit garanti)..."
tc class add dev "${IFACE}" parent 1:1 classid 1:10 \
htb rate "${MGMT_RATE}mbit" ceil "${TOTAL}mbit" prio 1
echo "[QoS] Classe Interactive 1:20 (prio=2, ${INTER_RATE}mbit garanti)..."
tc class add dev "${IFACE}" parent 1:1 classid 1:20 \
htb rate "${INTER_RATE}mbit" ceil "${INTER_CEIL}mbit" prio 2
echo "[QoS] Classe Bulk 1:30 (prio=3, ${BULK_RATE}mbit garanti)..."
tc class add dev "${IFACE}" parent 1:1 classid 1:30 \
htb rate "${BULK_RATE}mbit" ceil "${TOTAL}mbit" prio 3
echo "[QoS] fq_codel sur chaque classe feuille..."
tc qdisc add dev "${IFACE}" parent 1:10 handle 10: fq_codel \
target 5ms interval 100ms
tc qdisc add dev "${IFACE}" parent 1:20 handle 20: fq_codel \
target 20ms interval 100ms
tc qdisc add dev "${IFACE}" parent 1:30 handle 30: fq_codel \
target 100ms interval 1000ms
echo "[QoS] Filtres u32 DSCP → classes..."
# CS6 (48) et CS7 (56) → MGMT
tc filter add dev "${IFACE}" parent 1: protocol ip prio 1 u32 \
match ip dsfield 0xc0 0xfc flowid 1:10
tc filter add dev "${IFACE}" parent 1: protocol ip prio 2 u32 \
match ip dsfield 0xe0 0xfc flowid 1:10
# CS3 (24) et AF41 (34) → Interactive
tc filter add dev "${IFACE}" parent 1: protocol ip prio 3 u32 \
match ip dsfield 0x60 0xfc flowid 1:20
tc filter add dev "${IFACE}" parent 1: protocol ip prio 4 u32 \
match ip dsfield 0x88 0xfc flowid 1:20
# CS1 (8) → Bulk
tc filter add dev "${IFACE}" parent 1: protocol ip prio 5 u32 \
match ip dsfield 0x20 0xfc flowid 1:30
echo "[QoS] HTB + fq_codel opérationnel sur ${IFACE}."Commandes Utiles
# Vérifier la configuration HTB active
docker exec rgz-gateway tc qdisc show dev eth0
docker exec rgz-gateway tc class show dev eth0
docker exec rgz-gateway tc filter show dev eth0
# Statistiques de trafic par classe (bytes, paquets, drops)
docker exec rgz-gateway tc -s class show dev eth0
# Statistiques fq_codel — latence mesurée, drops
docker exec rgz-gateway tc -s qdisc show dev eth0
# Appliquer manuellement le script QoS (après modification)
docker exec rgz-gateway /scripts/qos/htb_setup.sh
# Supprimer toutes les règles tc (reset propre)
docker exec rgz-gateway tc qdisc del dev eth0 root 2>/dev/null || true
# Vérifier les drops par classe (indicateur de saturation)
docker exec rgz-gateway tc -s class show dev eth0 | grep -A5 "class htb 1:30"
# Test débit depuis le gateway (vers un hôte test)
docker exec rgz-gateway iperf3 -c 10.0.0.1 -t 10 -b 100M
# Logs de démarrage QoS dans le gateway
docker logs rgz-gateway --tail=30 | grep -i "QoS\|htb\|tc"Sécurité
| Règle | Application |
|---|---|
| LL#40 | Ce script n'appelle jamais nft flush ruleset — les règles nftables sont gérées séparément par entrypoint.sh |
| LL#41 | Les règles tc sont non-persistantes (contrairement à nftables) — elles sont recréées à chaque démarrage du conteneur sans conflit |
| LL#27 | Logs Docker limités à max-size: 10m, max-file: 3 pour rgz-gateway |
| LL#33 | restart: unless-stopped dans docker-compose.core.yml pour rgz-gateway |
Interaction avec les autres outils QoS
Le script htb_setup.sh doit être exécuté après le script DSCP (#28) dans entrypoint.sh pour que les filtres tc u32 trouvent les paquets déjà marqués. L'ordre d'exécution dans entrypoint.sh est :
# docker/gateway/entrypoint.sh — ordre obligatoire
/scripts/gateway/nftables_setup.sh # #7 rgz-gateway (deny-all, NAT)
/scripts/qos/dscp_marking.sh # #28 nftables DSCP marking
/scripts/qos/htb_setup.sh # #27 HTB + fq_codel (ce fichier)
/scripts/gateway/watch_config.sh & # #32 hot-reload nftablesImplémentation TODO
- [ ] Créer
scripts/qos/htb_setup.shavec les paramètres via variables d'environnement - [ ] Ajouter les variables QoS dans
.env.example(QOS_INTERFACE,QOS_TOTAL_MBPS, etc.) - [ ] Intégrer l'appel
htb_setup.shdansdocker/gateway/entrypoint.sh(après DSCP) - [ ] Valider
tcdisponible dans l'imagedocker/gateway/Dockerfile(iproute2 installé) - [ ] Tester les 3 classes avec
iperf3et marquage DSCP sur banc de test - [ ] Vérifier que la suppression
tc qdisc del dev eth0 rootest idempotente au redémarrage - [ ] Documenter les seuils adaptés à la capacité WAN réelle (100Mbit = valeur par défaut)
- [ ] Valider que fq_codel réduit effectivement la latence sous charge (test bufferbloat)
- [ ] Ajouter métriques
tc -sdans le dashboard Grafana (#37) via node_exporter textfile collector
Dernière mise à jour: 2026-02-21