コンテンツにスキップ

Blog

リリース:新しい Rust 製クラスターエージェント


tl;dr クラスターエージェントを Go から Rust に移行し、より小さく、より少ないメモリで動作するようになりました。新しいクラスターエージェントを使用するには、最新リリース(cli/v0.8.2、helm/v0.15.2)にアップグレードしてください。こちらでライブ体験もできます。


最近、クラスターエージェントを Go から Rust に移行することを決定しました。書き直しが完了したことをお伝えできて嬉しいです。結果は、CPU 使用率を最小限(約 0.1%)に保ちながら、イメージサイズが 57% 削減(10MB)、メモリ使用量が 70% 削減(約 3MB)されたクラスターエージェントです。

Kubetail の最初のバージョンはクラスター内で動作し、Web ブラウザを通じてユーザーにログを提供するよう設計されていました。そのバージョンでは、バックエンドの主な役割は Kubernetes API にリクエストを送り、レスポンスをリアルタイムでフロントエンドに中継することでした。Python や JavaScript などのオプションを検討した後、Go を選びました。Kubernetes API との互換性が優れており、マルチスレッドのサポートも充実していて、高速な実行ファイルと小さな Docker イメージを生成できるからです。

次のバージョンの Kubetail では、Web ダッシュボードをローカルで実行できる kubetail CLI ツールが追加されました。CLI ツールの実装にも再び Go を選びました。優れた CLI インタラクションライブラリがあり(spf13 に感謝!)、クロスプラットフォームサポートが優れており、何より、インクラスターダッシュボードで使用していた Go ベースの Web アプリを再利用できたからです。

それまで、Kubetail は Kubernetes API を通じてのみログを取得していました。しかし、ログファイルサイズや最後のイベントタイムスタンプなど、Kubernetes API が公開していないデータを活用した新機能を追加したくなったとき、各ノードの生のログファイルに直接アクセスできるエージェントが必要だと気づきました。別の言語を使うこともできましたが、最もよく知っていてこれまで良い結果をもたらしてくれた Go を再び選びました。幸い、Go にはエージェントのインターフェースとして自然な選択である gRPC の優れたサポートもありました。

当時のアプリの機能セットを考えると、デスクトップでもクラスター内でも Go がうまく機能していたことから、最初に Go を選んだことに非常に満足していました。そして、最も要望の多かった機能であるログ検索の実装方法を検討し始めました。

ログ検索を考え始めたとき、フルテキストインデックスではなく grep を使いたいと思っていました。ほとんどのユースケースには十分であり、ユーザーにフルテキストインデックスのメンテナンスコストを負わせたくなかったからです。同時に、個人的にログの grep に rg を使っていてその速度に感銘を受けており、grep ソリューションを探す中でそれを活用できないかと考えました。そこで、ライブラリとして利用可能だが、Rust で書かれているという条件があることに気づきました。

カスタムコードを書く前に、exec.Command を使って stdin/stdout 経由で rg を外部実行ファイルとして使うアイデアを探りました。基本的なユースケースでは動作しましたが、時間フィルター、ANSI エスケープシーケンス処理、JSON フォーマット行のサポートなどのカスタム機能を追加するにつれて扱いにくくなっていきました。そこで、カスタムログファイル grepper を自作することにしました。Go を使うことも少し検討しましたが、パフォーマンスと堅牢性の観点から rg の背後にあるライブラリ ripgrep を使いたかったため、コードを Rust で書かなければなりませんでした。

当時はクラスターエージェント全体を Rust で書き直したくなかったので、Go から Rust を呼び出す方法(例:rustgo)を調べ、カスタム Rust コードを独立した実行ファイルとして保持し、exec.Command を使って Go から呼び出す方法に落ち着きました。コードをできるだけシンプルにするため、共有の protocol buffers スキーマを使い、stdin/stdout インターフェースでシリアライズ/デシリアライズを実装しました。

