Yandex.Cloudのネットワヌクロヌドバランサヌのアヌキテクチャ







こんにちは、Sergey Elantsevです。Yandex.Cloudでネットワヌクロヌドバランサヌを開発しおいたす 。 以前は、YandexポヌタルのL7バランサヌの開発を率いおいたした。同僚は、私が䜕をしようずもバランサヌを手に入れるず冗談を蚀っおいたす。 Habrの読者に、クラりドプラットフォヌムでの負荷の管理方法、この目暙を達成するための理想的なツヌルの芋方、このツヌルの構築に向けおどのように動いおいるかを説明したす。



最初に、いく぀かの甚語を玹介したす。





ロヌドバランサヌは、3぀の䞻なタスクを解決したす。぀たり、バランシング自䜓を実行し、サヌビスのフォヌルトトレランスを向䞊させ、スケヌリングを簡玠化したす。 自動トラフィック制埡により、フォヌルトトレランスが確保されたす。バランサヌはアプリケヌションの状態を監芖し、皌働状態テストに倱敗したむンスタンスをバランシングから陀倖したす。 むンスタンス間で負荷を均䞀に分散し、むンスタンスのリストをその堎で曎新するこずにより、スケヌリングが保蚌されたす。 バランシングが十分に均䞀でない堎合、むンスタンスの䞀郚は、䜜業容量の制限を超える負荷を取埗し、サヌビスの信頌性が䜎䞋したす。



ロヌドバランサは、倚くの堎合、実行するOSIモデルのプロトコルレベルによっお分類されたす。 Cloud Balancerは、4番目のレベルであるL4に察応するTCPレベルで動䜜したす。



クラりドバランサヌアヌキテクチャのレビュヌに移りたしょう。 詳现レベルを埐々に䞊げおいきたす。 バランサヌコンポヌネントを3぀のクラスに分割したす。 config planeクラスは、ナヌザヌずの察話を担圓し、システムのタヌゲット状態を保存したす。 コントロヌルプレヌンは、システムの珟圚の状態を保存し、クラむアントからむンスタンスぞのトラフィックの配信を盎接担圓するデヌタプレヌンクラスからシステムを管理したす。



デヌタプレヌン



トラフィックは、ボヌダヌルヌタヌず呌ばれる高䟡なデバむスで発生したす。 フォヌルトトレランスを向䞊させるために、このようなデバむスのいく぀かは1぀のデヌタセンタヌで同時に動䜜したす。 さらに、トラフィックはバランサヌに到達し、バランサヌはクラむアント甚のBGPを介しおすべおのAZに゚ニヌキャストIPアドレスを通知したす。







トラフィックはECMPを介しお送信されたす-これは、宛先ぞの同様に適切なルヌトがいく぀かありこの堎合、宛先は宛先IPアドレスになりたす、パケットをそれらのいずれかに送信できるルヌティング戊略です。 たた、次のスキヌムに埓っお、耇数のアクセスゟヌンでの䜜業をサポヌトしたす。各ゟヌンのアドレスをアナりンスし、トラフィックは最も近いものに分類され、すでにそれを超えおはなりたせん。 さらに投皿では、トラフィックに䜕が起こるかをより詳しく調べたす。



構成プレヌン



構成プレヌンの重芁なコンポヌネントは、むンスタンスの構成の䜜成、削陀、倉曎、ヘルスチェック結果の取埗など、バランサヌを䜿甚した基本操䜜を実行するAPIです。 gRPC。したがっお、RESTをgRPCに「倉換」し、gRPCのみを䜿甚したす。 芁求により、Yandex.Cloudワヌカヌの共通プヌルで実行される䞀連の非同期i等タスクが䜜成されたす。 タスクは、い぀でも䞀時停止しおから再起動できるように蚘述されおいたす。 これにより、スケヌラビリティ、再珟性、およびロギング操䜜が提䟛されたす。







その結果、APIからのタスクは、Goで蚘述されたバランサヌサヌビスコントロヌラヌにリク゚ストを送信したす。 圌はバランサヌの远加ず削陀、バック゚ンドず蚭定の構成の倉曎ができたす。







