Aller au contenu

Blog

Annonce : Nouvel agent de cluster basé sur Rust


tl;dr Nous avons migré notre agent de cluster de Go vers Rust et il est maintenant plus petit et utilise moins de mémoire. Pour utiliser le nouvel agent de cluster, il suffit de mettre à jour vers le dernier release (cli/v0.8.2, helm/v0.15.2). Vous pouvez également l’essayer en direct ici.


Récemment, nous avons décidé de migrer notre agent de cluster de Go vers Rust. Je suis maintenant heureux de dire que la réécriture est terminée et le résultat est une image d’agent de cluster 57% plus petite (10 Mo) qui utilise 70% moins de mémoire (~3 Mo) tout en utilisant toujours un minimum de CPU (~0,1%).

La première version de Kubetail était conçue pour s’exécuter dans le cluster et exposer les logs aux utilisateurs via un navigateur web. Pour cette version, la principale responsabilité du backend était de faire des requêtes à l’API Kubernetes et de relayer les réponses au frontend en temps réel. Après avoir examiné quelques options dont Python et JavaScript, j’ai décidé de l’écrire en Go car il est bien supporté par l’API Kubernetes, a un excellent support du multithreading et produit des exécutables rapides et de petites images Docker.

La prochaine version de Kubetail a ajouté l’outil CLI kubetail capable d’exécuter le tableau de bord web localement. Pour implémenter l’outil CLI, j’ai de nouveau choisi Go car le langage a de bonnes bibliothèques d’interaction CLI (merci spf13 !), un excellent support multiplateforme et surtout parce que cela m’a permis de réutiliser l’application web basée sur Go utilisée par le tableau de bord dans le cluster.

Jusque-là, Kubetail ne récupérait les logs qu’en utilisant l’API Kubernetes. Mais lorsque j’ai voulu ajouter de nouvelles fonctionnalités telles que les tailles de fichiers de logs et les horodatages des derniers événements — des données non exposées par l’API Kubernetes — j’ai réalisé que nous avions besoin d’un agent avec un accès direct aux fichiers de logs bruts sur chaque nœud. Bien que j’aurais pu utiliser un autre langage, j’ai de nouveau choisi Go car c’était le langage que je connaissais le mieux et qui nous avait bien servi jusqu’ici. Heureusement, il avait également un excellent support pour gRPC, qui était un choix naturel pour l’interface de l’agent.

Compte tenu de l’ensemble de fonctionnalités de l’application à ce moment-là, j’étais très satisfait de mon choix initial de Go car il nous avait bien servi sur le bureau ainsi que dans le cluster. Puis j’ai commencé à réfléchir à la façon d’implémenter notre fonctionnalité la plus demandée : la recherche de logs.

Lorsque j’ai commencé à réfléchir à la recherche de logs, je savais que je voulais utiliser grep plutôt qu’un index de texte intégral car c’est suffisant pour la plupart des cas d’utilisation et je ne voulais pas que nos utilisateurs supportent le coût en ressources de la maintenance d’un index de texte intégral. En même temps, j’utilisais rg personnellement pour grep des logs depuis un moment et j’étais impressionné par sa vitesse, donc lorsque j’ai commencé à chercher une solution de grep, j’étais curieux de savoir si je pouvais l’utiliser d’une manière ou d’une autre. C’est là que j’ai réalisé qu’il était disponible en tant que bibliothèque, mais avec une condition — il était écrit en Rust.

Avant d’écrire du code personnalisé, j’ai exploré l’idée d’utiliser rg comme exécutable externe en utilisant exec.Command pour interagir avec lui via stdin/stdout. Cela a fonctionné pour les cas d’utilisation de base, mais c’est devenu difficile à gérer à mesure que j’ai commencé à ajouter des fonctionnalités personnalisées comme les filtres temporels, la gestion des séquences d’échappement ANSI et le support des lignes formatées en JSON. J’ai donc décidé de plonger et d’écrire un grepper de fichiers de logs personnalisé. J’ai brièvement exploré l’utilisation de Go, mais j’ai finalement décidé que pour des raisons de performance et de robustesse, je voulais utiliser la bibliothèque derrière rg, ripgrep, ce qui signifiait que le code devait être écrit en Rust.