検索機能のリリース後、コミュニティが成長し始め、私よりはるかに Rust の経験豊富な 2 人の開発者に出会いました。Christopher Valerio(freexploit)と Giannis Karagiannis(gikaragia)です。最初は 2 人が Rust コードの改善を始め、コードベースに慣れるにつれて、クラスターエージェントにおける Go と Rust のインピーダンスミスマッチをどう解消するかについて話し合うようになりました。検索機能とは別に、クラスターエージェントはクラスターのすべてのノードで動作するため、できるだけ高性能で軽量であることが重要です。これはまさに Rust が輝くユースケースです。こうしたアイデアが浮かぶ中で、エージェント全体を Rust に移行するアイデアを話し合うコミュニティミーティングを開きました。2 人とも喜んで取り組むと言ってくれたので、やってみることにしました!

決定が下されると、Christopher と Giannis はすぐに作業に取り掛かりました。Christopher はプロジェクトの初期高レベルアーキテクチャを定義し、GitHub にいくつかの初期 issue を作成しました。その後 Giannis が加わり、機能セットの実装、テストの作成、他のコントリビューターの協力を得るためのさらなる issue 作成を始めました。Giannis は数週間で Go ベースのクラスターエージェントと機能的に同等になり、さらに約 1 週間のテストを経て、コードを main にマージする準備ができたと判断しました。

私自身は最近 Rust を学び始めたばかりだったので、Giannis のプルリクエストをレビューするのに Claude Code と Codex CLI が非常に役立ちました。彼も自分の側でチャットボットを使っていたので、GitHub プルリクエストを介した真の人間とボットの共同作業でした。私たちが持っていた重要な利点の一つは、エージェントが明確に定義された gRPC インターフェースを使用しているため、protocol buffers スキーマを再利用でき、Rust ベースのエージェントが Go ベースのバージョンと機能的に同等になった時点でスイッチを切り替えるだけでよかったことです。Rust ベースの gRPC サーバーの構築には tonic を使いました。これは直感的で、Go ベースの gRPC サーバーとの違いはわずかな差異のみでした。

最終結果は、CPU 使用率を最小限(約 0.1%)に保ちながら、イメージサイズが 57% 削減(10MB)、メモリ使用量が 70% 削減(約 3MB)されたクラスターエージェントです。さらに、コードがすべて同じ言語で書かれているため、作業が格段に容易になりました。

私たちのミッションは、シンプルで軽量なパッケージで強力なロギングツールをユーザーに提供することです。しかし Kubernetes API のロギング機能には限界があり、より高度な機能を解放するには各ノードの生のログファイルへの直接アクセスが必要です。それがクラスターエージェントの役割です——これは次に構築したいすべてのものの基盤となります。

もちろん、ユーザーがクラスターにエージェントをインストールすることに慎重になるのは理解できます。役立つことに加えて、エージェントは小さく、高速で、安全でなければなりません。Rust への移行はこれらの要件に対する私たちの回答です。イメージサイズを半分以上削減し、メモリ使用量を 70% 削減することで、Kubetail エージェントはリソースが最も制限された環境でもデプロイできるほど小さくなりました。

しかし、これは始まりに過ぎません。Rust を活用することで、CPU とメモリをできるだけ少なく使いながら、クラスター内でディスク上のファイルをリアルタイムに直接処理する可能性の限界を押し広げていきます。現在はログに焦点を当てていますが、同じアプローチはメトリクス、通知、その他の種類のオブザーバビリティデータにも適用できます。

次のステップにワクワクしており、ぜひ皆さんにもご参加いただきたいです。私たちの取り組みに共感し、コードのコントリビューションやユーザーとしてのフィードバックを共有したい方は、Discord でご参加ください。

OCV、ありがとう

すべてのクラスターで動作する Kubernetes の新しいロギングレイヤーを構築するという私たちのミッションが成功するなら、それはAlexSmith が率いる Open Core Ventures(OCV)と彼らの Catalyst プログラムのおかげである部分が大きいでしょう。

OCV は Sid Sijbandij(GitLab の共同創業者)が設立したベンチャー会社で、オープンコア原則に基づいたアーリーステージのオープンソース企業に投資しています。オープンソースへの取り組みの一環として、プロジェクトを成長させたいオープンソースプロジェクトのメンテナーに少額の助成金と多くのメンターシップを提供する Catalyst というプログラムを始めました。12 週間にわたって、オープンソースコミュニティの構築方法と、製品を効果的にマーケティングして成長とトラクションを見つける方法を学べます。Kubetail は最近このプログラムに参加し、私たちにとってゲームチェンジャーとなりました。

