😽

Azure Gateway Load Balancer (Preview) を試す

2021/11/11に公開

Gateway Load Balancer とは何か

Gateway Load Balancer (GWLB) は、Microsoft Ignite 2021 で発表された新たな Azure Load Balancer の SKU です。ざっくり言ってしまうと、Azure VM に関連付けられた Instance Level Public IP (ILPIP) や Public Load Balancer (LB) に対するトラフィックを、別環境に用意した Network Virtual Appliance (NVA) に横流しするためのものです。AWS でも、同様の機能を持つ AWS Gateway Load Balancer が提供されていますね。

実は、同じようなことをしたければ、GWLB がなくても実現は出来る状況ではありました。ただ、User Defined Route (UDR) を使ってルーティングを考慮する必要があり、かなり構成を考えるのが面倒だったことは否めません。今回の LBGW の登場で、より手軽に、透過的なファイアウォールや監査ログ収集が実現できるようになりました。

https://docs.microsoft.com/ja-jp/azure/load-balancer/gateway-overview

コンシューマーとプロバイダー

GWLB を構成するものは、大きく分けてコンシューマーとプロバイダーです。

  • コンシューマー (Consumer): トラフィックを横取りしたい ILPIP/LB + その配下の Azure VM / VMSS
  • プロバイダー (Provider): GWLB + NVA (Azure VM)

コンシューマーは ILPIP を関連付けた Azure VM や、Public Load Balancer とその配下の Azure VM 群です。コンシューマーに流れてくるパケット、あるいは、コンシューマーから出ていくパケットが横取りされる対象です。具体的な設定は後述しますが、コンシューマー側の LB や NIC に、GWLB のフロントエンド IP 構成の ResourceID を関連付けることで、透過的にパケットを流し込む宛先を決定付けます。

プロバイダー側では、GWLB のバックエンドプールに配置された NVA (実体は Azure VM) が、横取りされたパケットの到着を待ち受けています。NVA 内部のインターフェイスでパケットを処理した後、同じインターフェイスからパケットを送り返すことで、元通りコンシューマー側にパケットが到着します。少し踏み込んで説明すると、L2 ネットワークを延伸 (コンシューマー側のネットワークとプロバイダー側の VNet を接続) するために、VXLAN を使っています。

ドキュメントにわかりやすい図が記載されてます。これは行きのパケットのフォワーディングの様子を表していますが、戻りのパケットも逆順で NVA を経由します。つまり、5 -> 4 -> 3 -> 2 -> 1 と戻っていきます。

チュートリアル

https://docs.microsoft.com/ja-jp/azure/load-balancer/tutorial-gateway-portal

