モノリシックアプリケーションからマイクロサービスアーキテクチャへの移行プロセスでは、新しい問題に直面しています。
モノリシックアプリケーションでは、通常、システムのどの部分でエラーが発生したかを判断するのは非常に簡単です。 ほとんどの場合、問題はモノリス自体のコードまたはデータベースにあります。 しかし、マイクロサービスアーキテクチャの問題を探し始めたとき、すべてがそれほど明白ではありません。 数百のマイクロサービスからリクエストを選択するには、リクエストが最初から最後まで進んだパス全体を見つける必要があります。 さらに、それらの多くには独自のリポジトリもあり、これにより論理エラーが発生したり、パフォーマンスやフォールトトレランスの問題が発生したりする可能性があります。
長い間、私はそのような問題に対処するのに役立つツールを探していました(Habréでそれについて書いた: 1、2 )が、最終的に私は私自身のオープンソースソリューションを作りました。 この記事では、サービスメッシュアプローチの利点について説明し、その実装のための新しいツールを共有しています。
分散トレースは、分散システムでエラーを見つける問題の一般的な解決策です。 しかし、システムがネットワークインタラクションに関する情報を収集するためのそのようなアプローチをまだ実装していない場合、またはさらに悪いことに、システムの一部で既に正常に動作し、一部では古いサービスに追加されていないため、そうでない場合はどうでしょうか? 問題の正確な根本原因を特定するには、システムで何が起こっているのかを完全に把握する必要があります。 どのマイクロサービスが主要なビジネスクリティカルパスに関与しているかを理解することが特に重要です。
ここでは、サービスメッシュアプローチが私たちの助けになります。これは、サービス自体が行うことよりも低いレベルでネットワーク情報を収集するためのすべての機械を処理します。 このアプローチにより、すべてのトラフィックを傍受し、その場で分析することができます。 さらに、それについてのアプリケーションは何も知らないはずです。
サービスメッシュアプローチ
サービスメッシュアプローチの主なアイデアは、ネットワーク上に別のインフラストラクチャレイヤーを追加することです。これにより、サービス間のやり取りであらゆることができるようになります。 ほとんどの実装は次のように機能します。透過プロキシを備えた追加のサイドカーコンテナが各マイクロサービスに追加され、すべての着信および発信サービストラフィックが通過します。 これは、クライアントバランシングを実行し、セキュリティポリシーを適用し、リクエストの数に制限を導入し、本番環境でのサービスの相互作用に関する重要な情報を収集できる場所です。
解決策
このアプローチにはすでにいくつかの実装があります: Istioとリンカーd2 。 すぐに使える多くの機能を提供します。 しかし同時に、リソースには大きなオーバーヘッドがかかります。 さらに、このようなシステムが機能するクラスターが大きいほど、新しいインフラストラクチャを維持するためにより多くのリソースが必要になります。 Avitoでは、数千のサービスインスタンスでkubernetesクラスターを運用しています(その数は急速に増え続けています)。 現在の実装では、Istioはサービスインスタンスごとに最大300MbのRAMを消費します。 多数の機能があるため、透過的バランシングはサービスの合計応答時間にも影響します(最大10ミリ秒)。
最終的に、今必要な機能を正確に調べ、そのようなソリューションを実装し始めた主な理由は、システム全体から透過的にトレース情報を収集できることだと判断しました。 また、サービスの相互作用を制御し、サービス間で転送されるヘッダーを使用してさまざまな操作を行いたいと考えました。
最終的に、 Netrameshを決定しました 。
ネトラメッシュ
Netrameshは、システム内のサービスの数に関係なく、無限のスケーラビリティを備えた軽量のサービスメッシュソリューションです。
新しいソリューションの主な目標は、リソースのわずかなオーバーヘッドと高いパフォーマンスです。 主な機能のうち、すぐにトレーススパンをJaegerシステムに透過的に送信できるようにしたいと考えました。
現在、ほとんどのクラウドソリューションはGolangに実装されています。 そして、もちろん、これには理由があります。 I / Oと非同期で動作し、必要に応じてカーネルに合わせて拡張するGolangネットワークアプリケーションを作成することは、便利で非常に簡単です。 また、これも非常に重要であり、パフォーマンスはこの問題を解決するのに十分です。 したがって、Golangも選択しました。
性能
私たちは最大限のパフォーマンスを達成することに注力しています。 サービスの各インスタンスの隣に展開されるソリューションの場合、RAMとプロセッサ時間のわずかな消費が必要です。 そして、もちろん、応答遅延も小さくする必要があります。
結果が何であるか見てみましょう。
RAM
Netrameshは、トラフィックなしで最大10Mbを消費し、インスタンスあたり最大10,000 RPSの負荷で最大50Mbを消費します。
Istio envoyプロキシは、数千のインスタンスを持つクラスターで常に約300Mbを消費します。 これにより、クラスター全体に拡張することはできません。
Netrameshを使用すると、メモリ消費が10分の1以下になりました。
CPU
CPU使用率は、負荷がかかっても比較的等しくなります。 これは、サイドカーへの単位時間あたりのリクエスト数に依存します。 ピーク時の1秒あたり3000リクエストの値:
もう1つの重要なポイントがあります。Netramesh-コントロールプレーンと負荷のないソリューションはCPU時間を消費しません。 Istioを使用すると、サイドカーは常にサービスエンドポイントを更新します。 その結果、負荷のないこのような画像を見ることができます。
サービス間の通信にはHTTP / 1を使用します。 特使を介してプロキシする場合のIstioの応答時間の増加は最大5〜10ミリ秒で、これは1ミリ秒で応答する準備ができているサービスでは非常に多くなります。 Netrameshでは、この時間は0.5〜2ミリ秒に短縮されました。
拡張性
各プロキシが消費するリソースの量が少ないため、各サービスの隣に配置できます。 Netrameshは、各サイドカーの明るさを単純に維持するために、コントロールプレーンコンポーネントなしで意図的に作成されました。 多くの場合、サービスメッシュソリューションでは、コントロールプレーンがサービスディスカバリ情報を各サイドカーに配信します。 それに加えて、タイムアウト、バランス設定に関する情報があります。 これにより、多くの便利なことができますが、残念ながら、サイドカーのサイズが大きくなります。
サービス発見
Netrameshは、サービス検出のための追加メカニズムを追加しません。 すべてのトラフィックは、netraサイドカーを介して透過的にプロキシされます。
Netrameshは、HTTP / 1アプリケーションプロトコルをサポートしています。 ポートの構成可能なリストは、それを決定するために使用されます。 通常、システムにはHTTPで通信するポートがいくつかあります。 たとえば、サービスと外部リクエストのやり取りには80、8890、8080を使用しますが、この場合はNETRA_HTTP_PORTS
環境NETRA_HTTP_PORTS
を使用して設定できNETRA_HTTP_PORTS
。
Kubernetesをオーケストラとして使用し、サービス間のクラスタ内相互作用のためにそのサービスエンティティのメカニズムを使用する場合、メカニズムはまったく同じままです。 最初に、マイクロサービスはkube-dnsを使用してサービスIPアドレスを取得し、それに新しい接続を開きます。 この接続は最初にローカルのnetra-sidecarと確立され、すべてのTCPパケットは最初にnetraに到着します。 次に、netra-sidecarは元の宛先との接続を確立します。 ノードのポッドIPのNATは、netraを使用しない場合とまったく同じままです。
分散トレースとコンテキストスクロール
Netrameshは、HTTP相互作用に関するトレーススパンを送信するために必要な機能を提供します。 Netra-sidecarは、HTTPプロトコルを解析し、要求の遅延を測定し、HTTPヘッダーから必要な情報を取得します。 最終的に、単一のJaegerシステムですべてのトレースを取得します。 微調整のために、公式のjaeger goライブラリが提供する環境変数を使用することもできます 。
しかし、問題があります。 サービスが特別なuberヘッダーを生成して転送するまで、接続されたトレーススパンはシステムに表示されません。 そして、これが問題の原因を迅速に見つけるために必要なものです。 ここでも、Netrameshには解決策があります。 プロキシはHTTPヘッダーを読み取り、uberトレースIDがない場合は生成します。 また、Netrameshはサイドカーに着信および発信リクエストに関する情報を保存し、発信リクエストの必要なヘッダーを充実させることでそれらを比較します。 サービスで行う必要があるのは、 NETRA_HTTP_REQUEST_ID_HEADER_NAME
環境NETRA_HTTP_REQUEST_ID_HEADER_NAME
を使用して構成できるX-Request-Id
ヘッダーを1つだけスローすることNETRA_HTTP_REQUEST_ID_HEADER_NAME
。 Netrameshのコンテキストのサイズを制御するために、次の環境変数を設定できますNETRA_TRACING_CONTEXT_EXPIRATION_MILLISECONDS
(コンテキストが保存される時間)およびNETRA_TRACING_CONTEXT_CLEANUP_INTERVAL
(コンテキストクリーニングの周期)。
システム内のいくつかのパスを特別なセッションマーカーでマークすることで組み合わせることもできます。 Netraでは、 HTTP_HEADER_TAG_MAP
を設定して、HTTPヘッダーを適切なトレーススパンタグにHTTP_HEADER_TAG_MAP
できます。 これは、テストに特に役立ちます。 機能テストに合格すると、対応するセッションキーによるフィルタリングによって、システムのどの部分が影響を受けたかを確認できます。
リクエストのソースの決定
要求がどこから来たかを判断するには、関数を使用して、ソースにヘッダーを自動的に追加します。 環境変数NETRA_HTTP_X_SOURCE_HEADER_NAME
を使用して、自動的に設定されるヘッダーの名前を指定できます。 NETRA_HTTP_X_SOURCE_VALUE
を使用して、すべての発信要求に対してX-Sourceヘッダーを設定する値を設定できます。
これにより、ネットワーク全体で均一にこの便利なヘッダーを配布できます。 その後、すでにサービスで使用して、ログとメトリックに追加できます。
Netrameshトラフィックと内部ルーティング
Netrameshは2つの主要コンポーネントで構成されています。 最初のnetra-initは、トラフィックを傍受するためのネットワークルールを設定します。 iptablesリダイレクトルールを使用して、Netrameshの2番目の主要コンポーネントであるサイドカーのトラフィックのすべてまたは一部をインターセプトします。 着信および発信TCPセッションに対してインターセプトするポートを構成できます: INBOUND_INTERCEPT_PORTS, OUTBOUND_INTERCEPT_PORTS
。
このツールには興味深い機能があります-確率的ルーティングです。 トレーススパンの収集専用にNetrameshを使用する場合、実稼働環境では、変数NETRA_INBOUND_PROBABILITY
およびNETRA_OUTBOUND_PROBABILITY
(0から1)を使用してリソースを節約し、確率的ルーティングを有効にできます。 デフォルト値は1です(すべてのトラフィックがインターセプトされます)。
インターセプトに成功すると、netraサイドカーは新しい接続を受け入れ、 SO_ORIGINAL_DST
ソケットオプションを使用して元の宛先を取得します。 次に、Netraは元のIPアドレスへの新しい接続を開き、当事者間の双方向TCP通信を確立し、通過するすべてのトラフィックをリッスンします。 ポートがHTTPとして定義されている場合、Netraはそれを解析してルーティングしようとします。 HTTP解析が失敗した場合、NetraはTCPにフォールバックし、バイトを透過的にプロキシします。
依存関係グラフの作成
Jaegerで多くのトレース情報を受け取った後、システム内の相互作用の完全なグラフを取得したいと思います。 しかし、システムに十分な負荷がかかり、1日で数十億のトレーススパンが蓄積される場合、それらの集計を行うことはそれほど簡単な作業ではありません。 これを行う公式の方法があります: spark-dependencies 。 ただし、完全なグラフを作成し、過去24時間にわたってデータセット全体をJaegerから強制的にダウンロードするには数時間かかります。
Elasticsearchを使用してトレーススパンを保存する場合、Elasticsearchの機能を使用して数分で同じグラフを作成するGolangのシンプルなユーティリティを使用できます。
Netrameshの使用方法
Netraは、オーケストレーターを実行しているサービスに簡単に追加できます。 ここに例を見ることができます 。
現時点では、Netraにはサイドカーをサービスに自動的に展開する機能はありませんが、実装の計画があります。
未来のネトラメッシュ
Netrameshの主な目標は、最小限のリソースコストと高いパフォーマンスを実現し、サービス間の相互作用を観察および制御するための主な機会を提供することです。
将来、NetrameshはHTTP以外のアプリケーションレベルのプロトコルのサポートを受け取ります。 近い将来、L7ルーティングの可能性があります。
同様の問題が発生した場合はNetrameshを使用し、質問や提案をお寄せください。