Kubetail に取り組む前、私は Y Combinator の W07 バッチに属する Octopart というスタートアップの共同創業者でした。スタートアップとしては悪くない結果だったので、Kubetail を始めたときも同様のアプローチを取りました。MVP の構築に集中し、準備ができたら Hacker News(HN)に投稿しました。幸いにも、投稿は数時間フロントページに到達し、数百の GitHub スターと少数の実際のユーザー(約 10 人)を獲得しました。

その後、Kubetail は悲しみの谷に入りました。これは最初のリリース後、最初の盛り上がりが収まり、一握りのユーザーと外部からの検証なしに、内なる楽観主義だけを頼りに前進しなければならないスタートアップカーブの部分です。この谷は初めてではなかったので、以前やったことをそのまま行い、頭を下げてコーディングを続けました。

この期間、私は MVP(Kubetail ダッシュボード)をできる限り使いやすくすることに集中しました。数人の初期ユーザーからのフィードバックに応えて、クラスター内での動作に加えてユーザーのデスクトップでも実行できるようにアーキテクチャを変更しました。また、ユーザーが Homebrew や他のパッケージリポジトリを通じてアプリを見つけてダウンロードしやすくする取り組みも行いました。そしてバックグラウンドでは、最も要望の多かった機能である検索の実装に集中していました。

1 年以上の間、私は一人で作業を続けながらもプロジェクトの成長は停滞したままでした。そんな時、OCV からの予期せぬメールを受け取り、それがすべてを変えた Catalyst スポンサーシッププログラムへの参加につながりました。

Catalyst の参加者として、Alex と OCV チームから実践的なメンターシップを受けました。技術的なスキルはあってもコミュニティ構築やオープンソースプロジェクトの管理経験がなかった私には、これは非常に貴重なものでした。Catalyst の支援により、私は純粋なコーディングルーティンから、開発とコミュニティ参加とコントリビューターサポートのバランスを取る生活へと切り替えました。

Catalyst に参加する前、Kubetail にはコミュニティが全くありませんでした。Discord サーバーはありましたが、私一人だけで毎日一人で作業していました。その後 Alex が週ごとに何に集中すべきか、新しく試すべきことを提案してくれました。彼の助けにより、Kubetail は 12 週間で約 300 スターから 1,300 以上に成長しました。さらに重要なのは、コミュニティが立ち上がったことです。Catalyst 前は 3 人のコントリビューターがいて Discord にはユーザーがいませんでした。今では 35 人のコントリビューターと 61 人のメンバーを持つ活発な Discord コミュニティがあります。

Catalyst の期間中、すべてが整い、ついにログ検索機能をリリースする準備が整いました——今回はコミュニティを背後に持ち、OCV のメンターシップが新しいユーザーへの機能の普及を助けてくれました。今回機能を発表したとき、Kubetail は 1 日以上 HN のフロントページに掲載され、Reddit と Twitter で何万人ものユーザーに見られました。これにより月間ダウンロード数が 100 未満から 400 以上に増加し、Kubetail は小さな情熱プロジェクトから野心的なコミュニティ主導のプロジェクトへと変貌しました。私にとっての Catalyst のハイライトは、この頃に新しい Kubetail メンテナー(rxinui)とコミュニティの皆さんと一緒に GitHub 1,000 スターのマイルストーンを祝うことができたことです。

Discord でのお祝い

前途の困難さについて幻想はありません。困難な技術的問題に取り組んでおり、すでに潜在的ユーザーの多くの注目を集めている Datadog、Grafana、New Relic、ClickHouse などの資金力のある企業と競争する市場で活動しています。さらに、ユーザーはオブザーバビリティツールに多くの機能を既に期待しており、目標達成には多くの優秀なエンジニアが必要であり、そのためにはまだ解決できていないリソースの確保が必要です。

しかし、成功の可能性についてこれほど楽観的になったことはありません。経験豊富なコントリビューターから新しいことを学んだり、若いコントリビューターのプルリクエストがマージされたときの彼らの興奮を見るたびに、私は活力をもらいます。ユーザーが自分の問題を解決するプルリクエストをレビューしたり、新機能について話し合ったりするたびに、オープンソースコミュニティの一員として共同で製品を作るというのが最善の方法であるという確信が深まります。