À ce moment-là, je ne voulais pas réécrire tout l’agent de cluster en Rust, j’ai donc cherché des moyens d’appeler Rust depuis Go (ex. rustgo) et j’ai opté pour garder le code Rust personnalisé en tant qu’exécutable séparé et l’appeler depuis Go en utilisant exec.Command. Pour rendre le code aussi simple que possible, j’ai utilisé un schéma protocol buffers partagé avec la sérialisation/désérialisation implémentée à l’interface stdin/stdout.

Après le lancement de la fonctionnalité de recherche, notre communauté a commencé à croître et j’ai rencontré deux développeurs qui avaient beaucoup plus d’expérience Rust que moi, Christopher Valerio (freexploit) et Giannis Karagiannis (gikaragia). Au départ, ils ont commencé à apporter des améliorations au code Rust et à mesure qu’ils se familiarisaient avec la base de code, nous avons commencé à parler de la façon d’éliminer l’inadéquation d’impédance entre Go et Rust dans l’agent de cluster. Séparément de la fonctionnalité de recherche, l’agent de cluster s’exécute sur chaque nœud d’un cluster, il est donc important qu’il soit aussi performant et léger que possible, ce qui est exactement le cas d’utilisation pour Rust. Avec ces idées dans l’air, nous avons eu une réunion de communauté où nous avons discuté de l’idée de migrer tout l’agent vers Rust. Ils ont dit qu’ils étaient impatients de travailler dessus, alors j’ai dit : faisons-le !

Une fois la décision prise, Christopher et Giannis se sont mis au travail. Christopher a défini l’architecture initiale de haut niveau du projet et a créé quelques issues initiales sur GitHub. Ensuite, Giannis est intervenu et a commencé à implémenter l’ensemble des fonctionnalités, à écrire des tests et à créer d’autres issues pour que nous puissions obtenir l’aide d’autres contributeurs. Giannis a réussi à atteindre la parité de fonctionnalités avec l’agent de cluster basé sur Go en seulement quelques semaines et après environ une semaine de tests supplémentaires, nous avons décidé que le code était prêt à être fusionné dans main.

Je n’ai commencé à apprendre Rust que récemment, donc Claude Code et Codex CLI ont été inestimables pour m’aider à réviser les pull requests de Giannis. Il utilisait également les chatbots de son côté, c’était donc un véritable partenariat humain-bot médié par les pull requests GitHub. L’un des principaux avantages que nous avions était que comme l’agent utilise une interface gRPC bien définie, nous avons pu réutiliser le schéma protocol buffers et ensuite simplement basculer quand l’agent basé sur Rust a atteint la parité de fonctionnalités avec la version basée sur Go. Pour construire le serveur gRPC basé sur Rust, nous avons utilisé tonic qui était simple et n’avait que de petites différences par rapport au serveur gRPC basé sur Go.

Le résultat final est une image d’agent de cluster 57% plus petite (10 Mo) qui utilise 70% moins de mémoire (~3 Mo) tout en utilisant toujours un minimum de CPU (~0,1%). De plus, le code est beaucoup plus facile à utiliser maintenant car il est tout dans le même langage.

Notre mission est de donner aux utilisateurs accès à de puissants outils de logging dans un package simple et léger, mais l’API Kubernetes a des capacités de logging limitées, donc débloquer des fonctionnalités plus avancées nécessite un accès direct aux fichiers de logs bruts sur chaque nœud. C’est là qu’intervient l’agent de cluster — c’est la base de tout ce que nous voulons construire ensuite.

Bien sûr, les utilisateurs sont compréhensiblement prudents quant à l’installation d’agents dans leurs clusters. En plus d’être utiles, les agents doivent également être petits, rapides et sécurisés. La migration vers Rust est notre réponse à ces exigences. En réduisant la taille de l’image de plus de moitié et en réduisant l’utilisation mémoire de 70%, nous avons rendu l’agent Kubetail suffisamment petit pour être déployé même dans les environnements les plus contraints en ressources.

Mais ce n’est que le début. Rust nous permettra de repousser les limites de ce qui peut être fait à l’intérieur du cluster en temps réel, directement avec des fichiers sur disque, tout en utilisant le moins de CPU et de mémoire possible. En ce moment, notre focus est sur les logs, mais la même approche s’applique aux métriques, aux notifications et à d’autres types de données d’observabilité.