チュートリアルが公開されているので早速構成してみます。プレビュー中の機能ということもあり、Portal で作業をしたい場合は Preview Portal (https://preview.portal.azure.com) を利用する必要があります。もちろん REST API を直接叩いても構いません。

また、前準備として Feature Flag を有効化しておきます。

Register-AzProviderFeature -ProviderNamespace Microsoft.Network -FeatureName AllowGatewayLoadBalancer
Register-AzResourceProvider -ProviderNamespace Microsoft.Network

これらの考慮はプレビュー中の機能特有のもので、General Available (GA) 後はおそらく不要になると思います。

コンシューマー側の設定

コンシューマーになり得る環境は以下の 2 つです。

  • Public Load Balancer とその配下の Azure VM / VMSS
  • Instance Level Public IP (ILPIP) が関連付けられた Azure VM

どちらの場合でも、やらなければならないことは一つだけです。GWLB を作成した後、それを関連付けることです。非常に簡単ですね。

Public Load Balancer に GWLB を関連付ける場合、LB の [フロントエンド IP 構成] ページから、関連付けるフロントエンドを選択して、以下のように関連付けが可能です。

ILPIP に GWLB を関連付ける場合、Azure VM の NIC の [IP 構成] ページから関連付けが可能です。

プロバイダー側の設定

プロバイダーには、次の 3 のものをデプロイする必要があります。

  • Gateway Load Balancer (GWLB)
  • Virtual Network (VNet)
  • NVA の役割を担う Azure VM

GWLB と VNet のデプロイに関しては、公式ドキュメントの手順を参考にしながら、実施してください。

一方、NVA には多くの選択肢があります。簡単に調べただけでも、現在、GWLB との統合をアナウンスしている製品には次のようなものがあります。

チュートリアルを公開しているものもあるので、まずはそちらで試すのも手だとは思います。ただ、何が起きているのか把握するため、Linux VM を NVA としてデプロイしてみました。

Linux VM を GWLB NVA として利用する

1. GWLB と VNet の作成

公式ドキュメントに従って、GWLB と VNet を作成します (「Azure Gateway Load Balancer を作成する」セクションまで完了させる)。

https://docs.microsoft.com/ja-jp/azure/load-balancer/tutorial-gateway-portal

なお、以下手順で想定する GWLB 構成は以下の通りです。

  • Frontend IP Configuration: 10.7.0.4
  • Backend Pool (VXLAN): Internal and External、後はデフォルト設定
  • Health Probe: 8080/tcp

2. Azure VM のデプロイ

個人的に Debian 系の OS が扱いやすいので、GWLB の VNet に Ubuntu 18.04 の Azure VM をデプロイします。

3. GWLB のバックエンドに追加

作成した Azure VM を GWLB のバックエンドに追加します。Azure Portal で GWLB を開いて、バックエンドに追加するだけです。

4. GWLB のための設定を行う

デプロイした Azure VM にログインして諸々のセッティングをしていきます。

正常性プローブ

まずは、正常性プローブに応答するプロセスを立ち上げます。自分の場合だと 8080/tcp をチェックする正常性プローブを設定していたので、以下のように HTTP Server を立ち上げておきます。

mkdir -p /tmp/www
cd /tmp/www
python3 -m http.server 8080 &>/dev/null &

Load Balancer (168.63.129.16) と TCP Connection が確立できていれば OK です [1]

root@vm-nva:~# ss -tlnp sport = :8080
State    Recv-Q   Send-Q   Local Address:Port   Peer Address:Port
LISTEN   0        5              0.0.0.0:8080           0.0.0.0:*  users:(("python3",pid=2047,fd=3))

root@vm-nva:~# ss -nt state established 'src :8080'
Recv-Q    Send-Q   Local Address:Port     Peer Address:Port
0         0             10.7.1.5:8080   168.63.129.16:60179

VXLAN 仮想インターフェイス

次に、コンシューマーから横流しされたパケットは VXLAN でカプセル化された状態で届くため、VXLAN を喋れるインターフェイスを作成していきます。

バックエンドプールを作成する際、Internal と External の VXLAN に関しての設定を行います。なぜ 2 つあるんだ?? という疑問が最初あったのですが、Internal は「プロバイダーの NVA」と「コンシューマーの Azure VM」間の VXLAN Tunnel、External は「インターネット上ホスト」と「プロバイダーの NVA」間の VXLAN Tunnelを指している、と理解するとわかりやすいです。既定だと、Internal は 10800/udp (VNI:800) で、External は 10801/udp (VNI:801) を使う事になってます。

Internet Host <-- External --> Provider NVA <-- Internal --> Consumer VM

Ingree パケットの処理を図式化すると以下の通りです。インターネット上ホストがクライアントになる場合の SYN パケット等が相当します。

一方、Egress パケットの処理を図式化すると以下の通りです。インターネット上ホストがクライアントになる場合の SYN/ACK、コンシューマーの Azure VM がクライアントになる場合の SYN 等が相当します。

普通に eth0 に届くパケットを tcpdump で観測すると、以下のようにアンダーレイ側の UDP パケットが見えるだけです。

root@vm-nva:~# tcpdump -ni eth0 -w nva.pcap 'host 10.7.0.4 and port (10800 or 10801)'
root@vm-nva:~# tcpdump -r nva.pcap -nv
reading from file nva.pcap, link-type EN10MB (Ethernet)
20:17:38.426378 IP (tos 0x0, ttl 42, id 0, offset 0, flags [DF], proto UDP (17), length 110)
    10.7.0.4.26968 > 10.7.1.5.10801: UDP, length 82
20:17:40.438039 IP (tos 0x0, ttl 42, id 0, offset 0, flags [DF], proto UDP (17), length 110)
    10.7.0.4.26885 > 10.7.1.5.10801: UDP, length 82
20:17:40.560311 IP (tos 0x0, ttl 127, id 0, offset 0, flags [DF], proto UDP (17), length 122)
    10.7.0.4.5120 > 10.7.1.5.10801: UDP, length 94

ただ、このパケットのペイロードには、コンシューマーのパケットがカプセル化されています。パケットを採取して Wireshark で VXLAN としてデコードしてあげると中身がわかります。GUI でやる場合は [Analyze] -> [Decode as ...] を押して、以下のように設定します。

tshark でやる場合は以下のコマンドを実行するのと等価です。

root@vm-nva:~# tshark -d udp.port==10800,vxlan -d udp.port==10801,vxlan -r nva.pcap

https://explainshell.com/explain?cmd=tshark+-d+udp.port%3D%3D10800%2Cvxlan+-d+udp.port%3D%3D10801%2Cvxlan+-r+nva.pcap

仮想インターフェイスの作り方は色々あるとは思いますが、今回は以下のようにブリッジとともに設定してみました。

dev="eth0"
remote="10.7.0.4"
in_port=10800
in_vni=800
ex_port=10801
ex_vni=801

# add bridge
ip link add dev br0 type bridge
ip address add dev br0 192.168.255.1/28
ip link set br0 up

# add interfaces
ip link add vxlan-in type vxlan id $in_vni remote $remote dstport $in_port dev $dev
ip link add vxlan-ex type vxlan id $ex_vni remote $remote dstport $ex_port dev $dev

ip address add 192.168.255.2/28 dev vxlan-in
ip address add 192.168.255.3/28 dev vxlan-ex

ip link set vxlan-in master br0
ip link set vxlan-ex master br0

ip link set vxlan-in up
ip link set vxlan-ex up

実行後は、GWLB 経由でコンシューマーの通信が出来るようになっているはずです。また、NVA の仮想インターフェイス (e.g. vxlan-in) 上でコンシューマーのパケットが見えるようになっているはずです。

root@vm-nva:~# tcpdump -ni vxlan-in
17:35:06.116276 IP [Consumer Client IP]>.1234 > [Consumer ILPIP or Public LB]>.8080: Flags [F.], seq 76...

5. NVA っぽいことをしてみる

試しに透過的なファイアウォールを構成してみます。iptablesphysdev モジュールで、ブリッジを通る通信 (8.8.8.8 からの 22/tcp に対するアクセス) をブロックしてみました。

iptables -A FORWARD -m physdev --physdev-in vxlan-ex --physdev-is-bridged -s 8.8.8.8 -p tcp --dport 22 -j DROP

iptables の使い方は以下のような文献を漁ってみてください。

https://kazmax.zpp.jp/cmd/i/iptables.8.html

パフォーマンス測定

せっかくなのでネットワークレイテンシの増加がどれくらい発生するか確認してみることにします。


出典[2]

以下の環境で、GWLB を経由する場合とそうでない場合のそれぞれについて、Client <-> Server 間の 3-way handshake latency を調べました。テスト用の VM Size なので現実世界に完全に即しているわけではないけど、雰囲気を感じることは出来るんじゃないでしょうか (主観的な意見)。

  • Client: Azure VM
    • Region: East US
    • Size: Standard B2ms
    • OS: Ubuntu 18.04.6 LTS (5.4.0-1063-azure)
  • Server (Consumer): ILPIP + Azure VM
    • Region: East US
    • Size: Standard B2ms
    • OS: Ubuntu 18.04.6 LTS (5.4.0-1063-azure)
  • GWLB:
    • Region: East US
  • GWLB NVA: Azure VM
    • Region: East US
    • Size: Standard B2ms
    • OS: Ubuntu 18.04.6 LTS (5.4.0-1063-azure)

測定に使ったのは ethr です。

https://github.com/microsoft/ethr

GWLB がない時

root@client-vm:~# ./ethr -c <server-ilpip> -p tcp -t pi -d 60s
...
  Sent = 60, Received = 60, Lost = 0
-----------------------------------------------------------------------------------------
      Avg       Min       50%       90%       95%       99%     99.9%    99.99%       Max
  1.469ms 966.721us   1.308ms   1.908ms   2.101ms   2.190ms   2.190ms   2.190ms   3.892ms
-----------------------------------------------------------------------------------------

GWLB がある時

root@client-vm:~# ./ethr -c <server-ilpip> -p tcp -t pi -d 60s
...
  Sent = 60, Received = 60, Lost = 0
-----------------------------------------------------------------------------------------
      Avg       Min       50%       90%       95%       99%     99.9%    99.99%       Max
  6.078ms   5.208ms   5.756ms   6.537ms   8.263ms   9.835ms   9.835ms   9.835ms  11.396ms
-----------------------------------------------------------------------------------------

このデータに加えて、今回の NVA 上でのパケット処理時間 (eth0 に入ってきたパケットが再び eth0 から出ていくまでの平均時間) は大凡 0.1 ms だったので、レイテンシ増加に寄与している大部分は LBGW のデータ転送だと言えます。

3-way handshake latency で 4ms ~ 5ms の増加なので、RTT に換算すると 3ms ~ 4ms のレイテンシ増加といったところでしょうか。GWLB が同一リージョンであってもこの程度のパフォーマンス悪化は避けられず、他リージョンともなるとさらにリージョン間 RTT が加わります。レイテンシにシビアな場合は、GWLB を他リージョンにデプロイするのはやめたほうが懸命かもしれません

https://docs.microsoft.com/ja-jp/azure/networking/azure-network-latency

参考文献

https://docs.microsoft.com/ja-jp/azure/load-balancer/gateway-overview
https://docs.microsoft.com/ja-jp/azure/load-balancer/tutorial-gateway-portal
https://man7.org/linux/man-pages/man8/ip-link.8.html
https://markunet.github.io/blog/Intro_Linux_interfaces_JP/
https://blog.tiqwab.com/2021/07/11/linux-network-vxlan.html
http://infratraining.blogspot.com/2016/02/nsx-for-vspherevxlanwireshark.html

脚注
  1. 現状、GWLB では正常性に関するメトリックが利用できないみたいなので、Health Probe Status は NVA (Azure VM) 側で判断するしかなさそうです。 ↩︎

  2. https://omocoro.jp/kiji/120163/ ↩︎

Discussion