Akvorado release 2.0
Vincent Bernat
Akvorado 2.0 est sorti aujourd’hui ! Akvorado est un collecteur de flux réseau IPFIX et sFlow. Il enrichit les flux et les stocke dans une base de données ClickHouse. Les utilisateurs peuvent parcourir les données via une console web. Cette version introduit un changement architectural important et d’autres améliorations mineures. Regardons plus en détails ! 🤿
$ git diff --shortstat v1.11.5 493 files changed, 25015 insertions(+), 21135 deletions(-)
Nouveau service « outlet »#
Le changement majeur dans Akvorado 2.0 est la séparation du service inlet en deux parties : l’inlet et l’outlet. Auparavant, l’inlet gérait tout le traitement des flux : réception, décodage et enrichissement. Les flux étaient ensuite envoyés à Kafka pour stockage dans ClickHouse :
Les flux réseau atteignent le service inlet via des chaussettes UDP, un protocole non fiable. L’inlet doit les traiter assez rapidement pour éviter de perdre des paquets. Pour gérer un grand nombre de flux, l’inlet crée plusieurs ensembles de workers pour recevoir les flux, récupérer les métadonnées et assembler les flux enrichis pour Kafka. De nombreuses options de configuration existaient pour la montée en charge, ce qui augmentait la complexité pour les utilisateurs. Le code devait éviter tout blocage, rendant la chaîne de traitement complexe et parfois peu fiable, notamment le composant BMP1. L’ajout de nouvelles fonctionnalités sans aggraver le problème devenait difficile2.
Dans Akvorado 2.0, l’inlet reçoit les flux et les pousse vers Kafka sans les décoder. Le nouveau service outlet gère les tâches restantes :
Ce changement va au-delà d’un simple découpage3 : l’outlet lit maintenant les flux depuis Kafka et les pousse vers ClickHouse, deux tâches qu’Akvorado ne gérait pas auparavant. Les flux sont groupés par lots pour augmenter l’efficacité et réduire la charge sur ClickHouse en utilisant ch-go, un client Go bas niveau pour ClickHouse. Quand les lots sont trop petits, des insertions asynchrones sont utilisées (e20645). Le nombre de workers de l’outlet s’ajuste dynamiquement (e5a625) en fonction de la cible sur la taille des lots et de la latence (50 000 flux et 5 secondes par défaut).
Cette nouvelle architecture nous permet également de simplifier et optimiser
le code. L’outlet récupère les métadonnées de façon synchrone (e20645). Le
composant BMP devient plus simple en supprimant le multitâche coopératif
(3b9486). Réutiliser le même objet RawFlow
pour décoder les flux Protobuf
depuis Kafka réduit la pression sur le ramasse-miettes (8b580f).
L’effet sur les performances globales d’Akvorado était quelque peu incertain, mais un utilisateur a indiqué gagner 35 % d’utilisation CPU en moins après migration depuis la version précédente, ainsi que la résolution du problème de longue date du composant BMP. 🥳
Autres changements#
Cette nouvelle version inclut de nombreux changements divers. On y trouve par exemple la complétion pour les ports sources et destinations (f92d2e), et le redémarrage automatique de l’orchestrateur (0f72ff) lors de changements de configuration pour éviter un piège courant pour les nouveaux utilisateurs.
Concentrons-nous sur quelques domaines clés pour cette version : observabilité, documentation, CI, Docker, Go et JavaScript.
Observabilité#
Akvorado expose des métriques pour fournir une visibilité sur la chaîne de
traitement et aider à diagnostiquer les problèmes. Elles sont disponibles au
format Prometheus, via par exemple /api/v0/inlet/metrics
. Avec
l’introduction de l’outlet, de nombreuses métriques ont été déplacées. Certaines
ont également été renommées (4c0b15) pour correspondre aux bonnes pratiques
Prometheus. Une métrique pour mesurer le décalage
des consommateurs Kafka a été ajoutée (e3a778).
Si vous n’avez pas votre propre pile d’observabilité, la configuration Docker Compose fournie avec Akvorado en propose une. Vous pouvez l’activer avec les profils introduits à cette fin (529a8f).
Le profil prometheus
fournit Prometheus pour stocker les métriques et
Alloy pour les collecter (2b3c46, f81299 et 8eb7cd). Les
métriques Redis et Kafka sont collectées via l’exporteur intégré à Alloy
(560113). Les autres métriques sont exposées au format Prometheus et sont
automatiquement récupérées par Alloy à l’aide d’annotations Docker. C’est
similaire à ce qui est fait pour configurer Traefik. cAdvisor a également
été ajouté (83d855) pour fournir des métriques liées aux conteneurs.
Le profil loki
fournit Loki pour stocker les logs (45c684). Bien
qu’Alloy puisse collecter et envoyer les logs vers Loki, ses capacités d’analyse
sont limitées : je n’ai pas trouvé de moyen de préserver toutes les métadonnées
associées aux logs structurés produits par de nombreuses applications, y compris
Akvorado. Vector remplace Alloy (95e201) et propose un langage, VRL,
pour transformer les logs. Malheureusement, il ne peut actuellement pas
récupérer les logs Docker précédenat son démarrage.
Enfin, le profil grafana
fournit Grafana, mais les tableaux de bord
fournis sont pour le moment cassés. Ce sera corrigé dans une prochaine version.
Documentation#
La configuration Docker Compose fournie par Akvorado facilite la mise en route rapide. Cependant, Akvorado nécessite quelques étapes obligatoires pour être fonctionnel. Il est livré avec une documentation complète, y compris un chapitre sur le dépannage des problèmes. J’espérais que cette documentation réduirait la charge de support. Il est difficile de savoir si cela fonctionne. Les utilisateurs satisfaits rapportent rarement leurs succès, tandis qu’une poignée d’utilisateurs ouvrent des discussions pour demander de l’aide sans avoir vraiment lu la documentation.
Dans cette version, la documentation a été considérablement améliorée.
$ git diff --shortstat v1.11.5 -- console/data/docs 10 files changed, 1873 insertions(+), 1203 deletions(-)
La documentation a été mise à jour (fc1028) pour correspondre à la nouvelle architecture d’Akvorado. La section de dépannage a été réécrite (17a272). Une documentation sur comment améliorer les performances de ClickHouse lors de la mise à niveau depuis des versions antérieures à 1.10.0 a été ajoutée (5f1e9a). Un LLM a relu l’ensemble (06e3f3). La documentation destinée aux développeurs a également été améliorée (548bbb, e41bae et 871fc5).
Du point de vue de l’utilisabilité, les sections de la table des matières peuvent maintenant se replier (c142e5). Les avertissements aident à attirer l’attention des utilisateurs sur les points importants (8ac894).