Nous sommes enthousiastes à propos de ce qui suit et nous serions ravis que vous en fassiez partie. Si vous aimez ce que nous faisons et que vous souhaitez contribuer du code ou partager des retours en tant qu’utilisateur, rejoignez-nous sur Discord.

Merci, OCV

Si nous réussissons notre mission de construire une nouvelle couche de logging pour Kubernetes qui fonctionne dans chaque cluster, ce sera en grande partie grâce à Open Core Ventures (OCV) et à leur programme Catalyst dirigé par Alex Smith.

OCV est une société de capital-risque fondée par Sid Sijbandij (cofondateur de GitLab) qui finance des entreprises open source en phase initiale fondées sur les principes Open Core. Dans le cadre de leurs efforts de sensibilisation à l’open source, ils ont lancé un programme appelé Catalyst qui offre une petite bourse et beaucoup de mentorat aux mainteneurs de projets open source souhaitant développer leurs projets. Au cours de 12 semaines, ils vous apprennent à construire une communauté open source et à commercialiser efficacement votre produit afin qu’il puisse croître et trouver sa traction. Kubetail a récemment participé au programme, et pour nous ce fut un changement de jeu.

Avant de travailler sur Kubetail, j’ai cofondé une startup appelée Octopart qui faisait partie du batch W07 de Y Combinator. En tant que startup, nous ne nous en sommes pas trop mal sortis, alors quand j’ai commencé à travailler sur Kubetail, j’ai suivi une approche similaire : je me suis concentré sur la construction d’un MVP et dès qu’il fut prêt, je l’ai posté sur Hacker News (HN). Heureusement, le post a atteint la première page pendant quelques heures et nous avons terminé avec quelques centaines d’étoiles GitHub et un petit nombre d’utilisateurs réels (~10).

Ensuite, Kubetail est entré dans le Creux du Désespoir. C’est la partie de la courbe d’une startup après votre lancement initial quand le buzz retombe et que vous vous retrouvez avec une poignée d’utilisateurs, aucune validation externe, et seulement votre optimisme intérieur pour vous maintenir à flot. Je n’étais pas étranger au creux, alors j’ai fait ce que j’avais fait précédemment et j’ai juste mis la tête dans le travail et continué à coder.

Pendant cette période, je me suis concentré sur la mise à disposition de notre MVP (le Tableau de bord Kubetail) de manière aussi simple que possible. En réponse aux retours de quelques premiers utilisateurs, j’ai modifié l’architecture pour qu’il puisse s’exécuter sur le bureau d’un utilisateur en plus de fonctionner dans le cluster. Je me suis également concentré sur la facilitation de la recherche et du téléchargement de l’application via Homebrew et d’autres référentiels de packages. Et en arrière-plan, je me suis concentré sur l’implémentation de notre fonctionnalité la plus demandée, la recherche.

Pendant plus d’un an, j’ai travaillé seul tandis que la croissance du projet stagnait. Puis j’ai reçu un e-mail inattendu de OCV qui a conduit à notre acceptation dans le programme de parrainage Catalyst qui a tout changé.

En tant que participant au Catalyst, j’ai reçu un mentorat pratique d’Alex et de l’équipe OCV qui s’est avéré inestimable pour moi en tant que quelqu’un ayant des compétences techniques mais sans aucune expérience en construction de communauté ou en gestion d’un projet open source. Avec l’aide de Catalyst, j’ai modifié ma routine de pur codage pour équilibrer le développement avec l’engagement communautaire et le soutien aux contributeurs.

Avant de faire Catalyst, Kubetail n’avait aucune communauté. Nous avions un serveur Discord mais j’étais le seul dedans, travaillant seul chaque jour. Ensuite, Alex m’a guidé semaine après semaine en suggérant des choses sur lesquelles me concentrer et de nouvelles choses à essayer. Avec son aide, Kubetail est passé d’environ 300 étoiles à plus de 1 300 en l’espace de 12 semaines. Et encore plus significativement, la communauté a décollé. Avant Catalyst, nous avions 3 contributeurs et aucun utilisateur sur Discord. Maintenant nous avons 35 contributeurs et une communauté Discord dynamique avec 61 membres.