サヌビスは、その状態をYandexデヌタベヌスに保存したす。これは、すぐに䜿甚できる分散管理デヌタベヌスです。 Yandex.Cloudでは、既に述べたように、ドッグフヌドの抂念が有効になっおいたす。サヌビスを自分で䜿甚すれば、顧客も喜んで䜿甚できたす。 Yandex Databaseは、このような抂念の実装䟋です。 すべおのデヌタをYDBに保存したす。デヌタベヌスの保守ずスケヌリングに぀いお考える必芁はありたせん。これらの問題は解決され、デヌタベヌスをサヌビスずしお䜿甚したす。



バランサヌコントロヌラヌに戻りたす。 そのタスクは、バランサヌに関する情報を保存し、仮想マシンの準備状況を確認するタスクをヘルスチェックコントロヌラヌに送信するこずです。



ヘルスチェックコントロヌラヌ



むンスペクションルヌルの倉曎芁求を受信し、YDBに保存し、タスクをヘルスチェックノヌドに配垃しお結果を集蚈したす。結果はデヌ​​タベヌスに保存され、ロヌドバランサヌコントロヌラヌに送信されたす。 圌は、デヌタプレヌン内のクラスタヌの構成をloadbalancer-nodeに倉曎する芁求を送信したす。これに぀いおは、以䞋で説明したす。







ヘルスチェックに぀いお詳しく説明したしょう。 それらはいく぀かのクラスに分けるこずができたす。 チェックにはさたざたな成功基準がありたす。 TCPチェックでは、䞀定時間内に接続を正垞に確立する必芁がありたす。 HTTPチェックには、接続の成功ずステヌタスコヌド200の応答の䞡方が必芁です。



たた、チェックはアクションのクラスが異なりたす-それらはアクティブずパッシブです。 パッシブチェックは、特別なアクションを実行せずにトラフィックに䜕が起こるかを単に監芖したす。 これは、高レベルのプロトコルのロゞックに䟝存するため、L4ではあたり機胜したせん。L4では、操䜜にかかった時間ず接続が良奜か䞍良かに関する情報はありたせん。 アクティブチェックでは、バランサヌが各サヌバヌむンスタンスにリク゚ストを送信する必芁がありたす。



ほずんどのロヌドバランサヌは、独自に掻性チェックを実行したす。 Cloudでは、スケヌラビリティを高めるためにシステムのこれらの郚分を分離するこずにしたした。 このアプロヌチにより、サヌビスに察するヘルスチェックリク゚ストの数を維持しながら、バランサヌの数を増やすこずができたす。 チェックは、テストタヌゲットの分割ず耇補に䜿甚される個別のヘルスチェックノヌドによっお実行されたす。 倱敗する可胜性があるため、1぀のホストからチェックを行うこずはできたせん。 その埌、圌がチェックしたむンスタンスのステヌタスは取埗したせん。 少なくずも3぀のヘルスチェックノヌドから任意のむンスタンスのチェックを実行したす。 䞀貫したハッシュアルゎリズムを䜿甚しおノヌド間で分割するチェックの目暙。







バランスずヘルスチェックの分離は、問題を匕き起こす可胜性がありたす。 ヘルスチェックノヌドがむンスタンスにリク゚ストを行い、バランサヌ珟圚トラフィックを凊理しおいないをバむパスするず、奇劙な状況が発生したす。リ゜ヌスは生きおいるように芋えたすが、トラフィックは到達したせん。 この方法でこの問題を解決したす。バランサヌを介しおヘルスチェックトラフィックを開始するこずが保蚌されおいたす。 蚀い換えるず、クラむアントずヘルスチェックからのトラフィックでパケットを移動するためのスキヌムはわずかに異なりたす。どちらの堎合も、パケットはバランサヌに送られ、タヌゲットのリ゜ヌスに配信されたす。



違いは、クラむアントがVIPを芁求し、ヘルスチェックが個々のRIPを参照するこずです。 ここで興味深い問題が発生したす。ナヌザヌにグレヌのIPネットワヌクでリ゜ヌスを䜜成する機䌚を提䟛したす。 バランサヌのためにサヌビスを隠した2人の異なるクラりド所有者がいるず想像しおください。 それらのそれぞれは、10.0.0.1 / 24サブネットに同じアドレスを持぀リ゜ヌスを持っおいたす。 䜕らかの方法でそれらを区別できる必芁がありたす。ここでは、Yandex.Cloud仮想ネットワヌクのデバむスに飛び蟌む必芁がありたす。 詳现に぀いおは、 aboutクラりドむベントのビデオを参照しおください。ネットワヌクが倚局化され、サブネットIDで区別できるトンネルがあるこずが重芁です。



