Pular para o conteúdo

Blog

Anunciando: Novo Cluster Agent baseado em Rust


tl;dr Migramos nosso cluster agent de Go para Rust e agora ele é menor e usa menos memória. Para usar o novo cluster agent, basta atualizar para o último release (cli/v0.8.2, helm/v0.15.2). Você também pode experimentá-lo ao vivo aqui.


Recentemente, decidimos migrar nosso cluster agent de Go para Rust. Agora tenho o prazer de dizer que a reescrita está completa e o resultado é uma imagem do cluster agent 57% menor (10MB) que usa 70% menos memória (~3MB) enquanto ainda usa CPU mínima (~0.1%).

A primeira versão do Kubetail foi projetada para rodar dentro do cluster e expor logs aos usuários por meio de um navegador web. Para essa versão, a principal responsabilidade do backend era fazer requisições à API do Kubernetes e retransmitir as respostas ao frontend em tempo real. Depois de analisar algumas opções incluindo Python e JavaScript, decidi escrever em Go porque é bem suportado pela API do Kubernetes, tem excelente suporte a multithreading e produz executáveis rápidos e imagens Docker pequenas.

A próxima versão do Kubetail adicionou a ferramenta CLI kubetail capaz de rodar o dashboard web localmente. Para implementar a CLI escolhi Go novamente porque a linguagem tem boas bibliotecas de interação CLI (obrigado spf13!), excelente suporte multiplataforma e, o mais importante, porque permitiu reutilizar o app web baseado em Go usado pelo dashboard no cluster.

Até então, o Kubetail buscava logs apenas usando a API do Kubernetes. Mas quando quis adicionar novos recursos como tamanhos de arquivos de log e timestamps do último evento — dados não expostos pela API do Kubernetes — percebi que precisávamos de um agente com acesso direto aos arquivos de log brutos em cada nó. Embora pudesse ter usado outra linguagem, escolhi Go novamente porque era a linguagem que eu conhecia melhor e que nos tinha servido bem até então. Por sorte, também tinha excelente suporte para gRPC, que foi uma escolha natural para a interface do agente.

Dado o conjunto de recursos do app naquele momento, fiquei muito satisfeito com minha escolha original de Go, pois nos serviu bem tanto no desktop quanto no cluster. Então comecei a pensar em como implementar nosso recurso mais solicitado: pesquisa de logs.

Quando comecei a pensar em pesquisa de logs, sabia que queria usar grep em vez de um índice de texto completo porque é suficiente para a maioria dos casos de uso e não queria que nossos usuários arcassem com o custo de manter um índice de texto completo. Ao mesmo tempo, eu usava rg pessoalmente para fazer grep de logs há algum tempo e estava impressionado com sua velocidade, então quando comecei a procurar uma solução de grep fiquei curioso se poderia usá-lo de alguma forma. Foi então que percebi que estava disponível como biblioteca, mas com uma condição — estava escrito em Rust.

Antes de escrever qualquer código customizado, explorei a ideia de usar rg como um executável externo usando exec.Command para interagir com ele via stdin/stdout. Isso funcionou para casos de uso básicos, mas ficou difícil de manejar à medida que comecei a adicionar recursos customizados como filtros de tempo, tratamento de sequências de escape ANSI e suporte para linhas formatadas em JSON. Então decidi mergulhar e escrever um grepper de arquivos de log customizado. Explorei brevemente usar Go, mas no final decidi que por razões de desempenho e robustez queria usar a biblioteca por trás do rg, ripgrep, o que significava que o código precisava ser escrito em Rust.

Na época, não queria reescrever todo o cluster agent em Rust, então procurei formas de chamar Rust a partir de Go (ex.: rustgo) e acabei mantendo o código Rust customizado como um executável separado e chamando-o a partir do Go usando exec.Command. Para manter o código o mais simples possível, usei um esquema de protocol buffers compartilhado com serialização/desserialização implementada na interface stdin/stdout.

Após lançar o recurso de pesquisa, nossa comunidade começou a crescer e conheci dois desenvolvedores com muito mais experiência em Rust do que eu, Christopher Valerio (freexploit) e Giannis Karagiannis (gikaragia). Inicialmente eles começaram a fazer melhorias no código Rust e à medida que foram ficando confortáveis com o código base, começamos a falar sobre como eliminar o descompasso de impedância entre Go e Rust no cluster agent. Separadamente do recurso de pesquisa, o cluster agent roda em cada nó do cluster, por isso é importante que seja o mais performático e leve possível, que é exatamente o caso de uso para o Rust. Com essas ideias no ar, tivemos uma reunião da comunidade onde discutimos a ideia de migrar todo o agente para Rust. Eles disseram que estavam animados para trabalhar nisso, então eu disse: vamos fazer!