Pendant Catalyst, tout s’est mis en place et nous étions enfin prêts à lancer notre fonctionnalité de recherche de logs, mais cette fois avec une communauté derrière nous et le mentorat d’OCV pour nous aider à promouvoir la fonctionnalité auprès de nouveaux utilisateurs. Cette fois quand nous avons annoncé la fonctionnalité, Kubetail s’est retrouvé sur la première page de HN pendant plus d’un jour et a été vu par des dizaines de milliers d’utilisateurs sur Reddit et Twitter. Cela s’est traduit par une augmentation des téléchargements mensuels de moins de 100 à plus de 400 et a transformé Kubetail d’un petit projet passion en une entreprise ambitieuse portée par la communauté. Le moment fort de Catalyst pour moi s’est produit aux alentours de cette époque quand j’ai pu partager notre étape de 1 000 étoiles GitHub avec un nouveau mainteneur Kubetail (rxinui) et le reste de notre communauté.

Célébration Discord

Je ne me fais aucune illusion sur la difficulté du chemin à parcourir. Nous travaillons sur un problème technique difficile et nous opérons dans un espace avec de nombreuses entreprises bien financées comme Datadog, Grafana, New Relic et ClickHouse qui ont déjà l’attention de la plupart de nos utilisateurs potentiels. De plus, les utilisateurs s’attendent déjà à beaucoup de fonctionnalités des outils d’observabilité, nous aurons donc besoin de nombreux ingénieurs talentueux pour accomplir le travail, et pour cela nous avons besoin de ressources que nous n’avons pas encore trouvé comment obtenir.

Cependant, je n’ai jamais été aussi optimiste quant à nos chances de succès. Chaque fois que j’apprends quelque chose de nouveau d’un de nos contributeurs expérimentés ou que je vois à quel point nos jeunes contributeurs sont enthousiastes quand l’un de leurs pull requests est fusionné, cela m’énergise. Chaque fois que je révise un pull request d’un utilisateur résolvant son propre problème ou que j’engage une conversation avec quelqu’un à propos d’une nouvelle fonctionnalité, cela me rend encore plus confiant que j’ai choisi la meilleure façon de construire un produit — ensemble en tant que partie d’une communauté open source.

Pour moi, un projet open source est comme une marmite qui peut produire des produits de haute qualité que les utilisateurs adorent utiliser et qui sont bons pour eux aussi. Mais bien sûr, l’ingrédient magique derrière chaque produit est la communauté, et quand il s’agit de la communauté de Kubetail, je dois ici donner un grand merci à Alex et au reste de l’équipe OCV.

Annonce : Recherche de logs en temps réel pour Kubernetes

Depuis le lancement de Kubetail l’année dernière, la fonctionnalité la plus demandée a été la recherche dans les logs. Je suis heureux d’annoncer que nous avons enfin la recherche de logs disponible dans notre dernier release officiel (cli/v0.4.3, helm/v0.10.1). Vous pouvez la voir en action ici :

https://www.kubetail.com/demo

L’implémentation de la recherche a pris du temps car l’API Kubernetes ne la supporte pas nativement, nous avons donc dû construire cette fonctionnalité de zéro. Nous avons envisagé de l’implémenter rapidement en utilisant un grep côté client, mais cela n’aurait pas été une bonne expérience utilisateur car chaque recherche pourrait potentiellement déclencher un téléchargement complet de nombreux fichiers de logs, ce qui aurait été lent et gourmand en bande passante. Il existe des moyens de contourner cela, mais cela aurait nécessité une saisie supplémentaire de l’utilisateur, ce qui n’est pas non plus une bonne expérience.

À la place, nous avons implémenté la recherche en créant un exécutable personnalisé basé sur Rust qui encapsule ripgrep. Pourquoi Rust ? Parce que c’est incroyablement rapide. La majeure partie du backend Kubetail est écrite en Go, mais pour ce composant bas niveau qui lit les fichiers de logs sur disque, nous voulions qu’il soit le plus rapide possible. Le résultat : un scan complet d’un fichier de 1 Go prend environ 250 ms. À chaque requête, l’exécutable ne scanne que les fichiers de logs de conteneur pertinents sur chaque nœud et ne transmet que les lignes correspondantes à votre navigateur. La plupart des requêtes peuvent s’arrêter tôt, ce qui signifie qu’elles peuvent même retourner des résultats avant d’effectuer un scan complet. Vous pouvez considérer la recherche Kubetail comme un « grep distant » pour vos logs Kubernetes. Vous n’avez plus besoin de télécharger un fichier de log entier juste pour l’explorer localement.