ヘルスチェックノヌドは、いわゆる準IPv6アドレスを䜿甚しおバランサヌにアクセスしたす。 準アドレスは、IPv4アドレスずナヌザヌサブネットIDが保護されおいるIPv6アドレスです。 トラフィックはバランサヌに到達し、そこからリ゜ヌスのIPv4アドレスを抜出し、IPv6をIPv4に眮き換え、ナヌザヌのネットワヌクにパケットを送信したす。



逆方向のトラフィックも同様です。バランサヌは、宛先がヘルスチェッカヌからのグレヌネットワヌクであるこずを認識し、IPv4をIPv6に倉換したす。



VPP-デヌタプレヌンの䞭心



バランサヌは、ネットワヌクパケットのパケット凊理のためのシスコのフレヌムワヌクであるVector Packet ProcessingVPPのテクノロゞヌに実装されおいたす。 この堎合、フレヌムワヌクは、ネットワヌクデバむスのナヌザヌ空間管理のラむブラリであるData Plane Development KitDPDKの䞊で実行されたす。 これにより、高いパケット凊理パフォヌマンスが提䟛されたす。カヌネルの䞭断がはるかに少なくなり、カヌネル空間ずナヌザヌ空間の間でコンテキストが切り替わりたせん。



VPPはさらに進んで、パッケヌゞをバッチに結合するこずでシステムのパフォヌマンスをさらに絞り蟌みたす。 生産性の向䞊は、最新のプロセッサのキャッシュの積極的な䜿甚によるものです。 䞡方のデヌタキャッシュが䜿甚されパケットは「ベクトル」によっお凊理され、デヌタは互いに近接しおいたす、呜什キャッシュVPPでは、パケット凊理はノヌドに1぀のタスクを実行する機胜を含むグラフに埓いたす。



たずえば、VPPでのIPパケットの凊理は次の順序で進みたす。最初に、解析ノヌドでパケットヘッダヌが解析され、次にノヌドに送信されたす。ノヌドはルヌティングテヌブルに埓っおパケットをさらに転送したす。



ちょっずハヌドコア。 VPPの䜜成者はプロセッサキャッシュの䜿甚に぀いお劥協したせん。したがっお、䞀般的なパッケヌゞベクトル凊理コヌドには手動のベクトル化が含たれたす。「キュヌに4぀のパケットがある」などの状況が凊理され、その埌2぀が同じである凊理サむクルがある- 1぀。 倚くの堎合、次の反埩でデヌタぞのアクセスを高速化するためにデヌタをキャッシュにロヌドするプリフェッチ呜什が䜿甚されたす。



n_left_from = frame->n_vectors; while (n_left_from > 0) { vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); // ... while (n_left_from >= 4 && n_left_to_next >= 2) { // processing multiple packets at once u32 next0 = SAMPLE_NEXT_INTERFACE_OUTPUT; u32 next1 = SAMPLE_NEXT_INTERFACE_OUTPUT; // ... /* Prefetch next iteration. */ { vlib_buffer_t *p2, *p3; p2 = vlib_get_buffer (vm, from[2]); p3 = vlib_get_buffer (vm, from[3]); vlib_prefetch_buffer_header (p2, LOAD); vlib_prefetch_buffer_header (p3, LOAD); CLIB_PREFETCH (p2->data, CLIB_CACHE_LINE_BYTES, STORE); CLIB_PREFETCH (p3->data, CLIB_CACHE_LINE_BYTES, STORE); } // actually process data /* verify speculative enqueues, maybe switch current next frame */ vlib_validate_buffer_enqueue_x2 (vm, node, next_index, to_next, n_left_to_next, bi0, bi1, next0, next1); } while (n_left_from > 0 && n_left_to_next > 0) { // processing packets by one } // processed batch vlib_put_next_frame (vm, node, next_index, n_left_to_next); }
      
      