Uma vez tomada a decisão, Christopher e Giannis foram trabalhar. Christopher definiu a arquitetura de alto nível inicial do projeto e criou algumas issues iniciais no GitHub. Então Giannis entrou e começou a implementar o conjunto de recursos, escrever testes e criar mais issues para que pudéssemos obter ajuda de outros contribuidores. Giannis conseguiu atingir paridade de recursos com o cluster agent baseado em Go em apenas algumas semanas e depois de mais ou menos uma semana de testes decidimos que o código estava pronto para fazer merge na main.

Só recentemente comecei a aprender Rust, então Claude Code e Codex CLI foram inestimáveis para me ajudar a revisar os pull requests do Giannis. Ele também estava usando os chatbots do seu lado, então foi uma verdadeira parceria humano-bot mediada por pull requests do GitHub. Um dos principais benefícios que tivemos foi que como o agente usa uma interface gRPC bem definida, pudemos reutilizar o esquema de protocol buffers e então simplesmente mudar o switch quando o agente baseado em Rust atingiu paridade de recursos com a versão baseada em Go. Para construir o servidor gRPC baseado em Rust usamos tonic, que foi direto ao ponto e só teve pequenas diferenças em comparação com o servidor gRPC baseado em Go.

O resultado final é uma imagem do cluster agent 57% menor (10MB) que usa 70% menos memória (~3MB) enquanto ainda usa CPU mínima (~0.1%). Além disso, o código é muito mais fácil de trabalhar agora porque está todo na mesma linguagem.

Nossa missão é dar aos usuários acesso a ferramentas de logging poderosas em um pacote simples e leve, mas a API do Kubernetes tem capacidades de logging limitadas, então desbloquear recursos mais avançados requer acesso direto aos arquivos de log brutos em cada nó. É aí que o cluster agent entra — é a base para tudo que queremos construir a seguir.

Claro, os usuários compreensivelmente são cautelosos sobre instalar agentes em seus clusters. Além de ser úteis, os agentes também precisam ser pequenos, rápidos e seguros. A migração para Rust é nossa resposta a esses requisitos. Ao cortar o tamanho da imagem em mais da metade e reduzir o uso de memória em 70%, tornamos o agente do Kubetail pequeno o suficiente para ser implantado até mesmo nos ambientes com mais restrições de recursos.

Mas isso é apenas o começo. Rust nos permitirá ampliar os limites do que pode ser feito dentro do cluster em tempo real, diretamente com arquivos em disco, usando o mínimo de CPU e memória possível. Agora, nosso foco é em logs, mas a mesma abordagem se aplica a métricas, notificações e outros tipos de dados de observabilidade.

Estamos animados com o que vem a seguir e adoraríamos que você fizesse parte disso. Se você gosta do que estamos fazendo e quer contribuir com código ou compartilhar feedback como usuário, junte-se a nós no Discord.

Obrigado, OCV

Se conseguirmos cumprir nossa missão de construir uma nova camada de logging para Kubernetes que rode em todo cluster, será em grande parte graças à Open Core Ventures (OCV) e ao seu programa Catalyst liderado por Alex Smith.

A OCV é uma empresa de venture capital fundada por Sid Sijbandij (cofundador do GitLab) que financia empresas de código aberto em estágio inicial fundadas em princípios de Open Core. Como parte de seus esforços de divulgação de código aberto, eles criaram um programa chamado Catalyst que oferece uma pequena bolsa e muito mentoria a mantenedores de projetos de código aberto interessados em fazer seus projetos crescerem. Ao longo de 12 semanas, eles ensinam como construir uma comunidade de código aberto e como comercializar seu produto de forma eficaz para que possa crescer e encontrar tração. O Kubetail participou recentemente do programa, e para nós foi um divisor de águas.