Pour activer la recherche, vous devez installer les « Cluster Resources » de Kubetail dans votre cluster. Cela peut être fait facilement en cliquant sur « Install » dans l’interface graphique ou en appelant kubetail cluster install depuis la CLI. Cette action déploiera un Kubetail Cluster Agent sur chaque nœud ainsi qu’une instance de la Kubetail Cluster API. Lorsque la Cluster API est disponible, le tableau de bord l’utilisera pour accéder aux fonctionnalités personnalisées Kubetail comme la recherche sur les nœuds. Sinon, le tableau de bord désactivera ces fonctionnalités dans l’interface graphique et se repliera sur l’utilisation de l’API Kubernetes.

Nous ne faisons que commencer avec la recherche de logs, et il y a beaucoup de place pour l’améliorer. Si vous êtes un développeur Rust, Go ou React — ou un designer UI qui aime les logs — venez nous aider à construire la plateforme de logging open-source la plus conviviale pour Kubernetes. Rejoignez notre communauté sur Discord !

Andres

Annonce de la commande "logs" de la CLI Kubetail

Pour faciliter la surveillance et le débogage des workloads multi-conteneurs sur Kubernetes, nous avons ajouté une nouvelle commande logs à l’outil CLI Kubetail. Avec la nouvelle commande logs, vous pouvez désormais effectuer un grep de vos logs de workloads Kubernetes en temps réel depuis votre terminal. Vous pouvez également filtrer par plage horaire et d’autres propriétés sources telles que le nœud et la zone.

Pour installer l’outil CLI Kubetail, vous pouvez le télécharger depuis la page des releases ou utiliser Homebrew :

Fenêtre de terminal
brew install kubetail

Voici quelques exemples de ce que vous pouvez faire avec la nouvelle commande logs :

Fenêtre de terminal
# Tail 'web' deployment in the 'default' namespace
kubetail logs deployments/web
# Tail 'web' deployment in the 'frontend' namespace
kubetail logs frontend:deployments/web
# Return last 100 records
kubetail logs deployments/web --tail=100
# Return first 100 records
kubetail logs deployments/web --head=100
# Stream new records
kubetail logs deployments/web --follow
# Return all records
kubetail logs deployments/web --all
# Return first 10 records starting from 30 minutes ago
kubetail logs deployments/web --since PT30M
# Return last 10 records leading up to 30 minutes ago
kubetail logs deployments/web --until PT30M
# Return first 10 records between two exact timestamps
kubetail logs deployments/web --since 2006-01-02T15:04:05Z07:00 --until 2007-01-02T15:04:05Z07:00
# Return last 10 records that match "GET /about"
kubetail logs deployments/web --grep "GET /about" --force
# Return first 10 records that match "GET /about"
kubetail logs deployments/web --grep "GET /about" --head --force
# Return last 10 records that match "GET /about" or "GET /contact"
kubetail logs deployments/web --grep "GET /(about|contact)" --force
# Stream new records that match "GET /about"
kubetail logs deployments/web --grep "GET /about" --follow --force

La commande logs utilise votre fichier de configuration kube local pour s’authentifier auprès de votre cluster. Pour changer de cluster, il suffit de modifier votre contexte kube config. Vous pouvez également utiliser le flag --kube-context :

Fenêtre de terminal
kubetail logs --kube-context minikube deployments/web

Vous remarquerez que pour utiliser --grep, vous devez également utiliser --force. C’est parce que le filtrage est effectué côté client, ce qui signifie que l’outil téléchargera en continu des logs depuis votre cluster jusqu’à ce que le nombre souhaité de correspondances soit trouvé. Cela pourrait entraîner des téléchargements inattendument volumineux, c’est pourquoi nous avons ajouté une vérification de flag secondaire. Nous travaillons sur une nouvelle fonctionnalité pour contourner ce problème.

Essayez la nouvelle commande logs et dites-nous ce que vous en pensez !