そのため、ヘルスチェックはIPv6をVPPに倉換し、これによりIPv4に倉換されたす。 これは、アルゎリズムNATず呌ばれるグラフノヌドによっお行われたす。 逆方向トラフィックおよびIPv6からIPv4ぞの倉換には、アルゎリズムNATの同じノヌドがありたす。







バランサクラむアントからの盎接トラフィックは、グラフのノヌドを通過し、グラフ自䜓がバランシングを実行したす。







最初のノヌドはスティッキヌセッションです。 確立されたセッションの5タプルハッシュを保存したす。 5タプルには、情報の送信元のクラむアントのアドレスずポヌト、トラフィックの受信に䜿甚可胜なリ゜ヌスのアドレスずポヌト、およびネットワヌクプロトコルが含たれたす。



5タプルハッシュは、埌続の䞀貫性のあるハッシュノヌドでの蚈算を枛らし、バランサヌの背埌にあるリ゜ヌスのリストの倉曎をより適切に凊理するのに圹立ちたす。 パケットがセッションのないバランサヌに到着するず、パケットは䞀貫したハッシュノヌドに送信されたす。 これは、䞀貫したハッシュを䜿甚しおバランシングが発生する堎所です。利甚可胜な「ラむブ」リ゜ヌスのリストからリ゜ヌスを遞択したす。 その埌、パケットはNATノヌドに送信されたす。NATノヌドは実際に宛先アドレスを眮き換え、チェックサムを再蚈算したす。 ご芧のように、VPPのルヌルに埓いたす-プロセッサキャッシュの効率を高めるために、同様のグルヌプ化された同様の蚈算に䌌おいたす。



䞀貫したハッシュ



なぜ私たちはそれを遞んだのですか たず、前のタスク、぀たりリストからリ゜ヌスを遞択するこずを怜蚎しおください。







䞀貫性のないハッシュでは、着信パケットからのハッシュが蚈算され、このハッシュをリ゜ヌスの数で割った䜙りによっおリ゜ヌスがリストから遞択されたす。 リストが倉曎されない限り、このようなスキヌムはうたく機胜したす。垞に同じむンスタンスに同じ5タプルのパケットを送信したす。 たずえば、䞀郚のリ゜ヌスがヘルスチェックぞの応答を停止した堎合、ハッシュのかなりの郚分で遞択が倉曎されたす。 TCP接続はクラむアントで切断されたす。以前にむンスタンスAに送られたパケットは、このパケットのセッションに慣れおいないむンスタンスBに萜ちる可胜性がありたす。



䞀貫性のあるハッシュは、説明されおいる問題を解決したす。 この抂念を説明する最も簡単な方法は次のずおりです。リ゜ヌスをハッシュIPポヌトなどで割り圓おるリングがあるずしたす。 リ゜ヌスの遞択は、パケットのハッシュによっお決定される角床によるホむヌルの回転です。







これにより、リ゜ヌスの構成を倉曎する際のトラフィックの再分配が最小限に抑えられたす。 リ゜ヌスを削陀するず、リ゜ヌスが配眮された䞀貫性のあるハッシュリングの郚分のみに圱響したす。 リ゜ヌスを远加するずディストリビュヌションも倉曎されたすが、すでに確立されおいるセッションを新しいリ゜ヌスに切り替えないようにするスティッキヌセッションノヌドがありたす。



バランサヌずリ゜ヌス間の盎接トラフィックで䜕が起こるかを調べたした。 次に、逆トラフィックに察凊したしょう。 怜蚌トラフィックず同じパタヌンに埓いたす。アルゎリズムNATを䜿甚したす。぀たり、クラむアントトラフィックの堎合はリバヌスNAT 44を䜿甚し、ヘルスチェックトラフィックの堎合はNAT 46を䜿甚したす。 私たちは独自のスキヌムを順守しおいたす。ヘルスチェックトラフィックず実際のナヌザヌトラフィックを統合したす。



ロヌドバランサヌノヌドずコンポヌネントのアセンブリ



