はじめに
こんにちは、インターン生の山口雄翔です。
この度2023年2月6日から2週間、NTTコミュニケーションズのインターンシップに、 エンタープライズ向け大規模クラウドサービスを支えるネットワーク開発 というテーマで参加させていただきました。 この記事ではその体験について書かせていただきます。
インターンシップについて
NTTコミュニケーションズでは、SDPFクラウド/サーバーというエンタープライズ向けクラウドサービスを提供しています。 このサービスでは、仮想的なL2ネットワークを自由に構築し、そこにベアメタルサーバ・VM・ストレージなどを(論理的に)接続させて使うことができます。 もちろんインターネット接続やロードバランサなどをサービスとして利用することもできます。
そのクラウドの内部のネットワークを制御するSDNコントローラの改善というのが、本インターンシップのテーマでした。 前半1週間はこのクラウド内のネットワークやSDNとその関連技術について教えてもらいながら学び、後半1週間でコントローラの性能改善に取り組みました。
クラウド内のネットワークとSDNについて
概要
クラウド内のネットワーク構成はこのようになっています。(紫で印をつけた部分がSDNコントローラ) クラウドを構成するデータセンタのスイッチは全て隣接するスイッチとBGPで接続していて、全体に対して疎通性があります。 機能を持つノード(ハイパーバイザ・ベアメタルサーバ・ゲートウェイ等)はLeafスイッチの下にあります。
VMを収容するハイパーバイザが乗っているマシン上では、VMに関係する経路情報をやり取りするための仮想ルータが動いています。
EVPN/VXLAN
VMやベアメタルサーバ同士のL2接続性を確保するために、VXLANとEVPNを利用しています。
EVPN(Ethernet VPN)とは、仮想的にL2接続性を提供するためのVPNのことです。
- 仮想L2ネットワークに接続するノードのMACアドレス
- VPNのエンドポイントのIPアドレス
- 仮想L2ネットワークの識別子
などをMP-BGPに乗せて交換することで、L2接続に必要な情報を共有し、ネットワークをスケールさせることができます。
https://datatracker.ietf.org/doc/html/rfc8365
https://datatracker.ietf.org/doc/html/rfc7432
VXLANはパケットをカプセル化することで、L3ネットワーク上に論理的なL2ネットワークを構築するトンネリングプロトコルです。
MACアドレスなどの情報をEVPNで交換したのち、VXLANでL2トンネルを作ることによって、L2接続性を実現しています。
図のようにencap/decapを行うことで、仮想L2ネットワークを実現しています。 (encap/decapをするVXLANトンネルのエンドポイントのことを、VTEPと呼びます。)
このように仮想ネットワークが実現できるような経路制御の管理をするのも、SDNコントローラの重要な役割のひとつです。 (次項参照)
SDNコントローラ
SDPFクラウド/サーバーでは、SDNコントローラにTungsten Fabricを採用しています。
SDNコントローラは、
- 仮想L2ネットワーク等のリソース作成のためのインターフェースの提供およびVTEPへの設定投入
- 経路情報の計算と広告
- コントローラ自身やリソース情報のメトリクスの収集
を持っています。 (3についてはインターンシップで扱っていないため、この記事では触れません。)
1について
クラウド内の仮想計算機基盤はOpenStackを利用して作られています。 OpenStackのネットワーク周りを管理するNWコントローラとTungsten Fabricを連携させることにより、仮想基盤側から投入された設定が、SDNコントローラを経由してネットワークに入るような設計を実現しています。
2について
SDNコントローラは、クラウドのネットワーク内で仮想L2ネットワークを作るための経路情報の計算・広告をする役割を担っています。 以下の図は、コントローラとネットワークの各コンポーネントの論理的な繋がりを示しています。
コントローラと仮想ルーターの経路情報のやり取りでは、XMPPというプロトコルに乗せてEVPNの経路情報のやり取りをしています。 物理ルータがVTEPとなっている箇所については、Route Reflector(以下RRと示す)がコントローラと物理ルーターの双方とBGPピアを張ってEVPNの経路情報を交換することで、コントローラからの経路情報をRR経由でVTEPに伝えることができます。
対外発表資料
SDPFクラウド/サーバーについての技術的な対外発表の資料を以下にまとめましたので、ご興味のある方はぜひご覧ください。
- JANOG: EVPN Anycast Gateway を 商⽤導⼊した話 : https://www.janog.gr.jp/meeting/janog48/evpn/
- JANOG: エンタープライズ向けクラウドのSDN基盤の安定化への挑戦 : https://www.janog.gr.jp/meeting/janog44/program/sdnclg/
- NTT Com Open TechLunch: クラウドの作り方 : https://speakerdeck.com/toby06/how-to-create-cloud-service
今回取り組んだ問題について
コントローラ内での経路の保持と複製処理
SDNコントローラはクラウドネットワークの経路情報を全て把握しています。 コントローラの内部では、各仮想ネットワークごとのルーティングテーブル(VRF)とFabric全体の経路を保持するグローバルルーティングテーブルを持っており、EVPNについてもこの構成で経路を保持しています。
そしてVRFテーブルとグローバルルーティングテーブルの間での経路の複製処理がイベントドリブンで実行されるようになっているのですが、コントローラのこの動作が時間のかかる処理となっています。 (Tungsten Fabric は複数コアを用いたマルチスレッドに対応していますが、経路複製がスレッドセーフでない処理を含むため、1コアのみを用いて順番に処理を行うタスクキューに入るようになっており、時間がかかってしまいます。)
ライブマイグレーションなどに伴う経路計算の際に、この部分の計算時間がボトルネックとなって経路計算の完了が遅れ、それによって通信ができない時間が発生してしまいます。
それを防ぐためには経路複製処理の速度の改善が必要であり、今回のインターンシップではその問題に取り組みました。
作業
実は経路複製処理のコード改善というのはインターンシップの前からチームで取り組まれていて、upstreamにmergeされている変更もあります。 (その説明はこちらの資料に書いてあります。) その中で同じ関数内に更なる改良の余地がありそうだとが分かり、自分はそれに取り組ませていただきました。
まずは教えてもらった関数内の処理内容を把握した上で、部分ごとに分けて時間を測りました。
この関数の中には、
- 経路を複製すべき宛先テーブルを種々の条件から絞る処理
- 宛先テーブルをRouteTarget等から決定する処理
- 実際に経路情報をメモリコピーする処理
などが含まれており、それらの処理ごとの時間をログに出力したのちpythonスクリプトを使って結果を見やすく表示しました。 (下はその様子です。)
そして、計測結果・プログラムのコード・そこで扱われる経路情報の3つを見ながら考えて、処理時間を短縮できそうな箇所を見つけました。
コード改善
アルゴリズム
コントローラが保持している経路(BGP route)には、複数のpathを持つものがあります。 例えば3つのRRから同じ経路を受け取った際、src addrだけ異なる3つのpathがrouteに登録されます。 ある1つのrouteの複製は、含まれるpathごとにループで行われる実装となっていて、その中でpathの複製先EVPNテーブルを選定していました。 (pathの中の拡張コミュニティに含まれるRouteTarget等の情報からテーブルを探索し、複製先テーブルリストを作っています。)
ですが同じrouteに属するpathは(自分が検証環境でチェックした限りでは)複製先テーブルは同じであったため、ここの処理を1つにまとめることで、性能改善ができるのではないかと考えました。
この効果は、多量(ほぼ全て)のVRFテーブルに複製する必要があるEVPN Type1経路において顕著に現れました。 (EVPN Type1経路はマルチホーミングの情報を広告するためのものです。)
実装結果・考察
改善前と後のパフォーマンス計測の結果
before after
この写真は経路複製をする関数の中の部分ごとの処理時間を出力したものです。 印をつけた部分が複製先EVPNテーブルを選定して該当するテーブルをリストに挿入している操作なのですが、コード改善後に処理時間が大幅に短縮されていることが分かります。
この関数全体の処理時間(左端の数値)は1割程度の削減でき、性能として10%程度の改善を実現できたと言えます。 (数値の単位は全てマイクロ秒です。 )
処理の測定、実装を通して想定通り性能改善を得ることはできました。 一方で、インターンシップの間ではその他に極端にボトルネックとなっているような処理を見つけることはできず、処理速度の大幅改善の難しさなども実感しました。
課題
時間がなくてできませんでしたが、複製先テーブル計算の処理をまとめても本当に大丈夫かというのを、ロジック・動作検証の両面から確認してゆく必要があるかなと思いました。 (商用環境へのデプロイまでには他にも様々な段階を踏んだ検証・テストが必要で、今回自分が書いたコードがそのまま乗ることはありません。)
感じたこと
大規模ネットワークを制御するSDNコントローラを作る難しさ
プログラム中の本当に些細なロジックの誤りであっても、コントローラから経路情報が流れる中でその影響がクラウド内に広く波及して、全体として見た時に大きなエラーになってしまうことが多々あると思いました。(実際自分も検証環境を壊しかけました。) 大規模SDNコントローラだからこそコード1行1行にこだわって書かなければいけないなと思いましたし、そのような環境で日々コードを書かれているチームメンバーの皆さんの、コードを検証する時の鋭さを強く感じました。
また小規模な検証環境で動かした時には気にならなかった少しの遅延が、大規模環境に移した途端顕著になるということも経験し、コードを最適化する重要性を身をもって体験できました。
デバッグ
SDNを用いる場合、コントロールプレーンでのエラーは基本SDNコントローラに原因があります。 原因となるコンポーネントの特定という意味では分かりやすい反面、ロジックの難しさやネットワークの状態管理の煩雑さが全てこのソフトウェアの内部に含まれるため、コードや挙動が非常に複雑です。 またそもそもエラーを再現すること自体が難しく、デバッグに高度な技術力や経験が必要であるなと感じました。
人間よりも機械の気持ちが分かる(他称)というある先輩は、エラーが起こった時迷いなくgdbでこの巨大でステートフルなプログラムの動作を見に行っていました。つよい...
謝辞
インターンシップに向けて検証環境整備や資料準備をしてくださり、期間中は丁寧に業務のことを教えてくださったチームの皆さんのおかげで、濃くて楽しい2週間になりました。本当に本当にありがとうございました。
本来は自分が参加した部署ではインターンブログを書くことはあまりないそうなのですが、とても面白くて素晴らしい体験ができたので、技術的なことを含めて是非共有したいと思い、無理を言って書かせていただきました。 (メールで質問攻めにしてしまいすみません。) 長い文章となってしまいましたが、多くの方にこのブログを読んでいただけると嬉しいです。