Virtual network device and its application in cloud native
質問してみましょう
VLAN について聞いたことがありますか?正式名称は Virtual Local Area Network(仮想ローカルエリアネットワーク)で、イーサネット内で異なるブロードキャストドメインを分離するために使用されます。非常に早い時期に誕生しました。1995 年に IEEE が 802.1Q 規格 [1] を公開し、イーサネットデータフレームにおける VLAN のフォーマットを定義しており、現在まで使用され続けています。VLAN をご存知であれば、MACVlan や IPVlan についてはどうでしょうか?コンテナ技術の台頭に伴い、Linux の仮想ネットワークデバイスである IPVlan と MACVlan が徐々に注目を集めるようになりました。2017 年の Docker Engine バージョン 1.13.1 [2] において、IPVlan と MACVlan がコンテナネットワークソリューションとして導入されています。
以下の疑問をお持ちではありませんか?
1. VLAN と IPVlan、MACVlan の間にはどのような関係があるのか?なぜ名前に VLAN が含まれているのか?
2. なぜ IPVlan と MACVlan には VEPA、Private、Passthrough など、さまざまなモードやフラグがあるのか?それぞれの違いは何か?
3. IPVlan と MACVlan にはどのような利点があるのか?どのような場面で使用すべきか?
かつて私も同じ疑問を持っていました。本記事では、上記の 3 つの疑問について探求していきます。
背景知識
以下にいくつかの背景知識を紹介します。Linux に詳しい方は読み飛ばしていただいて構いません。
・ネットワークデバイスの抽象化の確認
Linux では、ip コマンドまたは ifconfig コマンドを使用してネットワークデバイスを操作します。ip コマンドの実装である iproute2 が実際に依存しているのは、Linux が提供する netlink メッセージメカニズムです。カーネルは、ネットワークデバイスの種類(実在か仮想かを問わず)ごとに、netlink メッセージに応答する専用構造体を抽象化しており、それらはすべて rtnl_link_ops 構造体に準拠してネットワークデバイスの作成、破棄、変更に応答します。たとえば、より直感的な Veth デバイスの例を見てみましょう。
ネットワークデバイスにとって、Linux の操作とハードウェアデバイスの応答にも一連の仕様が必要です。Linux はこれを net_device_ops 構造体として抽象化しています。デバイスドライバーに興味がある方は、主にこの構造体を扱うことになります。Veth デバイスを例に取ってみましょう。
上記の定義から、いくつか意味的に直感的なメソッドを確認できます。ndo_start_xmit はパケットの送信に使用され、newlink は新しいデバイスの作成に使用されます。
パケットの受信について、Linux のパケット受信アクションは各プロセス自身が実行するのではなく、ドライバー、ネットワーク層(IP、iptables)、トランスポート層(TCP、UDP)から ksoftirqd カーネルスレッドが処理し、最終的にユーザープロセスが保持するソケットの recv バッファーに配置されます。その後、カーネルの inotify がユーザープロセスに通知します。仮想デバイスの場合、すべての差異はネットワーク層より前に集中しており、統一されたエントリーポイント、すなわち __netif_receive_skb_core があります。
・802.1Q プロトコルにおける VLAN の定義
802.1Q プロトコルでは、イーサネットデータフレームヘッダーをマークするために使用される VLAN フィールドは 32 ビットのドメインで、構造は以下の通りです。
上図に示す通り、プロトコルラベルに 16 ビット、優先度ラベルに 3 ビット、フォーマットラベルに 1 ビット、VLAN ID の格納に 12 ビットが割り当てられています。VLAN に基づいていくつのブロードキャストドメインに分割できるか、簡単に計算できると思います。そう、2^12 = 4096 です。全 0 と全 1 の予約値を差し引くと、ユーザーは 4094 の利用可能なブロードキャストドメインに分割できます。(OpenFlow の台頭以前、クラウドコンピューティングの最初期のプロトタイプにおける VPC 実装はネットワーク差別化に VLAN に依存していましたが、この制限によりすぐに淘汰され、皆さんにもおなじみのもう一つの用語、VxLAN が生まれました。両者は大きく異なりますが、参照すべき理由は依然としてあります。)
VLAN は元来、ブリッジのようなスイッチの概念でしたが、Linux がソフトウェアで実装しました。Linux は各イーサネットデータフレームで 16 ビットの vlan_proto フィールドと 16 ビットの vlan_tci フィールドを使用して 802.1Q プロトコルを実装しています。同時に、各 VLAN に対して子デバイスが仮想化され、VLAN が除去された後のパケットを処理します。そう、VLAN にも独自の子デバイス、すなわち VLAN サブインターフェイスがあるのです。異なる VLAN 子デバイスはマスターデバイスを通じて物理パケットを送受信します。この概念に少し馴染みはありませんか?そう、これは ENI-Trunking の原理そのものです。
VLAN/MACVlan/IPVlan のコア実装に迫る
背景知識を補ったところで、VLAN 子デバイスから始めて Linux カーネルの動作を見ていきましょう。ここでのカーネルコードはすべて最新バージョン 5.16.2 に基づいています。
VLAN 子デバイス
・デバイスの作成
VLAN 子デバイスは、当初は独立した仮想デバイスとして扱われていませんでした。非常に早い時期に登場し、コードの配置が混沌としていたためです。ただし、コアロジックは /net/8021q/ パスにあります。背景知識から、netlink メカニズムがネットワークカードデバイス作成のエントリーポイントを実装していることが分かります。VLAN 子デバイスの場合、netlink メッセージ実装の構造体は vlan_link_ops であり、VLAN 子デバイスの作成を担当するのは vlan_newlink メソッドです。カーネルの初期化コードフローは以下の通りです。
1. まず Linux の汎用 net_device 構造体を作成してデバイスの設定情報を保存し、vlan_newlink に入った後、vlan_check_real_dev を実行して渡された VLAN ID が使用可能かどうかを確認します。これは vlan_find_dev メソッドを呼び出し、マスターデバイスに対して条件を満たす子デバイスを探すために使用されます。コードの一部を観察してみましょう。
2. 次に vlan_changelink メソッドを通じてデバイスのプロパティを設定します。特別な設定がある場合、デフォルト値が上書きされます。
3. 最後に register_vlan_dev メソッドに入ります。このメソッドは完成した情報を net_device 構造体に読み込み、Linux の統一デバイス管理インターフェイスに従ってカーネルに登録します。
・メッセージの受信
作成プロセスの観点から、VLAN 子デバイスと汎用デバイスの違いは、マスターデバイスが VLAN ID を通じて vlan_find_dev で子デバイスを検索できる点にあります。これは非常に重要です。
次に、メッセージ受信プロセスを見てみましょう。背景知識によると、物理デバイスがメッセージを受信した後、プロトコルスタックに入って処理される前の通常のエントリーポイントは __netif_receive_skb_core です。このエントリーポイントから段階的に分析していきます。カーネルの処理フローは以下の通りです。
上図に基づき、__netif_receive_skb_core の一部を抜き出して分析します。
1. パケット処理プロセスの開始時に、skb_vlan_untag 操作が実行されます。VLAN パケットの場合、パケットプロトコルフィールドは常に ETH_P_8021Q であり、skb_vlan_untag は VLAN 情報をパケットの vlan_tci フィールドから抽出した後、vlan_set_encap_proto を呼び出してプロトコルを通常のネットワーク層プロトコルに更新します。この時点で、VLAN パケットは部分的に通常のパケットに変換されています。
2. VLAN タグを持つパケットは skb_vlan_tag_present に格納され、vlan_do_receive 処理フローに入ります。vlan_do_receive のコア処理は、vlan_find_dev で子デバイスを検索し、パケットの dev をその子デバイスに設定してから、Priority などの VLAN 関連情報をクリアすることです。これで、VLAN パケットは VLAN 子デバイス宛の通常のパケットに変換されました。
3. vlan_do_receive の後、もう一度 __netif_receive_skb_core を実行するラウンドに入り、通常のパケットフローに従って処理されます。子デバイス上の rx_handler はマスターデバイスと同じ rx_handler を通じて通常のパケットと同様にネットワーク層に処理されます。
・データの送信
VLAN 子デバイスのデータ送信エントリーポイントは vlan_dev_hard_start_xmit です。受信プロセスと比較すると、送信プロセスははるかに単純です。カーネルの送信フローは以下の通りです。
ハードウェアが送信する際、VLAN 子デバイスは vlan_dev_hard_start_xmit メソッドに入ります。このメソッドは ndo_start_xmit インターフェイスを実装し、__vlan_hwaccel_put_tag メソッドで VLAN 関連のイーサネット情報をメッセージに埋め込み、その後メッセージのデバイスをマスターデバイスに変更して、マスターデバイスの dev_queue_xmit メソッドを呼び出し、マスターデバイスの送信キューに再投入して送信します。キー部分を抜き出して分析しましょう。
MACVlan デバイス
VLAN 子デバイスを読んだところで、続けて MACVlan を分析します。MACVlan と VLAN 子デバイスの違いは、イーサネット自体の機能ではなく、独自のドライバーを持つ仮想ネットワークデバイスであることです。これはまず、ドライバーコードの独立性に表れています。MACVlan 関連のコードは基本的に /drivers/net/macvlan.c に配置されています。
MACVlan デバイスには 5 つのモードがあります。source モードを除く他の 4 つのモードはより早期に登場しました。定義は以下の通りです。
これらのモードの動作をまず覚えておきましょう。理由は後で回答する質問に関係します。
・デバイスの作成
MACVlan デバイスの場合、netlink 応答構造体は macvlan_link_ops で、デバイス作成に応答するメソッドは macvlan_newlink です。エントリーポイントから始めると、MACVlan デバイス作成の全体的なフローは以下の通りです。
1. macvlan_newlink は macvlan_common_newlink を呼び出して実際の子デバイス作成操作を実行します。macvlan_common_newlink はまず有効性の検証を行います。ここで注意すべきは、netif_is_macvlan で MACVlan 子デバイスがマスターデバイスとして作成される場合をチェックする点です。その場合、子デバイスのマスターデバイスが自動的に新しいネットワークカードのマスターデバイスとして使用されます。
2. 次に eth_hw_addr_random を通じて MACVlan デバイスにランダムな MAC アドレスを作成します。そう、MACVlan 子デバイスの MAC アドレスはランダムです。これは非常に重要で、後述します。
3. MAC アドレスを取得した後、マスターデバイス上で MACVlan ロジックの初期化を開始します。ここでチェックがあり、マスターデバイスにまだ MACVlan デバイスが作成されていない場合、macvlan_port_create を通じて MACVlan の初期化をサポートします。この初期化の最も重要な部分は、netdev_rx_handler_register を呼び出して MACVlan の rx_handler メソッド macvlan_handle_frame で、デバイスが元々登録していた rx_handler の動作を置き換えることです。
4. 初期化後、ポート、すなわち子デバイスを取得し、子デバイスの情報を設定します。
5. 最後に register_netdevice でデバイス作成を完了します。コアロジックの一部を抜き出して分析しましょう。
・メッセージの受信
MACVlan デバイスのメッセージ受信は __netif_receive_skb_core から始まります。具体的なコードフローは以下の通りです。
1. __netif_receive_skb_core でマスターデバイスが受信した後、MACVlan ドライバーが登録した macvlan_handle_frame メソッドに入ります。このメソッドはまずマルチキャストパケットを処理し、その後ユニキャストパケットを処理します。
2. マルチキャストメッセージの場合、is_multicast_ether_addr を通じた後、まず macvlan_hash_lookup を使用して子デバイス上の関連情報から子デバイスを検索し、ネットワークカードのモードに従って処理します。プライベートモードまたはパススルーモードの場合、子デバイスを見つけて macvlan_broadcast_one で単独処理します。ブリッジモードまたは VEPA がない場合、すべての子デバイスが macvlan_broadcast_enqueue を通じてブロードキャストメッセージを受信します。
3. ユニキャストメッセージの場合、ソースモードとパススルーモードが最初に処理され、上位層の動作を直接トリガーします。他のモードの場合、ソース MAC アドレスに基づいて macvlan_hash_lookup を実行し、VLAN 情報が見つかった場合はパケットの dev を見つかった子デバイスに設定します。
4. 最後に、PKT_TYPE_HANDLER_ANOTHER の戻り値で __netif_receive_skb_core を再度実行します。この操作中に macvlan_hash_lookup に到達すると、既に子デバイスであるため RX_HANDLER_PASS が返され、上位層に入ります。
5. MACVlan のデータ受信プロセスで最も重要なのは、マスターデバイスがメッセージを受信した後の子デバイス選択ロジックです。この部分のコードは以下の通りです。
・メッセージの送信
MACVlan のメッセージ送信プロセスは、子デバイスからの ndo_start_xmit コールバック関数から始まります。エントリーポイントは macvlan_start_xmit で、全体的なカーネルコードフローは以下の通りです。
1. パケットが macvlan_start_xmit に入った後、macvlan は主に macvlan_queue_xmit メソッドを通じてパケット送信を実行します。
2. macvlan_queue_xmit はまずブリッジモードを処理します。モードの定義から分かるように、ブリッジモードでのみメインデバイス内で異なる子デバイス間の直接通信が発生します。この特別なケースがここで処理されます。マルチキャストメッセージと他の子デバイス宛のユニキャストメッセージは、直接子デバイスに送信されます。
3. その他のメッセージは dev_queue_xmit_accel を通じて送信されます。dev_queue_xmit_accel はメインデバイスの netdev_start_xmit メソッドを直接呼び出して、メッセージの実際の送信を実現します。
IPVlan デバイス
MACVlan と VLAN 子デバイスと比較して、IPVlan 子デバイスはより複雑なモデルを持っています。MACVlan とは異なり、IPVlan は flags を通じて子デバイス間の相互接続動作を定義し、3 つのモードを提供します。定義は以下の通りです。
・デバイスの作成
前述の 2 つの子デバイスの分析を踏まえて、IPVlan の分析も同じ思路で進めることができます。IPVlan デバイスの netlink メッセージ処理構造体は ipvlan_link_ops で、デバイス作成のエントリーメソッドは ipvlan_link_new です。IPVlan 子デバイス作成のフローは以下の通りです。
1. ipvlan_link_new に入り、正当性を判断します。MACVlan と同様に、IPVlan デバイスがプライマリデバイスとして追加される場合、その IPVlan デバイスのプライマリデバイスが自動的に新しいデバイスのプライマリデバイスとして使用されます。
2. eth_hw_addr_set を通じて、IPVlan デバイスの MAC アドレスをプライマリデバイスの MAC アドレスに設定します。これが IPVlan と MACVlan の最も顕著な特徴の違いです。
3. 統一ネットワークカード登録の register_netdevice プロセスに入ります。このプロセス中で、現在 IPVlan 子デバイスが存在しない場合、MACVlan と同様に ipvlan_init 初期化プロセスに入ります。これによりプライマリデバイス上に ipvlan_port が作成され、IPVlan の rx_handler でプライマリデバイスの元の rx_handler を置き換え、マルチキャストパケットを処理する専用カーネルワーカーも起動します。つまり、IPVlan ではすべてのマルチキャストパケットが統一処理されます。
4. 次に ipvlan_set_port_mode を通じて、現在の子デバイスをマスターデバイスの情報に保存し、l3s の子デバイスに対してその l3mdev 処理メソッドを nf_hook に登録します。そう、これが前述のデバイスとの最大の違いです。l3s のマスターデバイスと子デバイスは、実際にネットワーク層でパケットを交換します。
IPVlan ネットワークデバイスについて、ipvlan_port_create のコードの一部を抜き出して分析しましょう。
・メッセージの受信
IPVlan 子デバイスの 3 つのモードは、それぞれ異なるパケット受信フローを持っています。カーネル内のフローは以下の通りです。
1. MACVlan と同様に、まず __netif_receive_skb_core を通って作成時に登録された ipvlan_handle_frame 処理フローに入ります。この時点では、データパケットはまだマスターデバイスの所有です。
2. l2 モードのメッセージ処理では、マルチキャストメッセージのみを処理し、メッセージは子デバイス作成時に初期化されたマルチキャスト処理キューに投入されます。ユニキャストメッセージは ipvlan_handle_mode_l3 に直接渡されます。
3. l3 モードまたは l2 モードのユニキャストメッセージは ipvlan_handle_mode_l3 処理フローに入り、まず ipvlan_get_l3_hdr でネットワーク層のヘッダー情報を取得し、IP アドレスに基づいて対応する子デバイスを検索し、最後に ipvlan_rcv_frame を呼び出してパケットの dev を IPVlan 子デバイスに設定し、RX_HANDLER_ANOTHER を返して次のパケット収集を実行します。
4. l3s モードの場合、ipvlan_handle_frame で RX_HANDLER_PASS を直接返します。つまり、l3s モードのメッセージはマスターデバイスでネットワーク層処理段階に入ります。l3s モードでは、事前に登録された nf_hook が NF_INET_LOCAL_IN でトリガーされた際に ipvlan_l3_rcv を実行し、addr を通じて子デバイスを検索してパケットのネットワーク層宛先アドレスを変更し、その後 ip_local_deliver に直接入ってネットワーク層の残りの操作を実行します。
・メッセージの送信
IPVlan のメッセージ送信実装は比較的複雑ですが、根本的には各子デバイスがマスターデバイスを使用してメッセージを送信しようとしています。IPVlan 子デバイスがパケットを送信する際、まず ipvlan_start_xmit に入り、コア送信操作は ipvlan_queue_xmit にあります。カーネルコードフローは以下の通りです。
1. ipvlan_queue_xmit は子デバイスのモードに応じて異なる送信方法を選択します。l2 モードは ipvlan_xmit_mode_l2 で送信し、l3 モードと l3s モードは ipvlan_xmit_mode_l3 で送信します。
2. ipvlan_xmit_mode_l2 の場合、まずローカルアドレスか VEPA モードかを判断します。VEPA モードでないローカルメッセージの場合、まず ipvlan_addr_lookup で同じマスターデバイス下の IPVlan 子デバイスかどうかを検索し、該当する場合は ipvlan_rcv_frame で他の子デバイスにパケットを受信させます。該当しない場合は dev_forward_skb でマスターデバイスに処理させます。
3. 次に ipvlan_xmit_mode_l2 はマルチキャストメッセージを処理します。処理前に ipvlan_skb_crossing_ns でパケットの netns 関連情報(Priority を含む)をクリアし、最後にパケットを ipvlan_multicast_enqueue に投入して前述のマルチキャスト処理フローをトリガーします。
4. ローカルでないデータパケットは、マスターデバイスの dev_queue_xmit を通じて送信します。
5. ipvlan_xmit_mode_l3 の処理もまず VEPA を判断します。VEPA モードでないデータパケットの場合、ipvlan_addr_lookup でもう一方の子デバイスかどうかを検索し、該当する場合は ipvlan_rcv_frame を呼び出して他のデバイスのパケット受信をトリガーします。
6. VEPA モードでないデータパケットの場合、まず ipvlan_skb_crossing_ns を実行し、その後 ipvlan_process_outbound を実行して、データパケットのネットワーク層プロトコルに応じて ipvlan_process_v4_outbound または ipvlan_process_v6_outbound を選択します。
7. ipvlan_process_v6_outbound を例に取ると、まず ip_route_output_flow でルートを検索し、その後ネットワーク層の ip_local_out を直接通って、メインデバイスのネットワーク層で残りの操作を続行します。
疑問の解決
上記の分析と経験を経て、少なくとも最初の疑問には簡単に答えられるようになったと思います。
VLAN と MACVlan/IPVlan の関係
VLAN と IPVlan、MACVlan の間にはどのような関係があるのか?なぜ名前に VLAN が含まれているのか?
MACVlan と IPVlan がこの名前を選んだからには、いくつかの側面で共通点があることを示しています。全体的な分析を通じて、VLAN 子デバイスのコアロジックは MACVlan や IPVlan と非常に似ていることが分かります。
1. メインデバイスがパケットの物理的な送受信を担当する。
2. マスターデバイスが子デバイスを複数のポートとして管理し、VLAN 情報、MAC アドレス、IP アドレスなどのルールに従ってポートを検索する(macvlan_hash_lookup、vlan_find_dev、ipvlan_addr_lookup)。
3. メインデバイスがパケットを受信した後、__netif_receive_skb_core で「折り返し」を経る必要がある。
4. 最終的に、子デバイスはパケットの dev を変更して直接送信し、メインデバイスに実際の操作を委ねる。
したがって、MACVlan/IPVlan の内部ロジックは Linux の VLAN 実装を大きく参照していると推測するのは難しくありません。Linux は 2007 年 6 月 18 日にリリースされたバージョン 2.6.23 [3] で初めて MACVlan を導入しました。その説明は以下の通りです。
The new "MACVlan" driver allows the system administrator to create virtual interfaces mapped to and from specific MAC addresses.
2014 年 12 月 7 日にリリースされたバージョン 3.19 [4] で、IPVlan が初めて導入されました。その説明は以下の通りです。
The new "IPVlan" driver enable the creation of virtual network devices for container interconnection. It is designed to work well with network namespaces. IPVlan is much like the existing MACVlan driver, but it does its multiplexing at a higher level in the stack.
VLAN については、Linux 2.4 よりもはるかに早く登場し、多くのデバイスの初期バージョンのドライバーが既に VLAN をサポートしていました。ただし、Linux が VLAN の hwaccel を実装したのは 2004 年の 2.6.10 [5] です。その際、多数の更新機能の中に以下の記述がありました。
I was poking about in the National Semi 83820 driver, and I happened to notice that the chip supports VLAN tag add/strip assist in hardware, but the driver wasn't making use of it. This patch adds in the driver support to use the VLAN tag add/remove hardware, and enables the drivers use of the kernel VLAN hwaccel interface.
つまり、Linux が VLAN をインターフェイスとして扱い始めた時には、既に MACVlan と IPVlan という 2 つの仮想インターフェイスが存在していたのです。VLAN パケットの処理を高速化するために、Linux は異なる VLAN をデバイスとして仮想化しました。この考え方の下で、後期の MACVlan と IPVlan は仮想デバイスをより有用なものにしたのです。
そう考えると、これらの関係はむしろオマージュに近いと言えます。
VEPA/パススルー/ブリッジ/プライベートについて
なぜ IPVlan と MACVlan には VEPA、Private、Passthrough など、さまざまなモードやフラグがあるのか?それぞれの違いは何か?
実際、カーネルの分析を通じて、これらのモードの動作はおおよそ理解できました。メインデバイスをグループチャット、すべてのメンバーが外部にメッセージを送信できると考えると、各モードは非常に直感的です。
1. プライベートモードでは、メンバー間の通信は禁止されており、グループ内でもグループ外でも通信できません。
2. ブリッジモードでは、メンバーはグループ内で自由にやり取りできます。
3. VEPA モードでは、メンバーはグループ内での発言を禁止されていますが、グループ外では直接プライベートにやり取りできます。年会の紅包争奪戦の期間に集団で発言禁止にされているようなものです。
4. パススルーモードでは、あなたがグループリーダーであり、あなた以外は誰も発言できません。
では、なぜこれらのモードが存在するのでしょうか?カーネルの動作から見て、ポートであれブリッジであれ、実際にはネットワークの概念です。つまり、最初から Linux は自身を適切なネットワークデバイスとして表現しようとしてきました。メインデバイスにとっては、Linux はそれをスイッチにしようとし、子デバイスにとっては、各ネットワークケーブルの背後にあるデバイスです。これは非常に理にかなっています。
実際、まさにその通りです。VEPA とプライベートは元来ネットワークの概念です。Linux だけでなく、OpenvSwitch [6] のように、自身を物理ネットワークに偽装しようとする多くのプロジェクトが、すべてこれらの動作パターンに従っています。
MACVlan と IPVlan の応用
IPVlan と MACVlan にはどのような利点があるのか?どのような場面で使用すべきか?
実のところ、記事冒頭でこのテーマの本来の意図に触れています。第 2 の疑問から分かるように、IPVlan と MACVlan はどちらも同じことを行っています。それは仮想ネットワークです。なぜ仮想ネットワークが必要なのでしょうか?この質問には多くの答えがありますが、クラウドコンピューティングの価値と同様に、仮想ネットワークはクラウドコンピューティングの基盤技術として、最終的にはリソース利用効率の向上を目指しています。
MACVlan と IPVlan はこの究極の目標に奉仕しています。物理マシンで helloworld を実行する時代は既に過去のものとなりました。仮想化からコンテナ化へと、時代はネットワーク密度に対してますます高い要求を課しています。コンテナ技術の誕生により、第一歩をステージに踏み出しましたが、密度が十分でパフォーマンスも効率的である必要があり、MACVlan と IPVlan は子デバイス(そしてもちろん ENI-Trunking も)を通じて密度を向上させ、効率を保証することで誕生しました。
ここで、Alibaba Cloud Container Service ACK が提供する新しい高性能・高密度ネットワークソリューションである IPVlan ソリューション [7] をご紹介したいと思います。
ACK は Terway プラグインをベースに、IPVlan を活用した Kubernetes ネットワークソリューションを実装しています。Terway ネットワークプラグインは ACK が開発したネットワークプラグインで、ネイティブな Elastic Network Interface を Pod に割り当てて Pod ネットワークを実現します。Kubernetes 標準のネットワークポリシーをサポートしてコンテナ間のアクセスポリシーを定義でき、Calico のネットワークポリシーとも互換性があります。
Terway ネットワークプラグインでは、各 Pod が独自のネットワークスタックと IP アドレスを持ちます。同一 ECS 内の Pod 通信はマシン内部で直接転送され、ECS 間の Pod 通信は VPC の Elastic Network Interface を通じて直接転送されます。VxLAN などのトンネル技術によるメッセージのカプセル化が不要なため、Terway モードのネットワークは通信パフォーマンスが高いのが特徴です。Terway のネットワークモードを下図に示します。
ユーザーが ACK を使用してクラスターを作成する際、Terway ネットワークプラグインを選択すると、Terway IPv4 モードを使用するように設定できます。Terway IPvlan モードは IPvlan 仮想化と eBPF カーネル技術を使用して、高性能な Pod と Service ネットワークを実現します。
デフォルトの Terway ネットワークモードとは異なり、IPvlan モードは主に Pod ネットワーク、Service、ネットワークポリシーのパフォーマンスを最適化します。
・Pod ネットワークは ENI ネットワークカードの IPvlan L2 サブインターフェイスを通じて直接実現され、ホストマシン上のネットワーク転送プロセスを大幅に簡素化します。Pod のネットワークパフォーマンスはホストマシンとほぼ同等で、レイテンシは従来のモードと比較して 30% 削減されます。
・Service ネットワークは eBPF を使用して元の kube-proxy モードを置き換え、ホストマシン上の iptables や IPVS を介した転送が不要になります。大規模クラスターでもパフォーマンスはほぼ変わらず、スケーラビリティに優れています。大量の新規接続やポート再利用シナリオでは、リクエストのレイテンシが IPVS や iptables モードよりも大幅に低くなります。
・Pod のネットワークポリシーも eBPF を使用して iptables の元の実装を置き換えています。ホストマシン上に大量の iptables ルールを生成する必要がなく、ネットワークポリシーがネットワークパフォーマンスに与える影響を最小限に抑えます。
したがって、IPVlan を使用して各サービス Pod に IPVlan ネットワークカードを割り当てることで、ネットワークの密度を保証するだけでなく、従来のネットワークの Veth スキームと比較して大幅なパフォーマンス向上を実現できます(詳細は参考リンク 7 を参照)。同時に、Terway IPvlan モードは高性能な Service ソリューションを提供します。eBPF 技術に基づき、長年問題視されてきた Conntrack のパフォーマンス問題を回避しています。
VLAN について聞いたことがありますか?正式名称は Virtual Local Area Network(仮想ローカルエリアネットワーク)で、イーサネット内で異なるブロードキャストドメインを分離するために使用されます。非常に早い時期に誕生しました。1995 年に IEEE が 802.1Q 規格 [1] を公開し、イーサネットデータフレームにおける VLAN のフォーマットを定義しており、現在まで使用され続けています。VLAN をご存知であれば、MACVlan や IPVlan についてはどうでしょうか?コンテナ技術の台頭に伴い、Linux の仮想ネットワークデバイスである IPVlan と MACVlan が徐々に注目を集めるようになりました。2017 年の Docker Engine バージョン 1.13.1 [2] において、IPVlan と MACVlan がコンテナネットワークソリューションとして導入されています。
以下の疑問をお持ちではありませんか?
1. VLAN と IPVlan、MACVlan の間にはどのような関係があるのか?なぜ名前に VLAN が含まれているのか?
2. なぜ IPVlan と MACVlan には VEPA、Private、Passthrough など、さまざまなモードやフラグがあるのか?それぞれの違いは何か?
3. IPVlan と MACVlan にはどのような利点があるのか?どのような場面で使用すべきか?
かつて私も同じ疑問を持っていました。本記事では、上記の 3 つの疑問について探求していきます。
背景知識
以下にいくつかの背景知識を紹介します。Linux に詳しい方は読み飛ばしていただいて構いません。
・ネットワークデバイスの抽象化の確認
Linux では、ip コマンドまたは ifconfig コマンドを使用してネットワークデバイスを操作します。ip コマンドの実装である iproute2 が実際に依存しているのは、Linux が提供する netlink メッセージメカニズムです。カーネルは、ネットワークデバイスの種類(実在か仮想かを問わず)ごとに、netlink メッセージに応答する専用構造体を抽象化しており、それらはすべて rtnl_link_ops 構造体に準拠してネットワークデバイスの作成、破棄、変更に応答します。たとえば、より直感的な Veth デバイスの例を見てみましょう。
ネットワークデバイスにとって、Linux の操作とハードウェアデバイスの応答にも一連の仕様が必要です。Linux はこれを net_device_ops 構造体として抽象化しています。デバイスドライバーに興味がある方は、主にこの構造体を扱うことになります。Veth デバイスを例に取ってみましょう。
上記の定義から、いくつか意味的に直感的なメソッドを確認できます。ndo_start_xmit はパケットの送信に使用され、newlink は新しいデバイスの作成に使用されます。
パケットの受信について、Linux のパケット受信アクションは各プロセス自身が実行するのではなく、ドライバー、ネットワーク層(IP、iptables)、トランスポート層(TCP、UDP)から ksoftirqd カーネルスレッドが処理し、最終的にユーザープロセスが保持するソケットの recv バッファーに配置されます。その後、カーネルの inotify がユーザープロセスに通知します。仮想デバイスの場合、すべての差異はネットワーク層より前に集中しており、統一されたエントリーポイント、すなわち __netif_receive_skb_core があります。
・802.1Q プロトコルにおける VLAN の定義
802.1Q プロトコルでは、イーサネットデータフレームヘッダーをマークするために使用される VLAN フィールドは 32 ビットのドメインで、構造は以下の通りです。
上図に示す通り、プロトコルラベルに 16 ビット、優先度ラベルに 3 ビット、フォーマットラベルに 1 ビット、VLAN ID の格納に 12 ビットが割り当てられています。VLAN に基づいていくつのブロードキャストドメインに分割できるか、簡単に計算できると思います。そう、2^12 = 4096 です。全 0 と全 1 の予約値を差し引くと、ユーザーは 4094 の利用可能なブロードキャストドメインに分割できます。(OpenFlow の台頭以前、クラウドコンピューティングの最初期のプロトタイプにおける VPC 実装はネットワーク差別化に VLAN に依存していましたが、この制限によりすぐに淘汰され、皆さんにもおなじみのもう一つの用語、VxLAN が生まれました。両者は大きく異なりますが、参照すべき理由は依然としてあります。)
VLAN は元来、ブリッジのようなスイッチの概念でしたが、Linux がソフトウェアで実装しました。Linux は各イーサネットデータフレームで 16 ビットの vlan_proto フィールドと 16 ビットの vlan_tci フィールドを使用して 802.1Q プロトコルを実装しています。同時に、各 VLAN に対して子デバイスが仮想化され、VLAN が除去された後のパケットを処理します。そう、VLAN にも独自の子デバイス、すなわち VLAN サブインターフェイスがあるのです。異なる VLAN 子デバイスはマスターデバイスを通じて物理パケットを送受信します。この概念に少し馴染みはありませんか?そう、これは ENI-Trunking の原理そのものです。
VLAN/MACVlan/IPVlan のコア実装に迫る
背景知識を補ったところで、VLAN 子デバイスから始めて Linux カーネルの動作を見ていきましょう。ここでのカーネルコードはすべて最新バージョン 5.16.2 に基づいています。
VLAN 子デバイス
・デバイスの作成
VLAN 子デバイスは、当初は独立した仮想デバイスとして扱われていませんでした。非常に早い時期に登場し、コードの配置が混沌としていたためです。ただし、コアロジックは /net/8021q/ パスにあります。背景知識から、netlink メカニズムがネットワークカードデバイス作成のエントリーポイントを実装していることが分かります。VLAN 子デバイスの場合、netlink メッセージ実装の構造体は vlan_link_ops であり、VLAN 子デバイスの作成を担当するのは vlan_newlink メソッドです。カーネルの初期化コードフローは以下の通りです。
1. まず Linux の汎用 net_device 構造体を作成してデバイスの設定情報を保存し、vlan_newlink に入った後、vlan_check_real_dev を実行して渡された VLAN ID が使用可能かどうかを確認します。これは vlan_find_dev メソッドを呼び出し、マスターデバイスに対して条件を満たす子デバイスを探すために使用されます。コードの一部を観察してみましょう。
2. 次に vlan_changelink メソッドを通じてデバイスのプロパティを設定します。特別な設定がある場合、デフォルト値が上書きされます。
3. 最後に register_vlan_dev メソッドに入ります。このメソッドは完成した情報を net_device 構造体に読み込み、Linux の統一デバイス管理インターフェイスに従ってカーネルに登録します。
・メッセージの受信
作成プロセスの観点から、VLAN 子デバイスと汎用デバイスの違いは、マスターデバイスが VLAN ID を通じて vlan_find_dev で子デバイスを検索できる点にあります。これは非常に重要です。
次に、メッセージ受信プロセスを見てみましょう。背景知識によると、物理デバイスがメッセージを受信した後、プロトコルスタックに入って処理される前の通常のエントリーポイントは __netif_receive_skb_core です。このエントリーポイントから段階的に分析していきます。カーネルの処理フローは以下の通りです。
上図に基づき、__netif_receive_skb_core の一部を抜き出して分析します。
1. パケット処理プロセスの開始時に、skb_vlan_untag 操作が実行されます。VLAN パケットの場合、パケットプロトコルフィールドは常に ETH_P_8021Q であり、skb_vlan_untag は VLAN 情報をパケットの vlan_tci フィールドから抽出した後、vlan_set_encap_proto を呼び出してプロトコルを通常のネットワーク層プロトコルに更新します。この時点で、VLAN パケットは部分的に通常のパケットに変換されています。
2. VLAN タグを持つパケットは skb_vlan_tag_present に格納され、vlan_do_receive 処理フローに入ります。vlan_do_receive のコア処理は、vlan_find_dev で子デバイスを検索し、パケットの dev をその子デバイスに設定してから、Priority などの VLAN 関連情報をクリアすることです。これで、VLAN パケットは VLAN 子デバイス宛の通常のパケットに変換されました。
3. vlan_do_receive の後、もう一度 __netif_receive_skb_core を実行するラウンドに入り、通常のパケットフローに従って処理されます。子デバイス上の rx_handler はマスターデバイスと同じ rx_handler を通じて通常のパケットと同様にネットワーク層に処理されます。
・データの送信
VLAN 子デバイスのデータ送信エントリーポイントは vlan_dev_hard_start_xmit です。受信プロセスと比較すると、送信プロセスははるかに単純です。カーネルの送信フローは以下の通りです。
ハードウェアが送信する際、VLAN 子デバイスは vlan_dev_hard_start_xmit メソッドに入ります。このメソッドは ndo_start_xmit インターフェイスを実装し、__vlan_hwaccel_put_tag メソッドで VLAN 関連のイーサネット情報をメッセージに埋め込み、その後メッセージのデバイスをマスターデバイスに変更して、マスターデバイスの dev_queue_xmit メソッドを呼び出し、マスターデバイスの送信キューに再投入して送信します。キー部分を抜き出して分析しましょう。
MACVlan デバイス
VLAN 子デバイスを読んだところで、続けて MACVlan を分析します。MACVlan と VLAN 子デバイスの違いは、イーサネット自体の機能ではなく、独自のドライバーを持つ仮想ネットワークデバイスであることです。これはまず、ドライバーコードの独立性に表れています。MACVlan 関連のコードは基本的に /drivers/net/macvlan.c に配置されています。
MACVlan デバイスには 5 つのモードがあります。source モードを除く他の 4 つのモードはより早期に登場しました。定義は以下の通りです。
これらのモードの動作をまず覚えておきましょう。理由は後で回答する質問に関係します。
・デバイスの作成
MACVlan デバイスの場合、netlink 応答構造体は macvlan_link_ops で、デバイス作成に応答するメソッドは macvlan_newlink です。エントリーポイントから始めると、MACVlan デバイス作成の全体的なフローは以下の通りです。
1. macvlan_newlink は macvlan_common_newlink を呼び出して実際の子デバイス作成操作を実行します。macvlan_common_newlink はまず有効性の検証を行います。ここで注意すべきは、netif_is_macvlan で MACVlan 子デバイスがマスターデバイスとして作成される場合をチェックする点です。その場合、子デバイスのマスターデバイスが自動的に新しいネットワークカードのマスターデバイスとして使用されます。
2. 次に eth_hw_addr_random を通じて MACVlan デバイスにランダムな MAC アドレスを作成します。そう、MACVlan 子デバイスの MAC アドレスはランダムです。これは非常に重要で、後述します。
3. MAC アドレスを取得した後、マスターデバイス上で MACVlan ロジックの初期化を開始します。ここでチェックがあり、マスターデバイスにまだ MACVlan デバイスが作成されていない場合、macvlan_port_create を通じて MACVlan の初期化をサポートします。この初期化の最も重要な部分は、netdev_rx_handler_register を呼び出して MACVlan の rx_handler メソッド macvlan_handle_frame で、デバイスが元々登録していた rx_handler の動作を置き換えることです。
4. 初期化後、ポート、すなわち子デバイスを取得し、子デバイスの情報を設定します。
5. 最後に register_netdevice でデバイス作成を完了します。コアロジックの一部を抜き出して分析しましょう。
・メッセージの受信
MACVlan デバイスのメッセージ受信は __netif_receive_skb_core から始まります。具体的なコードフローは以下の通りです。
1. __netif_receive_skb_core でマスターデバイスが受信した後、MACVlan ドライバーが登録した macvlan_handle_frame メソッドに入ります。このメソッドはまずマルチキャストパケットを処理し、その後ユニキャストパケットを処理します。
2. マルチキャストメッセージの場合、is_multicast_ether_addr を通じた後、まず macvlan_hash_lookup を使用して子デバイス上の関連情報から子デバイスを検索し、ネットワークカードのモードに従って処理します。プライベートモードまたはパススルーモードの場合、子デバイスを見つけて macvlan_broadcast_one で単独処理します。ブリッジモードまたは VEPA がない場合、すべての子デバイスが macvlan_broadcast_enqueue を通じてブロードキャストメッセージを受信します。
3. ユニキャストメッセージの場合、ソースモードとパススルーモードが最初に処理され、上位層の動作を直接トリガーします。他のモードの場合、ソース MAC アドレスに基づいて macvlan_hash_lookup を実行し、VLAN 情報が見つかった場合はパケットの dev を見つかった子デバイスに設定します。
4. 最後に、PKT_TYPE_HANDLER_ANOTHER の戻り値で __netif_receive_skb_core を再度実行します。この操作中に macvlan_hash_lookup に到達すると、既に子デバイスであるため RX_HANDLER_PASS が返され、上位層に入ります。
5. MACVlan のデータ受信プロセスで最も重要なのは、マスターデバイスがメッセージを受信した後の子デバイス選択ロジックです。この部分のコードは以下の通りです。
・メッセージの送信
MACVlan のメッセージ送信プロセスは、子デバイスからの ndo_start_xmit コールバック関数から始まります。エントリーポイントは macvlan_start_xmit で、全体的なカーネルコードフローは以下の通りです。
1. パケットが macvlan_start_xmit に入った後、macvlan は主に macvlan_queue_xmit メソッドを通じてパケット送信を実行します。
2. macvlan_queue_xmit はまずブリッジモードを処理します。モードの定義から分かるように、ブリッジモードでのみメインデバイス内で異なる子デバイス間の直接通信が発生します。この特別なケースがここで処理されます。マルチキャストメッセージと他の子デバイス宛のユニキャストメッセージは、直接子デバイスに送信されます。
3. その他のメッセージは dev_queue_xmit_accel を通じて送信されます。dev_queue_xmit_accel はメインデバイスの netdev_start_xmit メソッドを直接呼び出して、メッセージの実際の送信を実現します。
IPVlan デバイス
MACVlan と VLAN 子デバイスと比較して、IPVlan 子デバイスはより複雑なモデルを持っています。MACVlan とは異なり、IPVlan は flags を通じて子デバイス間の相互接続動作を定義し、3 つのモードを提供します。定義は以下の通りです。
・デバイスの作成
前述の 2 つの子デバイスの分析を踏まえて、IPVlan の分析も同じ思路で進めることができます。IPVlan デバイスの netlink メッセージ処理構造体は ipvlan_link_ops で、デバイス作成のエントリーメソッドは ipvlan_link_new です。IPVlan 子デバイス作成のフローは以下の通りです。
1. ipvlan_link_new に入り、正当性を判断します。MACVlan と同様に、IPVlan デバイスがプライマリデバイスとして追加される場合、その IPVlan デバイスのプライマリデバイスが自動的に新しいデバイスのプライマリデバイスとして使用されます。
2. eth_hw_addr_set を通じて、IPVlan デバイスの MAC アドレスをプライマリデバイスの MAC アドレスに設定します。これが IPVlan と MACVlan の最も顕著な特徴の違いです。
3. 統一ネットワークカード登録の register_netdevice プロセスに入ります。このプロセス中で、現在 IPVlan 子デバイスが存在しない場合、MACVlan と同様に ipvlan_init 初期化プロセスに入ります。これによりプライマリデバイス上に ipvlan_port が作成され、IPVlan の rx_handler でプライマリデバイスの元の rx_handler を置き換え、マルチキャストパケットを処理する専用カーネルワーカーも起動します。つまり、IPVlan ではすべてのマルチキャストパケットが統一処理されます。
4. 次に ipvlan_set_port_mode を通じて、現在の子デバイスをマスターデバイスの情報に保存し、l3s の子デバイスに対してその l3mdev 処理メソッドを nf_hook に登録します。そう、これが前述のデバイスとの最大の違いです。l3s のマスターデバイスと子デバイスは、実際にネットワーク層でパケットを交換します。
IPVlan ネットワークデバイスについて、ipvlan_port_create のコードの一部を抜き出して分析しましょう。
・メッセージの受信
IPVlan 子デバイスの 3 つのモードは、それぞれ異なるパケット受信フローを持っています。カーネル内のフローは以下の通りです。
1. MACVlan と同様に、まず __netif_receive_skb_core を通って作成時に登録された ipvlan_handle_frame 処理フローに入ります。この時点では、データパケットはまだマスターデバイスの所有です。
2. l2 モードのメッセージ処理では、マルチキャストメッセージのみを処理し、メッセージは子デバイス作成時に初期化されたマルチキャスト処理キューに投入されます。ユニキャストメッセージは ipvlan_handle_mode_l3 に直接渡されます。
3. l3 モードまたは l2 モードのユニキャストメッセージは ipvlan_handle_mode_l3 処理フローに入り、まず ipvlan_get_l3_hdr でネットワーク層のヘッダー情報を取得し、IP アドレスに基づいて対応する子デバイスを検索し、最後に ipvlan_rcv_frame を呼び出してパケットの dev を IPVlan 子デバイスに設定し、RX_HANDLER_ANOTHER を返して次のパケット収集を実行します。
4. l3s モードの場合、ipvlan_handle_frame で RX_HANDLER_PASS を直接返します。つまり、l3s モードのメッセージはマスターデバイスでネットワーク層処理段階に入ります。l3s モードでは、事前に登録された nf_hook が NF_INET_LOCAL_IN でトリガーされた際に ipvlan_l3_rcv を実行し、addr を通じて子デバイスを検索してパケットのネットワーク層宛先アドレスを変更し、その後 ip_local_deliver に直接入ってネットワーク層の残りの操作を実行します。
・メッセージの送信
IPVlan のメッセージ送信実装は比較的複雑ですが、根本的には各子デバイスがマスターデバイスを使用してメッセージを送信しようとしています。IPVlan 子デバイスがパケットを送信する際、まず ipvlan_start_xmit に入り、コア送信操作は ipvlan_queue_xmit にあります。カーネルコードフローは以下の通りです。
1. ipvlan_queue_xmit は子デバイスのモードに応じて異なる送信方法を選択します。l2 モードは ipvlan_xmit_mode_l2 で送信し、l3 モードと l3s モードは ipvlan_xmit_mode_l3 で送信します。
2. ipvlan_xmit_mode_l2 の場合、まずローカルアドレスか VEPA モードかを判断します。VEPA モードでないローカルメッセージの場合、まず ipvlan_addr_lookup で同じマスターデバイス下の IPVlan 子デバイスかどうかを検索し、該当する場合は ipvlan_rcv_frame で他の子デバイスにパケットを受信させます。該当しない場合は dev_forward_skb でマスターデバイスに処理させます。
3. 次に ipvlan_xmit_mode_l2 はマルチキャストメッセージを処理します。処理前に ipvlan_skb_crossing_ns でパケットの netns 関連情報(Priority を含む)をクリアし、最後にパケットを ipvlan_multicast_enqueue に投入して前述のマルチキャスト処理フローをトリガーします。
4. ローカルでないデータパケットは、マスターデバイスの dev_queue_xmit を通じて送信します。
5. ipvlan_xmit_mode_l3 の処理もまず VEPA を判断します。VEPA モードでないデータパケットの場合、ipvlan_addr_lookup でもう一方の子デバイスかどうかを検索し、該当する場合は ipvlan_rcv_frame を呼び出して他のデバイスのパケット受信をトリガーします。
6. VEPA モードでないデータパケットの場合、まず ipvlan_skb_crossing_ns を実行し、その後 ipvlan_process_outbound を実行して、データパケットのネットワーク層プロトコルに応じて ipvlan_process_v4_outbound または ipvlan_process_v6_outbound を選択します。
7. ipvlan_process_v6_outbound を例に取ると、まず ip_route_output_flow でルートを検索し、その後ネットワーク層の ip_local_out を直接通って、メインデバイスのネットワーク層で残りの操作を続行します。
疑問の解決
上記の分析と経験を経て、少なくとも最初の疑問には簡単に答えられるようになったと思います。
VLAN と MACVlan/IPVlan の関係
VLAN と IPVlan、MACVlan の間にはどのような関係があるのか?なぜ名前に VLAN が含まれているのか?
MACVlan と IPVlan がこの名前を選んだからには、いくつかの側面で共通点があることを示しています。全体的な分析を通じて、VLAN 子デバイスのコアロジックは MACVlan や IPVlan と非常に似ていることが分かります。
1. メインデバイスがパケットの物理的な送受信を担当する。
2. マスターデバイスが子デバイスを複数のポートとして管理し、VLAN 情報、MAC アドレス、IP アドレスなどのルールに従ってポートを検索する(macvlan_hash_lookup、vlan_find_dev、ipvlan_addr_lookup)。
3. メインデバイスがパケットを受信した後、__netif_receive_skb_core で「折り返し」を経る必要がある。
4. 最終的に、子デバイスはパケットの dev を変更して直接送信し、メインデバイスに実際の操作を委ねる。
したがって、MACVlan/IPVlan の内部ロジックは Linux の VLAN 実装を大きく参照していると推測するのは難しくありません。Linux は 2007 年 6 月 18 日にリリースされたバージョン 2.6.23 [3] で初めて MACVlan を導入しました。その説明は以下の通りです。
The new "MACVlan" driver allows the system administrator to create virtual interfaces mapped to and from specific MAC addresses.
2014 年 12 月 7 日にリリースされたバージョン 3.19 [4] で、IPVlan が初めて導入されました。その説明は以下の通りです。
The new "IPVlan" driver enable the creation of virtual network devices for container interconnection. It is designed to work well with network namespaces. IPVlan is much like the existing MACVlan driver, but it does its multiplexing at a higher level in the stack.
VLAN については、Linux 2.4 よりもはるかに早く登場し、多くのデバイスの初期バージョンのドライバーが既に VLAN をサポートしていました。ただし、Linux が VLAN の hwaccel を実装したのは 2004 年の 2.6.10 [5] です。その際、多数の更新機能の中に以下の記述がありました。
I was poking about in the National Semi 83820 driver, and I happened to notice that the chip supports VLAN tag add/strip assist in hardware, but the driver wasn't making use of it. This patch adds in the driver support to use the VLAN tag add/remove hardware, and enables the drivers use of the kernel VLAN hwaccel interface.
つまり、Linux が VLAN をインターフェイスとして扱い始めた時には、既に MACVlan と IPVlan という 2 つの仮想インターフェイスが存在していたのです。VLAN パケットの処理を高速化するために、Linux は異なる VLAN をデバイスとして仮想化しました。この考え方の下で、後期の MACVlan と IPVlan は仮想デバイスをより有用なものにしたのです。
そう考えると、これらの関係はむしろオマージュに近いと言えます。
VEPA/パススルー/ブリッジ/プライベートについて
なぜ IPVlan と MACVlan には VEPA、Private、Passthrough など、さまざまなモードやフラグがあるのか?それぞれの違いは何か?
実際、カーネルの分析を通じて、これらのモードの動作はおおよそ理解できました。メインデバイスをグループチャット、すべてのメンバーが外部にメッセージを送信できると考えると、各モードは非常に直感的です。
1. プライベートモードでは、メンバー間の通信は禁止されており、グループ内でもグループ外でも通信できません。
2. ブリッジモードでは、メンバーはグループ内で自由にやり取りできます。
3. VEPA モードでは、メンバーはグループ内での発言を禁止されていますが、グループ外では直接プライベートにやり取りできます。年会の紅包争奪戦の期間に集団で発言禁止にされているようなものです。
4. パススルーモードでは、あなたがグループリーダーであり、あなた以外は誰も発言できません。
では、なぜこれらのモードが存在するのでしょうか?カーネルの動作から見て、ポートであれブリッジであれ、実際にはネットワークの概念です。つまり、最初から Linux は自身を適切なネットワークデバイスとして表現しようとしてきました。メインデバイスにとっては、Linux はそれをスイッチにしようとし、子デバイスにとっては、各ネットワークケーブルの背後にあるデバイスです。これは非常に理にかなっています。
実際、まさにその通りです。VEPA とプライベートは元来ネットワークの概念です。Linux だけでなく、OpenvSwitch [6] のように、自身を物理ネットワークに偽装しようとする多くのプロジェクトが、すべてこれらの動作パターンに従っています。
MACVlan と IPVlan の応用
IPVlan と MACVlan にはどのような利点があるのか?どのような場面で使用すべきか?
実のところ、記事冒頭でこのテーマの本来の意図に触れています。第 2 の疑問から分かるように、IPVlan と MACVlan はどちらも同じことを行っています。それは仮想ネットワークです。なぜ仮想ネットワークが必要なのでしょうか?この質問には多くの答えがありますが、クラウドコンピューティングの価値と同様に、仮想ネットワークはクラウドコンピューティングの基盤技術として、最終的にはリソース利用効率の向上を目指しています。
MACVlan と IPVlan はこの究極の目標に奉仕しています。物理マシンで helloworld を実行する時代は既に過去のものとなりました。仮想化からコンテナ化へと、時代はネットワーク密度に対してますます高い要求を課しています。コンテナ技術の誕生により、第一歩をステージに踏み出しましたが、密度が十分でパフォーマンスも効率的である必要があり、MACVlan と IPVlan は子デバイス(そしてもちろん ENI-Trunking も)を通じて密度を向上させ、効率を保証することで誕生しました。
ここで、Alibaba Cloud Container Service ACK が提供する新しい高性能・高密度ネットワークソリューションである IPVlan ソリューション [7] をご紹介したいと思います。
ACK は Terway プラグインをベースに、IPVlan を活用した Kubernetes ネットワークソリューションを実装しています。Terway ネットワークプラグインは ACK が開発したネットワークプラグインで、ネイティブな Elastic Network Interface を Pod に割り当てて Pod ネットワークを実現します。Kubernetes 標準のネットワークポリシーをサポートしてコンテナ間のアクセスポリシーを定義でき、Calico のネットワークポリシーとも互換性があります。
Terway ネットワークプラグインでは、各 Pod が独自のネットワークスタックと IP アドレスを持ちます。同一 ECS 内の Pod 通信はマシン内部で直接転送され、ECS 間の Pod 通信は VPC の Elastic Network Interface を通じて直接転送されます。VxLAN などのトンネル技術によるメッセージのカプセル化が不要なため、Terway モードのネットワークは通信パフォーマンスが高いのが特徴です。Terway のネットワークモードを下図に示します。
ユーザーが ACK を使用してクラスターを作成する際、Terway ネットワークプラグインを選択すると、Terway IPv4 モードを使用するように設定できます。Terway IPvlan モードは IPvlan 仮想化と eBPF カーネル技術を使用して、高性能な Pod と Service ネットワークを実現します。
デフォルトの Terway ネットワークモードとは異なり、IPvlan モードは主に Pod ネットワーク、Service、ネットワークポリシーのパフォーマンスを最適化します。
・Pod ネットワークは ENI ネットワークカードの IPvlan L2 サブインターフェイスを通じて直接実現され、ホストマシン上のネットワーク転送プロセスを大幅に簡素化します。Pod のネットワークパフォーマンスはホストマシンとほぼ同等で、レイテンシは従来のモードと比較して 30% 削減されます。
・Service ネットワークは eBPF を使用して元の kube-proxy モードを置き換え、ホストマシン上の iptables や IPVS を介した転送が不要になります。大規模クラスターでもパフォーマンスはほぼ変わらず、スケーラビリティに優れています。大量の新規接続やポート再利用シナリオでは、リクエストのレイテンシが IPVS や iptables モードよりも大幅に低くなります。
・Pod のネットワークポリシーも eBPF を使用して iptables の元の実装を置き換えています。ホストマシン上に大量の iptables ルールを生成する必要がなく、ネットワークポリシーがネットワークパフォーマンスに与える影響を最小限に抑えます。
したがって、IPVlan を使用して各サービス Pod に IPVlan ネットワークカードを割り当てることで、ネットワークの密度を保証するだけでなく、従来のネットワークの Veth スキームと比較して大幅なパフォーマンス向上を実現できます(詳細は参考リンク 7 を参照)。同時に、Terway IPvlan モードは高性能な Service ソリューションを提供します。eBPF 技術に基づき、長年問題視されてきた Conntrack のパフォーマンス問題を回避しています。
Related Articles
-
A detailed explanation of Hadoop core architecture HDFS
Knowledge Base Team
-
What Does IOT Mean
Knowledge Base Team
-
6 Optional Technologies for Data Storage
Knowledge Base Team
-
What Is Blockchain Technology
Knowledge Base Team
Explore More Special Offers
-
Short Message Service(SMS) & Mail Service
50,000 email package starts as low as USD 1.99, 120 short messages start at only USD 1.00