Antes de trabalhar no Kubetail, fui cofundador de uma startup chamada Octopart que fez parte do batch W07 da Y Combinator. Não fomos mal como startup, então quando comecei a trabalhar no Kubetail segui uma abordagem similar: me concentrei em construir um MVP e assim que ficou pronto, publiquei no Hacker News (HN). Felizmente, o post chegou à primeira página por algumas horas e acabamos com algumas centenas de estrelas no GitHub e um pequeno número de usuários reais (~10).

Então o Kubetail entrou no Vale da Tristeza. Essa é a parte da curva de uma startup após seu lançamento inicial, quando o burburinho diminui e você fica com um punhado de usuários, sem validação externa, e só com seu próprio otimismo interno para continuar. Eu não era estranho ao vale, então fiz o que tinha feito antes: baixei a cabeça e continuei programando.

Durante esse período, me concentrei em tornar nosso MVP (o Kubetail Dashboard) o mais fácil de usar possível. Em resposta a alguns feedbacks de usuários iniciais, mudei a arquitetura para que pudesse rodar no desktop do usuário além de dentro do cluster. Também me concentrei em facilitar que os usuários encontrassem e baixassem o app via Homebrew e outros repositórios de pacotes. E em segundo plano, me concentrei em implementar nosso recurso mais solicitado, pesquisa.

Por mais de um ano, trabalhei sozinho enquanto o crescimento do projeto estagnava. Então recebi um e-mail inesperado da OCV que levou à nossa aceitação no programa de patrocínio Catalyst que mudou tudo.

Como parte do Catalyst, recebi mentoria prática de Alex e da equipe da OCV que se mostrou inestimável para mim como alguém com habilidades técnicas mas sem experiência em construção de comunidade ou gestão de um projeto de código aberto. Com a ajuda do Catalyst, mudei minha rotina de programação pura para equilibrar desenvolvimento com engajamento comunitário e suporte a contribuidores.

Antes de fazer o Catalyst, o Kubetail não tinha comunidade alguma. Tínhamos um servidor Discord mas eu era o único nele, trabalhando sozinho todo dia. Então Alex me guiou semana a semana sugerindo coisas em que me concentrar e novas coisas para tentar. Com sua ajuda, o Kubetail cresceu de cerca de 300 estrelas para mais de 1.300 em um período de 12 semanas. E ainda mais significativamente, a comunidade decolou. Antes do Catalyst, tínhamos 3 contribuidores e nenhum usuário no Discord. Agora temos 35 contribuidores e uma vibrante comunidade Discord com 61 membros.

Durante o Catalyst, tudo se encaixou e finalmente estávamos prontos para lançar nosso recurso de pesquisa de logs, mas desta vez com uma comunidade por trás de nós e a mentoria da OCV para nos ajudar a comercializar o recurso para novos usuários. Desta vez quando anunciamos o recurso, o Kubetail ficou na primeira página do HN por mais de um dia e foi visto por dezenas de milhares de usuários no Reddit e Twitter. Isso se traduziu em um aumento nos downloads mensais de menos de 100 para mais de 400 e transformou o Kubetail de um pequeno projeto de paixão em um empreendimento ambicioso conduzido pela comunidade. O destaque do Catalyst para mim ocorreu por volta dessa época, quando pude compartilhar nosso marco de 1.000 estrelas no GitHub com um novo mantenedor do Kubetail (rxinui) e o resto da nossa comunidade.

Celebração no Discord

Não tenho ilusões sobre o quão difícil é o caminho à frente. Estamos trabalhando em um problema técnico difícil e operando em um espaço com muitas empresas bem financiadas como Datadog, Grafana, New Relic e ClickHouse que já têm a atenção da maioria dos nossos usuários potenciais. Além disso, os usuários já esperam muitos recursos de ferramentas de observabilidade, então precisaremos de muitos engenheiros talentosos para fazer o trabalho, e para isso precisamos de recursos que ainda não descobrimos como obter.

No entanto, nunca estive mais otimista sobre nossas chances de sucesso. Cada vez que aprendo algo novo com um de nossos contribuidores experientes ou vejo como nossos contribuidores mais jovens ficam animados quando um de seus pull requests é mergeado, isso me energiza. Cada vez que reviso um pull request de um usuário resolvendo seu próprio problema ou entro em uma conversa com alguém sobre um novo recurso, isso me faz ainda mais confiante de que escolhi a melhor maneira de construir um produto — juntos como parte de uma comunidade de código aberto.