私にとって、オープンソースプロジェクトはユーザーが愛して使い、彼らにとっても良い高品質な製品を生み出すことができる料理鍋のようなものです。しかし、もちろんすべての製品の背後にある魔法の成分はコミュニティであり、Kubetail のコミュニティについて語るとき、Alex と OCV チーム全員に心から感謝の言葉を伝えなければなりません。

リリース:Kubernetes 向けリアルタイムログ検索

Kubetail が昨年リリースされて以来、最も要望の多かった機能はログ検索です。最新の公式リリース(cli/v0.4.3、helm/v0.10.1)でついにログ検索機能が利用可能になったことをお伝えできて嬉しいです。こちらで実際の動作を確認できます:

https://www.kubetail.com/demo

検索の実装に時間がかかった理由は、Kubernetes API がネイティブにサポートしていないため、ゼロから構築する必要があったからです。クライアント側 grep を使って素早く実装することも検討しましたが、検索のたびに大量のログファイルをフルダウンロードする可能性があり、遅くて帯域幅を大量に消費するため、良いユーザー体験とは言えません。回避策はありますが、ユーザーに追加の入力を求めることになり、これも良い体験ではありません。

代わりに、ripgrep をラップするカスタム Rust 製実行ファイルを作成して検索を実装しました。なぜ Rust か?とにかく速いからです。Kubetail のバックエンドの大部分は Go で書かれていますが、ディスク上のログファイルを読み込むこの低レベルコンポーネントは可能な限り高速であることが求められました。結果:1GB ファイルのフルスキャンに約 250ms。クエリごとに、実行ファイルは各ノード上の関連するコンテナログファイルのみをスキャンし、マッチした行だけをブラウザにストリーミングします。ほとんどのクエリは早期終了できるため、フルスキャン前に結果を返すことすら可能です。Kubetail の検索は Kubernetes ログの「リモート grep」と考えることができます。ローカルで grep するためにログファイル全体をダウンロードする必要はもうありません。

検索を有効にするには、クラスターに Kubetail「クラスターリソース」をインストールする必要があります。GUI から「Install」をクリックするか、CLI で kubetail cluster install を実行するだけで簡単にインストールできます。この操作により、各ノードに Kubetail クラスターエージェントと Kubetail クラスター API のインスタンスがデプロイされます。クラスター API が利用可能な場合、ダッシュボードはノード上での検索などの Kubetail カスタム機能にアクセスするためにそれを使用します。利用できない場合は、GUI でそれらの機能を無効にし、Kubernetes API にフォールバックします。

ログ検索はまだ始まったばかりで、改善の余地がたくさんあります。Rust、Go、React の開発者、またはログが大好きな UI デザイナーの方、Kubernetes 向けの最もユーザーフレンドリーなオープンソースロギングプラットフォームの構築に参加してください。Discord でコミュニティに参加しましょう!

Andres

Kubetail CLI "logs" コマンドのリリース

Kubernetes 上のマルチコンテナワークロードの監視とデバッグを容易にするため、Kubetail CLI ツールに新しい logs コマンドを追加しました。新しい logs コマンドを使うと、ターミナルから Kubernetes ワークロードのログをリアルタイムで grep できます。時間やノード・ゾーンなどのソース属性でフィルタリングすることも可能です。

Kubetail CLI ツールをインストールするには、リリースページからダウンロードするか、Homebrew を使ってください:

Terminal window
brew install kubetail

新しい 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

logs コマンドはローカルの kube config ファイルを使ってクラスターに認証するため、クラスターを切り替えるには kube config コンテキストを変更してください。--kube-context フラグを使うこともできます:

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

--grep を使用する場合は --force も必要なことに気づくでしょう。これはフィルタリングがクライアント側で行われるためで、ツールは希望する数のマッチが見つかるまでクラスターからログをダウンロードし続けます。これにより予期しない大量ダウンロードが発生する可能性があるため、二次フラグ確認を追加しました。この問題を回避する新機能を開発中です。

ぜひ新しい logs コマンドを試して、感想をお聞かせください!