Intégration continue#
Cette version inclut des efforts pour accélérer l’intégration continue sur GitHub. Les tests de couverture et de concurrence s’exécutent en parallèle (6af216 et fa9e48). L’image Docker se construit pendant les tests mais ne reçoit son étiquette qu’après leurs succès (8b0dce).

Les tests de bout en bout (883e19) s’assurent que la configuration Docker Compose fournie fonctionne comme attendu. Hurl exécute des tests sur diverses URL, notamment pour vérifier les métriques (42679b et 169fa9). Par exemple :
## Test inlet has received NetFlow flows GET http://127.0.0.1:8080/prometheus/api/v1/query [Query] query: sum(akvorado_inlet_flow_input_udp_packets_total{job="akvorado-inlet",listener=":2055"}) HTTP 200 [Captures] inlet_receivedflows: jsonpath "$.data.result[0].value[1]" toInt [Asserts] variable "inlet_receivedflows" > 10 ## Test inlet has sent them to Kafka GET http://127.0.0.1:8080/prometheus/api/v1/query [Query] query: sum(akvorado_inlet_kafka_sent_messages_total{job="akvorado-inlet"}) HTTP 200 [Captures] inlet_sentflows: jsonpath "$.data.result[0].value[1]" toInt [Asserts] variable "inlet_sentflows" >= {{ inlet_receivedflows }}
Docker#
Akvorado est fourni avec une configuration Docker Compose complète pour aider les utilisateurs à démarrer rapidement. Elle assure un déploiement cohérent, éliminant de nombreux problèmes liés à l’environnement et la configuration. Elle sert également de documentation.
Cette version apporte quelques petites améliorations autour de Docker :
- un exemple pour configurer Traefik pour TLS avec Let’s Encrypt (1a27bb),
- la compression HTTP (bee9a5), et
- le support d’IPv6 (a74a41).
Auparavant, de nombreuses images Docker provenaient de la bibliothèque de conteneurs Bitnami. Cependant, Bitnami a été racheté par VMWare en 2019 et VMWare a été racheté par Broadcom en 2023. En conséquence, les images Bitnami ont été dépréciées en moins d’un mois. Ce n’était pas vraiment une surprise4. Les versions précédentes d’Akvorado avaient déjà commencé à s’en éloigner. Dans cette version, l’image Kafka du projet Apache remplace celle de Bitnami (1eb382). Grâce au passage au mode KRaft, Zookeeper n’est plus nécessaire (0a2ea1, 8a49ca et f65d20).
Les images Docker d’Akvorado étaient auparavant compilées avec Nix. Cependant,
construire des images AArch64 sur x86-64 est lent car cela repose sur
l’émulation de QEMU. Le Dockerfile
mis à jour utilise des constructions
multi-étapes et multi-plateformes : une étape
construit la partie JavaScript sur la plateforme hôte, une étape construit la
partie Go en compilation croisée sur la plateforme hôte, et la dernière étape
assemble l’image en se basant sur une image légère
(268e95 et d526ca).
# Ceci est une version simplifiée FROM --platform=$BUILDPLATFORM node:20-alpine AS build-js RUN apk add --no-cache make WORKDIR /build COPY console/frontend console/frontend COPY Makefile . RUN make console/data/frontend FROM --platform=$BUILDPLATFORM golang:alpine AS build-go RUN apk add --no-cache make curl zip WORKDIR /build COPY . . COPY --from=build-js /build/console/data/frontend console/data/frontend RUN go mod download RUN make all-indep ARG TARGETOS TARGETARCH TARGETVARIANT VERSION RUN make FROM gcr.io/distroless/static:latest COPY --from=build-go /build/bin/akvorado /usr/local/bin/akvorado ENTRYPOINT [ "/usr/local/bin/akvorado" ]
Lors de la construction pour plusieurs plateformes avec --platform
linux/amd64,linux/arm64,linux/arm/v7
, les étapes de construction jusqu’à la
ligne surlignée ne s’exécutent qu’une seule fois pour toutes les plateformes.
Cela accélère considérablement la compilation. 🚅
Akvorado livre maintenant des images Docker pour ces plateformes :
linux/amd64
, linux/amd64/v3
, linux/arm64
et linux/arm/v7
. Lors du
téléchargement de ghcr.io/akvorado/akvorado
, Docker sélectionne la meilleure
image pour le CPU actuel. Sur x86-64, il y a deux choix. Si votre CPU est assez
récent, Docker télécharge linux/amd64/v3
. Cette version
contient des optimisations supplémentaires et devrait s’exécuter plus rapidement
que la version linux/amd64
. Il serait intéressant de livrer une image pour
linux/arm64/v8.2
, mais Docker ne supporte pas encore le même mécanisme pour
AArch64 (792808).
Go#
Cette version inclut de nombreux changements liés à Go mais non visibles pour les utilisateurs.
Chaîne de compilation#
Par le passé, Akvorado supportait les deux dernières versions de Go, empêchant
l’utilisation immédiate des dernières améliorations. L’objectif était de
permettre aux utilisateurs de distributions stables d’utiliser les versions de
Go fournies avec leur distribution pour compiler Akvorado. Cependant, cela
pouvait être frustrant quand des fonctionnalités intéressantes, comme go tool
,
étaient publiées. Akvorado 2.0 nécessite Go 1.25 (77306d) mais peut être
compilé avec des chaînes de compilation plus anciennes en téléchargeant
automatiquement une version plus récente (94fb1c).5 Les utilisateurs
peuvent toujours modifier GOTOOLCHAIN
pour annuler cette décision. La chaîne
de compilation recommandée est mise à jour hebdomadairement via la CI pour
s’assurer d’obtenir la dernière version mineure (5b11ec). Ce changement
simplifie aussi les mises à jour vers des versions plus récentes : seul go.mod
doit être mis à jour.
Grâce à ce changement, Akvorado utilise maintenant wg.Go()
(77306d) et
j’ai commencé à convertir certains tests unitaires vers le nouveau paquet
test/synctest
(bd787e, 7016d8 et 159085).
Tests#
Pour tester l’égalité, j’utilise une fonction Diff()
qui affiche les
différences en cas d’échec :
got := input.Keys() expected := []int{1, 2, 3} if diff := helpers.Diff(got, expected); diff != "" { t.Fatalf("Keys() (-got, +want):\n%s", diff) }
Cette fonction utilise kylelemons/godebug
. Ce paquet
n’est plus maintenu et présente des lacunes : par exemple, par défaut, elle ne
compare pas les champs privés des structures, ce qui peut causer des tests qui
réussissent de façon inattendue. Je l’ai remplacé par
google/go-cmp
, qui est plus strict et formatte la différence
de manière plus lisible (e2f1df).
Un autre paquet pour Kafka#
Un autre changement est le passage de Sarama à franz-go pour interagir avec Kafka (756e4a et 2d26c5). La principale motivation pour ce changement est de travailler avec un meilleur modèle de concurrence. Sarama repose fortement sur les canaux et il est difficile de savoir quand un objet n’est plus utilisé. franz-go utilise une approche plus moderne6 et performante avec des fonctions de rappel. Il est également livré avec un paquet pour lancer de faux clusters Kafka, ce qui est plus pratique que les fonctions de simulation fournies par Sarama.
Table de routage améliorée pour BMP#
Pour stocker les routes, le composant BMP utilisait
kentik/patricia
, une implémentation d’un arbre patricia
axée sur la réduction de la pression sur le ramasse-miettes.
gaissmai/bart
est une alternative plus récente utilisant une
adaptation de l’algorithme ART de Donald Kunth
qui promet de meilleures performances et les fournit :
recherches 90 % plus rapides et insertions 27 % plus rapides (92ee2e
et fdb65c).
Contrairement à kentik/patricia
, gaissmai/bart
n’aide pas à stocker
efficacement les valeurs attachées à chaque préfixe. J’ai adapté la même
approche utilisée par kentik/patricia
pour stocker les listes de routes pour
chaque préfixe : stocker un index 32 bits pour chaque préfixe, et l’utiliser
pour construire un index 64 bits pour rechercher les routes dans un tableau
associatif, qui est une structure particulièrement efficace de Go.
gaissmai/bart
supporte également une version sans verrous de la table de
routage, mais ce n’est pas aussi simple car nous aurions besoin d’étendre cela
au tableau stockant les routes et au mécanisme d’internement. J’ai également
tenté d’utiliser le nouveau paquet unique
de Go pour remplacer le paquet
intern
inclus dans Akvorado, mais les performances étaient pires7.
Divers#
Les versions précédentes d’Akvorado utilisaient un encodeur Protobuf
personnalisé pour les performances et la flexibilité.
Avec l’introduction du service outlet, Akvorado n’a besoin que d’un schéma
statique simple, donc ce code a été supprimé. Cependant, il est possible
d’améliorer les performances avec
planetscale/vtprotobuf
(e49a74 et 8b580f).
De plus, la dépendance à protoc
, un programme C++, était quelque peu
ennuyeuse. Par conséquent, Akvorado utilise maintenant buf, écrit en Go,
pour convertir un schéma Protobuf en code Go (f4c879).
Une autre petite optimisation pour réduire la taille du binaire d’Akvorado de 10 MB a consisté à compresser les ressources statiques intégrées dans Akvorado dans un fichier ZIP. Cela inclut la base des numéros d’AS, ainsi que les images SVG pour la documentation. Un bout de code rend ce changement transparent (b1d638 et e69b91).
JavaScript#
Récemment, deux grandes attaques de la chaîne d’approvisionnement ont frappé
l’écosystème JavaScript : une affectant les paquets populaires chalk
et
debug
et une autre impactant le paquet populaire
@ctrl/tinycolor
. Ces attaques existent aussi dans d’autres
écosystèmes, mais JavaScript est une cible privilégiée en raison de l’usage
intensif de petites dépendances. La version précédente d’Akvorado reposait sur
653 dépendances.
npm-run-all
a été supprimé (3424e8, 132 dépendances). patch-package
a
été supprimé (625805 et e85ff0, 69 dépendances) en déplaçant les
définitions TypeScript manquantes vers env.d.ts
. eslint
a été remplacé par
oxlint, un linter écrit en Rust (97fd8c, 125 dépendances, plugins
inclus).
J’ai basculé de npm
vers Pnpm, un gestionnaire de paquets alternatif
(fce383). Pnpm n’exécute pas les scripts d’installation par défaut8
et empêche l’installation de paquets trop récents. Il est également
significativement plus rapide9. Contrairement à npm
, il n’est pas livré
avec Node.js. Cependant, Node.js contient Corepack, qui nous permet
d’utiliser Pnpm sans l’installer. Pnpm peut également lister les licences
utilisées par chaque dépendance, supprimant le besoin d’utiliser
license-compliance
(a35ca8, 42 dépendances).
Pour des améliorations de vitesse supplémentaires, au-delà du passage à Pnpm et Oxlint, Vite a été remplacé par sa version Rolldown plus rapide (463827).
Après ces changements, Akvorado ne nécessite plus « que » 225 dépendances. 😱
Prochaines étapes#
J’aimerais intégrer trois fonctionnalités dans la prochaine version d’Akvorado :
-
Ajouter des tableaux de bord Grafana pour compléter la pile d’observabilité. Voir le ticket #1906 pour les détails.
-
Intégrer le plugin Grafana d’OVH en fournissant une API stable pour de telles intégrations. La console web d’Akvorado resterait utile pour parcourir les résultats, mais si vous voulez créer et partager des tableaux de bord, vous devrez passer à Grafana. Voir le ticket #1895.
-
Déplacer une partie du travail actuellement fait dans ClickHouse (dictionnaires personnalisés, GeoIP et ajout d’informations liées aux IP) vers le service outlet. Cela devrait donner plus de flexibilité pour ajouter des fonctionnalités comme celle demandée dans le ticket #1030.
J’ai commencé à travailler sur la division de l’inlet il y a plus d’un an. J’ai retrouvé de la motivation ces derniers mois, en partie grâce à Claude Code, que j’ai utilisé comme canard en plastique. Presqu’aucun code produit n’a été conservé10 : c’est comme un stagiaire qui n’apprend pas. D’ailleurs, il est aussi flemmard. Pour la traduction en Français, il a fallu insister de nombreuses fois pour traduire l’intégralité de l’article. 🦆
-
De nombreuses tentatives ont été faites pour rendre le composant BMP à la fois performant et non bloquant. Voir par exemple PR #254, PR #255 et PR #278. Malgré ces efforts, ce composant restait problématique pour la plupart des utilisateurs. Voir le ticket #1461 comme exemple. ↩︎
-
Certaines fonctionnalités ont été déplacées vers ClickHouse pour éviter le coût de traitement dans l’inlet. Voir par exemple PR #1059. ↩︎
-
C’est le plus gros changement :
↩︎$ git show --shortstat ac68c5970e2c | tail -1 231 files changed, 6474 insertions(+), 3877 deletions(-)
-
Broadcom est connu pour ses actions hostiles aux utilisateurs. Regardez ce qui s’est passé avec VMWare. ↩︎
-
En tant que développeur Debian, je n’aime pas ces mécanismes qui contournent le gestionnaire de paquets de la distribution. J’ai finalement changé d’avis quand Go 1.25 a passé un mois dans la file NEW de Debian, un mécanisme arbitraire que je n’aime pas du tout. ↩︎
-
Aux débuts de Go, l’utilisation des canaux était fortement encouragée. Sarama a été conçu pendant cette période. Quelques années plus tard, une approche plus nuancée a émergé. Voir notamment « Go channels are bad and you should feel bad ». ↩︎
-
Cela devrait être étudié davantage, mais ma théorie est que le paquet
intern
utilise des entiers 32 bits, tandis queunique
utilise des pointeurs 64 bits. Voir le commit 74e5ac. ↩︎ -
Une alternative encore plus rapide est Bun, mais elle est moins disponible. ↩︎
-
Les exceptions sont une partie du code pour les blocs d’avertissement, le code pour replier la table des matières et une partie de la documentation. ↩︎