昨年秋の Sprint 2.0 でNGINX Modern Apps Reference Architecture (MARA) プロジェクトを発表したとき、私たちは、これが一部のアーキテクチャのような「おもちゃ」ではなく、「Kubernetes 環境で実行されるライブ プロダクション アプリケーションにすぐにデプロイできる、堅牢でテスト済みの」ソリューションであるという意図を強調しました。 このようなプロジェクトでは、可観測性ツールが絶対に必要です。 MARA チームの全メンバーは、ステータスとパフォーマンスに関する洞察が不足しているために、アプリケーションの開発と配信がいかにフラストレーションのたまる作業になるかを直接経験しています。 MARA には、実稼働環境でのデバッグとトレースのための計測機能を含める必要があるという点で、私たちは即座に合意に達しました。
MARA のもう一つの指針は、オープン ソース ソリューションを優先することです。 この記事では、多機能なオープンソースの可観測性ツールを探した結果、どのようにしてOpenTelemetryにたどり着いたかを説明し、その後、Python、Java、NGINX で構築されたマイクロサービス アプリケーションと OpenTelemetry を統合するために使用したトレードオフ、設計上の決定、手法、テクノロジーについて詳しく説明します。
私たちの経験談を聞くことで、潜在的な落とし穴を回避し、OpenTelemetry の導入を加速していただければ幸いです。 この投稿は時間的制約のある進捗報告であることにご注意ください。ここで説明するテクノロジーは 1 年以内に成熟すると予想されます。 さらに、一部のプロジェクトには現在欠点があることを指摘していますが、私たちは行われているすべてのオープンソース作業に非常に感謝しており、その進歩を見るのが楽しみです。
可観測性ソリューションと統合するアプリケーションとして、Google のBank of Anthosサンプル アプリのフォークであるBank of Sirius を選択しました。 これは、インフラストラクチャをコードとしてデプロイできるマイクロサービス アーキテクチャを備えた Web アプリです。 このアプリケーションは、パフォーマンスと信頼性の面で改善できる方法が多数ありますが、ブラウンフィールドアプリケーションとして十分に考慮できるほど成熟しています。 そのため、理論上は分散トレースによってアプリケーション アーキテクチャの欠点に関する貴重な洞察が得られるため、これは OpenTelemetry をアプリケーションに統合する方法を示す良い例であると考えています。
図に示されているように、アプリケーションをサポートするサービスは比較的単純です。
OpenTelemetry を選択するまでの道のりは非常に紆余曲折があり、いくつかの段階がありました。
利用可能なオープンソースの可観測性ツール自体を評価する前に、私たちは、可観測性のどの側面を重視するかを特定しました。 過去の経験に基づいて、次のリストを作成しました。
もちろん、単一のオープンソース ツールやアプローチにこれらすべての機能が含まれているとは思っていませんでしたが、少なくとも、利用可能なツールを比較するための目標となる基盤は得られました。 私たちは各ツールのドキュメントを参照して、希望リストにある 7 つの機能のうちどの機能がサポートされているかを確認しました。 表は私たちの調査結果をまとめたものです。
テクノロジー | ログ記録 | 分散トレース | メトリクス | エラー集約 | 健康診断 | ランタイムイントロスペクション | ヒープ/コアダンプ |
---|---|---|---|---|---|---|---|
ELK +エラスティック APM | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ |
グラファナ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ |
グレイログ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
イェーガー | ❌ | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ |
オープンセンサス | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ |
オープンテレメトリー | ベータ | ✅ | ✅ | ✅ | ✅ * | ❌ | ❌ |
プロメテウス | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ |
統計D | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ |
ジプキン | ❌ | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ |
*拡張機能として
このテーブルを作ったのは、衝撃的な出来事でした。 さまざまなツールは機能や目的が大きく異なるため、すべてを同じカテゴリのメンバーと見なすことはできません。まるでリンゴとトースターを比較するようなものです。
たとえば、 ELK ( Elasticsearch-Logstash-Kibana 、およびFilebeat ) とZipkin は根本的に異なることを実行するため、これらを比較しようとすると、何よりも混乱が生じます。 残念ながら、「ミッションクリープ」は状況をさらに混乱させるだけです。ユーザーのリクエストに応えて、ツールの主な目的とは関係のない機能が追加され、他のツールと重複するようになっています。 表面的には、ELK はログの保存と視覚化を行い、Zipkin は分散トレースを行います。 しかし、Elastic 製品ポートフォリオをもう少し深く調べてみると、分散トレースをサポートし、 Jaeger との互換性も備えたElastic APMにすぐに出会うでしょう。
ミッションクリープの問題以外にも、多くのツールを相互に統合することができ、コレクター、アグリゲーター、ダッシュボードなどのさまざまな組み合わせが実現します。 一部のテクノロジーは互いに互換性がありますが、一部は互換性がありません。
そのため、この比較表では、選択の根拠となるほど正確な情報が得られませんでした。 各プロジェクトの目標、指針、将来の方向性について定性的な調査を行う必要がありました。プロジェクトの価値観が私たちの価値観に似ているほど、時間の経過とともに互換性が維持される可能性が高くなると考えたからです。 プロジェクト ページにアクセスすると、 OpenCensusページですぐにこれに気付きました。
OpenCensus と OpenTracing が統合されて OpenTelemetry が形成され、これが OpenCensus と OpenTracing の次のメジャー バージョンとして機能します。 OpenTelemetry は既存の OpenCensus 統合との下位互換性を提供し、今後 2 年間は既存の OpenCensus ライブラリに対するセキュリティ パッチを継続的に提供します。
私たちにとって、それは重要なデータポイントでした。 私たちの選択が将来にわたって有効であるとは保証できませんが、少なくとも今後はコミュニティの強力なサポートが得られることを知りたかったのです。 この情報があれば、OpenCensus を候補リストから削除できます。 Jaeger を使用するのもおそらく良い考えではなかったでしょう。これは、現在正式に廃止されているOpenTracingプロジェクトのリファレンス実装であるためです。新しい貢献の大部分は OpenTelemetry に投入される予定です。
次に、 OpenTelemetry Collectorについて調べます。
OpenTelemetry Collector は、テレメトリ データを受信、処理、エクスポートする方法についてベンダーに依存しない実装を提供します。 さらに、複数のオープンソースまたは商用バックエンドに送信するオープンソーステレメトリデータ形式(Jaeger、Prometheus など)をサポートするために、複数のエージェント/コレクターを実行、操作、保守する必要がなくなります。
OpenTelemetry Collector は、さまざまなバックエンドでさまざまな可観測性収集および計測方法を組み合わせて使用できるアグリゲータとして機能します。 基本的に、アプリケーションは Zipkin を使用してトレースを収集し、 Prometheusからメトリックを収集し、それを構成可能なバックエンドに送信してGrafanaで視覚化することができます。 この設計には他にもさまざまな組み合わせが考えられます。そのため、さまざまなアプローチを試して、どのテクノロジーがユースケースに適しているかを確認できます。
基本的に、私たちが OpenTelemetry Collector を導入したのは、理論上は観測可能性テクノロジーを切り替えることができるからです。 プロジェクトが比較的未熟であるにもかかわらず、状況を理解する唯一の方法はテクノロジーを使用することであるため、私たちは大胆に取り組み、オープンソース統合のみで OpenTelemetry を使用することを決定しました。
ただし、OpenTelemetry Collector には欠けている部分があるため、それらの機能については他のテクノロジに頼る必要があります。 次のセクションでは、私たちの選択とその理由を要約します。
ログ記録は、一見単純な可観測性の一部ですが、すぐに複雑な決定につながります。 コンテナからログ出力を収集するだけなので簡単ですが、その後、データをどこに保存するか、その保存場所にどのように転送するか、データを有用にするためにどのようにインデックスを作成するか、そしてデータをどのくらいの期間保持するかを決定する必要があるため、複雑になります。 ログ ファイルが有用であるためには、さまざまな検索者のニーズを満たすのに十分なさまざまな基準で簡単に検索できる必要があります。
OpenTelemetry Collector によるログ記録のサポートを検討しましたが、この記事の執筆時点ではまだベータ版です。 私たちは、他の選択肢を引き続き調査しながら、短期的にはELK をログ記録に使用することにしました。
特別な理由がない限り、 Elasticsearchツールをデフォルトで使用し、デプロイメントを取り込みノード、調整ノード、マスター ノード、およびデータ ノードに分割できるようにしました。 簡単に展開できるように、 Bitnami チャートを使用しました。 データを転送するために、Kubernetes DaemonSet の一部としてFilebeat をデプロイしました。 Kibanaを導入し、事前にロードされたインデックス、検索、視覚化、ダッシュボードを活用することで、検索機能が追加されました。
このソリューションは機能するものの、デフォルトの構成ではリソースを大量に消費するため、 K3SやMicrok8sなどのリソース フットプリントが小さい環境では実行が困難であることがすぐに明らかになりました。 各コンポーネントのレプリカ数を調整する機能を追加することでこの問題は解決されましたが、リソースの枯渇やデータ量の過剰が原因で障害が発生する可能性がありました。
私たちはこれに失望するどころか、これをさまざまな構成でログ記録システムをベンチマークし、 Grafana LokiやGraylogなどの他のオプションを調査する機会と捉えています。 軽量のログ記録ソリューションでは、一部のユーザーが必要とする機能の完全なセットが提供されず、リソースを大量に消費するツールで実現できないことが判明する可能性もあります。 MARAのモジュール性を考慮して、ユーザーにより多くの選択肢を提供するために、これらのオプション用の追加モジュールを構築する可能性があります。
必要なトレース機能を提供するツールを決定するだけでなく、ソリューションをどのように実装するか、どのテクノロジをソリューションに統合する必要があるかを検討する必要がありました。
まず、計測によってアプリケーション自体のサービス品質に悪影響が及ばないようにしたいと考えました。 私たち全員が、ログがエクスポートされるときに 1 時間に 1 回パフォーマンスが予想どおりに低下するシステムを扱ったことがあり、その経験をもう一度味わいたいとは思っていませんでした。 OpenTelemetry Collector のアーキテクチャは、ホストごとに 1 つのインスタンスを実行できるため、この点で魅力的でした。 各コレクターは、ホスト上で実行されているさまざまなアプリケーション (コンテナー化されているかどうかに関係なく) のクライアントとエージェントからデータを受信します。 コレクターはデータを集約し、場合によっては圧縮してからストレージ バックエンドに送信するので、理想的だと思われます。
次に、アプリケーションで使用しているさまざまなプログラミング言語とフレームワークでの OpenTelemetry のサポートを評価しました。 ここで、状況が少し複雑になりました。 表に示されている 2 つのプログラミング言語と関連フレームワークのみを使用しているにもかかわらず、複雑さのレベルは驚くほど高くなりました。
言語 | フレームワーク | サービス数 |
---|---|---|
ジャワ | スプリングブート | 3 |
パイソン | フラスコ | 3 |
言語レベルのトレース機能を追加するために、最初に OpenTelemetry の自動インストルメンテーションエージェントを試しましたが、その出力が乱雑でわかりにくいことがわかりました。 自動計測ライブラリが成熟するにつれて、この問題は改善されると確信していますが、当面は OpenTelemetry エージェントを除外し、トレース機能をコードに組み込むことにしました。
コード内でトレースを直接実装する前に、まず OpenTelemetry Collector を接続して、すべてのトレース データをローカルで実行されている Jaeger インスタンスに出力し、出力をより簡単に確認できるようにしました。 これは非常に便利で、OpenTelemetry を完全に統合する方法を見つけ出す際に、トレース データの視覚的な表示を試すことができました。 たとえば、依存サービスへの呼び出し時に HTTP クライアント ライブラリにトレース データが含まれていないことが判明した場合、その問題をすぐに修正リストに追加します。 Jaeger は、単一のトレース内ですべての異なるスパンをわかりやすく表示します。
Python コードにトレースを追加するのは比較的簡単でした。 すべてのサービスで参照される2 つの Python ソース ファイルを追加し、関連するopentelemetry-instrumentation-*
依存関係を含めるようにそれぞれのrequirements.txtファイルを更新しました。 つまり、すべての Python サービスに同じトレース構成を使用できるだけでなく、各リクエストのトレース ID をログ メッセージに含めたり、依存するサービスへのリクエストにトレース ID を埋め込んだりできるようになりました。
次に、Java サービスに注目しました。 グリーンフィールド プロジェクトで OpenTelemetry Java ライブラリを直接使用するのは比較的簡単です。必要なライブラリをインポートし、トレース API を直接使用するだけです。 ただし、私たちと同じように Spring を使用している場合は、追加の決定を行う必要があります。
Spring にはすでに分散トレース API であるSpring Cloud Sleuthがあります。 ドキュメントに記載されているように、これは基盤となる分散トレース実装の上にファサードを提供し、次の処理を実行します。
spring-cloud-sleuth-zipkin
が利用可能な場合、… [生成して報告する] HTTP 経由で Zipkin 互換のトレースを生成します。 デフォルトでは、ローカルホスト (ポート 9411) 上の Zipkin コレクター サービスに送信されます。 spring.zipkin.baseUrl を
使用してサービスの場所を構成します。API を使用すると、@
Scheduled アノテーションが付けられたタスクにトレースを追加することもできます。
言い換えれば、Spring Cloud Sleuth だけを使用すると、HTTP サービス エンドポイント レベルでトレースをすぐに取得できるため、大きなメリットがあります。 私たちのプロジェクトではすでに Spring を使用しているため、すべてをそのフレームワーク内に保持し、提供されている機能を活用することにしました。 しかし、すべてをMavenで接続しているときに、いくつかの問題が見つかりました。
opentelemetry-instrumentation-api
の古いアルファ バージョンに依存しています。 現在、このライブラリの最近の非アルファ1.xリリースはありません。spring-cloud-build を
プルする必要があります。これにより、Maven プロジェクトの定義が少し複雑になりました。Maven で Spring リポジトリと Maven Central からプルする必要があったためです。これは、Spring Cloud での OpenTelemetry サポートがいかに初期段階であったかを明確に示しています。 それでも、私たちは前進を続け、Spring Cloud Sleuth と OpenTelemetry を使用して分散トレースを構成するための共通テレメトリ モジュールを作成しました。これには、さまざまなテレメトリ関連のヘルパー関数と拡張機能が含まれています。
共通テレメトリ モジュールでは、次の機能を提供することで、Spring Cloud Sleuth および OpenTelemetry ライブラリによって提供されるトレース機能を拡張します。
自動構成
クラス。さらに、サービス間で HTTP 呼び出しを行う際に、より多くのメトリックとカスタマイズ性を必要としていたため、Apache HTTP クライアントを基盤とする Spring 互換の HTTP クライアントを実装しました。 この実装では、依存サービスが呼び出されたときにトレース識別子とスパン識別子がヘッダーとして渡され、トレース出力に含めることができます。 さらに、この実装では、OpenTelemetry によって集計される HTTP 接続プール メトリックが提供されます。
全体として、Spring Cloud Sleuth と OpenTelemetry を使用してトレースを接続するのは大変な作業でしたが、その価値はあったと信じています。 このプロジェクトとこの投稿が、この道を歩もうとする他の人々にとって道を照らす助けとなることを願っています。
リクエストのライフサイクル全体にわたってすべてのサービスを接続するトレースを取得するには、OpenTelemetry を NGINX に統合する必要がありました。この目的のために、 OpenTelemetry NGINX モジュール(まだベータ版) を使用しました。 NGINX のすべてのバージョンで動作するモジュールのバイナリを入手するのが困難になる可能性があることを予想して、サポートされていない NGINX モジュールを組み込んだコンテナ イメージのGitHub リポジトリを作成しました。 私たちは夜間ビルドを実行し、簡単にインポートできる Docker イメージを介してモジュール バイナリを配布します。
このプロセスはまだMARA プロジェクトのNGINX Ingress Controllerのビルド プロセスに統合されていませんが、近いうちに統合する予定です。
トレースの OpenTelemetry 統合を完了した後、次にメトリックに焦点を当てました。 Python ベースのアプリケーションには既存のメトリックが存在しなかったため、今のところは追加を延期することにしました。 Java アプリケーションの場合、 Micrometer をGoogle Cloud のStackdriverと組み合わせて使用するオリジナルのBank of Anthosソースコードがメトリクスをサポートしています。 ただし、Bank of Anthos をフォークした後、そのコードは Bank of Sirius から削除されました。これは、メトリクス バックエンドを構成できなかったためです。 それにもかかわらず、メトリック フックがすでに存在していたという事実は、適切なメトリック統合の必要性を物語っています。
構成可能で実用的なソリューションを見つけるために、まずOpenTelemetry Java ライブラリと Micrometer 内のメトリック サポートを検討しました。 これらのテクノロジの比較を検索したところ、執筆時点では OpenTelemetry メトリクスはまだアルファ版であるにもかかわらず、JVM でメトリクス API として使用されるOpenTelemetry の欠点を列挙する結果が多数ありました。 Micrometer は、JVM 用の成熟したメトリック ファサード レイヤーであり、独自のメトリック実装ではなく、構成可能なメトリック実装の前面に立つ共通 API ラッパーを提供するという点でslf4jに似ています。 興味深いことに、これは Spring のデフォルトのメトリック API です。
この時点で、私たちは以下の事実を検討していました。
いくつかの実験を行った後、最も実用的なアプローチは、Prometheus のバックエンド実装を備えた Micrometer ファサードを使用し、OpenTelemetry Collector が Prometheus API を使用してアプリケーションからメトリックを取得するように構成することであると判断しました。 OpenTelemetry にメトリック タイプが欠落していると問題が発生する可能性があることは多数の記事からわかっていましたが、私たちのユース ケースではそれらのタイプは必要ないため、妥協は許容範囲内でした。
OpenTelemetry Collector に関して興味深い点が 1 つ見つかりました。トレースを OTLP 経由で受信し、メトリックを Prometheus API 経由で受信するように構成していたにもかかわらず、OTLP またはその他のサポートされているプロトコルを使用して、両方のタイプのデータを外部データ レシーバーに送信するように構成できるということです。 これにより、 LightStepを使用してアプリケーションを簡単に試すことができました。
全体的に、Java でのメトリクスのコーディングは、多数の例とチュートリアルが用意されている Micrometer API に準拠するように記述したため、かなり簡単でした。 おそらく、メトリックとトレースの両方にとって最も困難だったのは、 telemetry-common
のpom.xmlファイル内の Maven 依存関係グラフを正しく取得することでした。
OpenTelemetry プロジェクト自体のミッションにはエラー集約は含まれておらず、 SentryやHoneybadger.ioのようなソリューションほどエレガントなエラータグ付けの実装は提供されていません。 それでも、別のツールを追加するのではなく、短期的にはエラー集約に OpenTelemetry を使用することにしました。 Jaeger のようなツールを使用すると、 error=true
を検索して、エラー状態のすべてのトレースを見つけることができます。 これにより、少なくとも何が一般的に問題になっているのかがわかります。 将来的には、 Sentry統合の追加を検討する可能性があります。
私たちのアプリケーションのコンテキストでは、ヘルスチェックにより、サービスが正常であるか、起動フェーズが完了したかを Kubernetes に知らせます。 サービスが正常でない場合は、インスタンスを終了または再起動するように Kubernetes を構成できます。 私たちのアプリケーションでは、ドキュメントが不十分であることがわかったため、OpenTelemetry ヘルスチェックを使用しないことにしました。
むしろ、JVM サービスでは、ヘルスチェック エンドポイントだけでなく、ランタイム イントロスペクション エンドポイントとヒープ ダンプ エンドポイントも提供するSpring Boot Actuatorという Spring プロジェクトを使用します。 Python サービスでは、Spring Boot Actuator 機能のサブセットを提供するFlask Management Endpoints モジュールを使用します。 現在は、カスタマイズ可能なアプリケーション情報とヘルスチェックのみが提供されています。
Spring Boot Actuator は JVM と Spring にフックして、イントロスペクション、監視、ヘルスチェックのエンドポイントを提供します。 さらに、エンドポイントで提示されるデフォルト データにカスタム情報を追加するためのフレームワークも提供します。 エンドポイントは、キャッシュ状態、ランタイム環境、データベース移行、ヘルスチェック、カスタマイズ可能なアプリケーション情報、メトリック、定期的なジョブ、HTTP セッション状態、スレッド ダンプなどの実行時イントロスペクションを提供します。
Spring Boot Actuator によって実装されるヘルスチェック エンドポイントはモジュール構成になっているため、サービスのヘルスは、ライブネスまたは準備状況のいずれかに分類される複数の個別のチェックで構成できます。 すべてのチェック モジュールを表示する完全なヘルス チェックも利用可能で、通常は次のようになります。
情報エンドポイントは、JSON ドキュメント内で、単一の高レベル JSON オブジェクトと一連の階層キーおよび値として定義されます。 通常、ドキュメントには、サービス名とバージョン、アーキテクチャ、ホスト名、OS 情報、プロセス ID、実行可能ファイル名、マシン ID や一意のサービス ID などのホストに関する詳細が指定されます。
ツールの機能とウィッシュリストの比較の表から、どのツールもランタイム イントロスペクションやヒープ/コア ダンプをサポートしていないことを思い出すかもしれません。 ただし、基盤となるフレームワークである Spring は両方をサポートしています。ただし、監視機能をアプリケーションに組み込むには、多少の作業が必要でした。 前のセクションで詳しく説明したように、実行時イントロスペクションでは、Spring Boot Actuator と組み合わせて Python モジュールを使用します。
同様に、ヒープ ダンプの場合、Spring Boot Actuator が提供するスレッド ダンプ エンドポイントを使用して、必要な機能のサブセットを実現します。 オンデマンドでコア ダンプを取得したり、JVM のヒープ ダンプを理想的な粒度レベルで取得したりすることはできませんが、追加の労力をほとんどかけずに、必要な機能の一部を実現できます。 残念ながら、Python サービスのコア ダンプには、かなりの追加作業が必要になるため、後日まで延期します。
何度も泣き、人生の選択に疑問を抱いた後、私たちは MARA の観測可能性のために次のテクノロジーを使用することに落ち着きました (以下、 OTel はOpenTelemetry の略です)。
エラーマーク
の付いたトレースを検索するためのエクスポーターの検索機能この実装は、ある時点のスナップショットです。 開発が進むにつれて、確実に変化し、進化していきます。 すぐに、広範な負荷テストを通じてアプリケーションを実行する予定です。 私たちは、可観測性アプローチの欠点について多くのことを学び、追加の可観測性機能を追加したいと考えています。
モダン アプリ リファレンス アーキテクチャとサンプル アプリケーション(Bank of Sirius) をお試しください。 改善方法についてのアイデアをお持ちの場合は、 GitHub リポジトリへの投稿を歓迎します。
この投稿はシリーズの一部です。 今後 MARA に機能が追加されるにつれて、その詳細をブログで公開していきます。
「このブログ投稿には、入手できなくなった製品やサポートされなくなった製品が参照されている場合があります。 利用可能な F5 NGINX 製品およびソリューションに関する最新情報については、 NGINX 製品ファミリーをご覧ください。 NGINX は現在 F5 の一部です。 以前の NGINX.com リンクはすべて、F5.com の同様の NGINX コンテンツにリダイレクトされます。"