Para mim, um projeto de código aberto é como uma panela que pode produzir produtos de alta qualidade que os usuários adoram usar e que são saudáveis para eles também. Mas é claro que o ingrediente mágico por trás de cada produto é a comunidade, e quando se trata da comunidade do Kubetail, tenho que dar um grande obrigado ao Alex e ao resto da equipe da OCV.

Anunciando: Pesquisa de logs em tempo real para Kubernetes

Desde o lançamento do Kubetail no ano passado, o recurso mais solicitado foi a pesquisa de logs. Agora tenho o prazer de informar que finalmente temos pesquisa de logs disponível no nosso último release oficial (cli/v0.4.3, helm/v0.10.1). Você pode vê-la em ação aqui:

https://www.kubetail.com/demo

A implementação da pesquisa levou tempo porque a API do Kubernetes não a suporta nativamente, então tivemos que construir o recurso do zero. Consideramos implementá-la rapidamente usando grep no lado do cliente, mas isso não seria uma boa experiência de usuário porque cada pesquisa poderia potencialmente disparar um download completo de muitos arquivos de log, o que seria lento e consumiria muita largura de banda. Existem formas de contornar isso, mas exigiria entrada adicional do usuário, o que também não é uma boa experiência.

Em vez disso, implementamos a pesquisa criando um executável customizado baseado em Rust que encapsula o ripgrep. Por que Rust? Porque é incrivelmente rápido. A maior parte do backend do Kubetail é escrita em Go, mas para este componente de baixo nível que lê arquivos de log em disco, queríamos que fosse o mais rápido possível. O resultado: um scan completo de um arquivo de 1GB leva cerca de 250ms. Em cada consulta, o executável escaneia apenas os arquivos de log do contêiner relevantes em cada nó e transmite de volta apenas as linhas correspondentes para o seu navegador. A maioria das consultas pode parar cedo, o que significa que podem retornar resultados antes mesmo de completar um scan completo. Você pode pensar na pesquisa do Kubetail como “grep remoto” para seus logs do Kubernetes. Agora você não precisa baixar um arquivo de log inteiro apenas para fazer grep localmente.

Para habilitar a pesquisa, você precisa instalar os “Cluster Resources” do Kubetail no seu cluster. Isso pode ser feito facilmente clicando em “Install” na GUI ou chamando kubetail cluster install na CLI. Essa ação implantará um Kubetail Cluster Agent em cada nó, bem como uma instância da Kubetail Cluster API. Quando a Cluster API estiver disponível, o dashboard a usará para acessar recursos customizados do Kubetail, como pesquisa nos nós. Caso contrário, o dashboard desabilitará esses recursos na GUI e usará a API do Kubernetes como fallback.

Estamos apenas começando com a pesquisa de logs e há muito espaço para melhorá-la. Se você é um desenvolvedor Rust, Go ou React — ou um designer de UI que ama logs — venha nos ajudar a construir a plataforma de logging open-source mais amigável para Kubernetes. Junte-se à nossa comunidade no Discord!

Andres

Anunciando o comando "logs" da CLI do Kubetail

Para facilitar o monitoramento e a depuração de workloads com múltiplos contêineres no Kubernetes, adicionamos um novo comando logs à ferramenta CLI do Kubetail. Com o novo comando logs, agora é possível fazer grep dos logs dos seus workloads do Kubernetes em tempo real direto do terminal. Você também pode filtrar por tempo e outras propriedades da fonte, como nó e zona.

Para instalar a ferramenta CLI do Kubetail, você pode fazer o download pela página de releases ou usar o Homebrew:

Terminal window
brew install kubetail

Aqui estão alguns exemplos do que você pode fazer com o novo comando logs:

Terminal window
# 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

O comando logs usa seu arquivo de configuração local do kube para autenticar no cluster, portanto, para trocar de cluster, basta alterar o contexto do kube config. Você também pode usar o flag --kube-context:

Terminal window
kubetail logs --kube-context minikube deployments/web

Você vai notar que para usar --grep também é necessário usar --force. Isso ocorre porque a filtragem é feita no lado do cliente, o que significa que a ferramenta continuará baixando logs do cluster até encontrar a quantidade desejada de correspondências. Isso pode resultar em downloads inesperadamente grandes, por isso adicionamos uma verificação de flag secundária. Estamos trabalhando em um novo recurso para contornar esse problema.

Experimente o novo comando logs e nos diga o que você achou!