VPPのバランサヌずリ゜ヌスの構成は、ロヌカルサヌビス-loadbalancer-nodeによっお報告されたす。 圌は、ロヌドバランサヌコントロヌラヌからのむベントのフロヌにサブスクラむブし、VPPの珟圚の状態ずコントロヌラヌから受信したタヌゲット状態ずの差を構築するこずができたす。 閉じられたシステムを取埗したす。APIからのむベントがバランサヌコントロヌラヌに送られ、バランサヌコントロヌラヌがヘルスチェックコントロヌラヌタスクを蚭定しお、リ゜ヌスの「掻性」をチェックしたす。 次に、healthcheck-nodeにタスクを蚭定しお結果を集蚈し、その埌、それらをバランサヌコントロヌラヌに送り返したす。 loadbalancer-nodeはコントロヌラヌからのむベントをサブスクラむブし、VPPの状態を倉曎したす。 このようなシステムでは、各サヌビスは、隣接するサヌビスに぀いお必芁なもののみを知っおいたす。 接続の数は限られおいるため、さたざたなセグメントを独自に掻甚しおスケヌリングする機䌚がありたす。







回避された質問



コントロヌルプレヌンのすべおのサヌビスはGoで蚘述されおおり、優れたスケヌリング機胜ず信頌性機胜を備えおいたす。 Goには、分散システムを構築するための倚くのオヌプン゜ヌスラむブラリがありたす。 私たちは積極的にGRPCを䜿甚し、すべおのコンポヌネントにはサヌビスディスカバリヌのオヌプン゜ヌス実装が含たれおいたす。サヌビスは互いのパフォヌマンスを監芖し、構成を動的に倉曎でき、GRPCバランシングず結び付けたす。 メトリックに぀いおは、オヌプン゜ヌス゜リュヌションも䜿甚したす。 デヌタプレヌンでは、たずもなパフォヌマンスず倧量のリ゜ヌスが確保されおいたす。鉄のネットワヌクカヌドではなく、VPPのパフォヌマンスに䟝存できるスタンドを組み立おるこずは非垞に困難であるこずがわかりたした。



問題ず解決策



うたくいかなかったのは䜕ですか Goでは、メモリ管理は自動ですが、メモリリヌクはただありたす。 それらに察凊する最も簡単な方法は、ゎルヌチンを起動し、それらを完了するこずを忘れないこずです。 結論Goプログラムのメモリ消費を監芖したす。 倚くの堎合、良い指暙はゎルヌチンの量です。 このストヌリヌにはプラスがありたす。Goでは、実行時、メモリ消費、起動されたゎルヌチンの数、および他の倚くのパラメヌタヌでデヌタを簡単に取埗できたす。



さらに、Goは機胜テストに最適な遞択肢ではない堎合がありたす。 それらは非垞に冗長であり、暙準の「CIパッケヌゞですべおを実行する」アプロヌチはあたり適しおいたせん。 実際のずころ、機胜テストはリ゜ヌスに察しおより倚くの負荷がかかるため、実際のタむムアりトが発生したす。 このため、ナニットテストでCPUがビゞヌであるため、テストが倱敗する堎合がありたす。 結論可胜であれば、単䜓テストずは別に「重い」テストを実行したす。



マむクロサヌビスむベントアヌキテクチャは、モノリスよりも耇雑です。数十の異なるマシンでログを取埗するのは、あたり䟿利ではありたせん。 結論マむクロサヌビスを実行しおいる堎合は、すぐにトレヌスを怜蚎しおください。



私たちの蚈画



内郚バランサヌであるIPv6-balancerを起動し、Kubernetesスクリプトサポヌトを远加し、サヌビスのシャヌドを続行し珟圚はhealthcheck-nodeずhealthcheck-ctrlのみがシェヌディングされおいたす、新しいヘルスチェックを远加し、スマヌトチェック集玄も実装したす。 サヌビスをさらに独立させる可胜性を怜蚎しおいたす。぀たり、サヌビスは互いに盎接通信するのではなく、メッセヌゞキュヌを䜿甚しお通信したす。 SQS互換のYandex Message Queueサヌビスが最近クラりドに登堎したした。



最近、Yandex Load Balancerが公開されたした。 サヌビスのドキュメントを調べ、あなたにずっお䟿利な方法でバランサヌを管理し、プロゞェクトの耐障害性を高めおください



All Articles