いまさらだけど Azure の Load Balancer のことをちょっと調べてみた

Standard Load Balancer が登場してからずーっと検証しようと思っていた NAT(SNAT)周りですが必要に迫られたのでついでにまとめました。

Azure の NAT まわりのこと

Load Balancer の話をする前に、まず、Azure の NAT の観点でネットワークまわりの現状をまとめます。

いまとなっては当たり前のことですが Azure の VM から直接見える IP アドレスは、192や10から始まるプライベート IP アドレスです。NIC に対して、グローバル IP アドレスが割り当てられているわけではありません。

つまりどういうことかというと、インターネットへの通信を行うには何らかの方法で、プライベート IP アドレスをグローバル IP アドレスに変換してあげる必要があります。

ここで、Azure の仮想マシンにパブリック IP アドレスを割り当てる場合(正確には仮想マシンの持つ NIC に割り当てる)と、割り当てない場合に、どのような NAT がされるかを考えます。

パブリック IP を割り当てる場合

パブリック IP アドレスを割り当てる場合は簡単です。単純に、割り当てたパブリック IP アドレスが外側のアドレスとして使われて、NAT 変換が行われます。
ドキュメントはこちら。

シナリオ 1:インスタンス レベルのパブリック IP アドレスがある VM
このシナリオでは、VM にはインスタンス レベルのパブリック IP (ILPIP) が割り当てられています。 送信接続に関する限り、VM が負荷分散されているかどうかは関係ありません。 このシナリオは他のシナリオよりも優先されます。 ILPIP が使用される場合、すべての送信フローで VM によって ILPIP が使用されます。
VM に割り当てられたパブリック IP は (1 対多ではなく) 1 対 1 の関係であり、1 対 1 のステートレス NAT として実装されます。 ポート マスカレード (PAT) は使用されず、VM は使用可能なすべてのエフェメラル ポートを備えます。
Azure の送信接続 | Microsoft Docs

パブリック IP を割り当てない場合

一方、パブリック IP アドレスを割り当てない場合どうなるかというと、Azure が暗黙のうちに用意してくれる NAT 機能が、これまた暗黙のうちに用意してくれているグローバルの IP アドレスに変換します。
ドキュメントはこちら。

シナリオ 3:インスタンス レベルのパブリック IP アドレスがないスタンドアロン VM
VM が送信フローを作成すると、Azure が、送信フローのプライベート ソース IP アドレスをパブリック ソース IP アドレスに変換します。 この送信フローで使用されるパブリック IP アドレスは構成不可能であり、サブスクリプションのパブリック IP リソースの制限に対してカウントされません。 このパブリック IP アドレスはユーザーのものではなく、予約することはできません。 VM、可用性セット、または仮想マシン スケール セットを再デプロイすると、このパブリック IP アドレスは解放され、新しいパブリック IP アドレスが要求されます。 IP アドレスをホワイトリストに登録する場合は、このシナリオを使用しないでください。 代わりに、送信シナリオと、送信接続で使用されるパブリック IP アドレスを明示的に宣言する他の 2 つのシナリオのいずれかを使用します。
Azure の送信接続 | Microsoft Docs

上記のドキュメントの通り、通常は課金されるパブリック IP アドレスですが、Azure が勝手に用意してくれるリソースなので当然課金は発生しません。

ここで問題がひとつ
エンタープライズでよくあるシナリオで、VM にはパブリックの IP アドレスを付与しないことで外部からのアクセスはさせず、ただ VM からはインターネットに接続させたいというケースがあります。さらに、これもよくあるのですが、他の Azure のサービスやアプリケーション等で送信元をホワイトリスト化したい、というケースです。

これまで説明してきた内容からは、送信元パブリック IP アドレスを固定化するには、VM にパブリック IP アドレスを割り当てる必要がありました。ただそうするとインターネット上にエンドポイントを晒してしまうということになってしまい、要件が満たせなくなります。

これを回避する1つの方法としては、プロキシサーバーを立てる方法です。ただ、単純にパブリック IP アドレスを固定化するのに、プロキシサーバーを立てるというのは、仮想マシンのコストや管理の工数を考えるとあまりやりたくはありません。
そこでもう1つの方法として、Azure の Load Balancer を使う方法があります。

ドキュメントのシナリオ2の部分です。

負荷分散 VM が送信フローを作成すると、Azure が、送信フローのプライベート ソース IP アドレスをパブリック ロード バランサー フロントエンドのパブリック IP アドレスに変換します。 Azure は、SNAT を使用してこの機能を実行します。 また、Azure は、PAT を使用して、複数のプライベート IP アドレスをパブリック IP アドレスでマスカレードします。
Azure の送信接続 | Microsoft Docs

AWS だと NAT ゲートウェイというのがあるらしいのですが、Azure の場合はそういうサービスがなく、その代わりに Load Balancer を使うと同じようなことが実現できます。
※ただ、負荷分散はいらないのに Load Balancer のサービスを作らないといけないのはいまいちな点もあるので、おそらくたぶんきっと、NAT ゲートウェイがそろそろ出てくるに違いありません。

Azure の Load Balancer まわりのこと

Azure には、いわゆる L4 のロードバランサーとして、Basic Load Balancer と Standard Load Balancer があります。Standard Load Balancer はそれなりに最近出てきたサービスです。

何が違うの?という具体的な内容は↓の公式ドキュメントに譲りますが、大きな違いとしてはざっくり、AZに対応していること、バックエンドプールとして仮想マシンが指定できること(Basic は原則可用性セットのみ)、その他いろいろ機能が追加されていること、です。
docs.microsoft.com

ちなみに、お金の話をすると、Basic Load Balancer は無料で使えていましたが、Standard Load Balancer は有料です。

azure.microsoft.com

とは言え、機能や今後の方向性を考えると、Standard を使うのがいいと思います。

検証

ようやくここからが今回の記事の本題です。

上記のドキュメントを読むと、Standard Load Balancer については、アウトバウンドの通信を明示的に作ってあげる必要がある、という記述があります。

仮想マシン、可用性セット、仮想マシン スケール セットが送信接続 (アウトバウンド接続) を使用するためには、送信シナリオを明示的に作成する "必要があります"。

送信シナリオは明示的であり、発信接続は指定されるまで存在しません。

実際、Standard Load Balancer のバックエンドにパブリック IP アドレスが割り当てられていない仮想マシンを展開して、インターネットに接続しようとすると、SYN_SENT の状態でとまってしまい通信ができません。

[tsunomur@VM ~]$ netstat -an --tcp | grep 13.71
tcp        0      1 10.8.0.4:37436          13.71.134.177:80        SYN_SENT

ということで、送信規則を作って割り当ててみました。

送信規則を作ってみる

ざっくりとした流れとしては、

  1. Standard Load Balancer を作る
  2. バックエンドプールを作って仮想マシンを指定する
  3. 送信規則を作る

という感じです。

バックエンドプールを作って仮想マシンを指定するところまでは、ポータルからできます。
現状、送信規則を作るには、Azure PowerShell/Azure CLI を使う必要があります。

こんな感じ。

# az network lb outbound-rule create  --resource-group rg  --lb-name lb  --name outboundrule  --frontend-ip-configs LoadBalancerFrontEnd  --protocol All  --idle-timeout 15  --outbound-ports 10000  --address-pool bepooloutbound
AllocatedOutboundPorts    EnableTcpReset    IdleTimeoutInMinutes    Name          Protocol    ProvisioningState    ResourceGroup
------------------------  ----------------  ----------------------  ------------  ----------  -------------------  ---------------
10000                     False             15                      outboundrule  All         Succeeded            rg

あとからパラメーターを変える時はこんな感じ。

az network lb outbound-rule update -g rg --lb-name lb --name outboundrule --enable-tcp-reset true --idle-timeout 4 --protocol tcp --outbound-ports 10000
NAT の割り当てポートを変えてみる

本当にAllocatedOutboundPortsが効いているのか?という疑問がふと出て来たので、実験してみました。


ポート割り当ての数を 8 に

# az network lb outbound-rule update -g rg --lb-name lb --name outboundrule --outbound-ports  8
AllocatedOutboundPorts    EnableTcpReset    IdleTimeoutInMinutes    Name          Protocol    ProvisioningState    ResourceGroup
------------------------  ----------------  ----------------------  ------------  ----------  -------------------  ---------------
8                         True              4                       outboundrule  Tcp         Succeeded            rg

VM からコネクションを 8 本張ってみる。

[tsunomur@VM ~]$ for i in `seq 8`; do nc nomupro.com 80&  done
[tsunomur@VM ~]$ netstat -an --tcp | grep 13.71
tcp        0      0 10.8.0.4:41290          13.71.134.177:80        ESTABLISHED
tcp        0      0 10.8.0.4:41276          13.71.134.177:80        ESTABLISHED
tcp        0      0 10.8.0.4:41288          13.71.134.177:80        ESTABLISHED
tcp        0      0 10.8.0.4:41286          13.71.134.177:80        ESTABLISHED
tcp        0      0 10.8.0.4:41282          13.71.134.177:80        ESTABLISHED
tcp        0      0 10.8.0.4:41284          13.71.134.177:80        ESTABLISHED
tcp        0      0 10.8.0.4:41277          13.71.134.177:80        ESTABLISHED
tcp        0      0 10.8.0.4:41280          13.71.134.177:80        ESTABLISHED

9本目のコネクションを張ってみる。

[tsunomur@VM ~]$ nc nomupro.com 80
Ncat: Connection timed out.
[tsunomur@VM ~]$
[tsunomur@VM ~]$ netstat -an --tcp | grep 13.71
tcp        0      0 10.8.0.4:41290          13.71.134.177:80        ESTABLISHED
tcp        0      0 10.8.0.4:41276          13.71.134.177:80        ESTABLISHED
tcp        0      0 10.8.0.4:41288          13.71.134.177:80        ESTABLISHED
tcp        0      0 10.8.0.4:41286          13.71.134.177:80        ESTABLISHED
tcp        0      1 10.8.0.4:41398          13.71.134.177:80        SYN_SENT
tcp        0      0 10.8.0.4:41282          13.71.134.177:80        ESTABLISHED
tcp        0      0 10.8.0.4:41284          13.71.134.177:80        ESTABLISHED
tcp        0      0 10.8.0.4:41277          13.71.134.177:80        ESTABLISHED
tcp        0      0 10.8.0.4:41280          13.71.134.177:80        ESTABLISHED
[tsunomur@VM ~]$

Standard Load Balancer では、メトリックを見ることができるので変化を見てみました。

f:id:mitsuki0820:20190730232528p:plain

f:id:mitsuki0820:20190730232718p:plain

ちゃんと割り当てポートが8になり、使用済みも8になってますね。
※ちなみに Allocated SNAT Portsは、プロトコル毎(TCPUDP)でそれぞれ設定できるのですが、今回はあらかじめ TCP のみにしています。なので、メトリックの値は 8 になります。

ポート割り当ての数を 16 に

# az network lb outbound-rule update -g rg --lb-name lb --name outboundrule --outbound-ports  16
AllocatedOutboundPorts    EnableTcpReset    IdleTimeoutInMinutes    Name          Protocol    ProvisioningState    ResourceGroup
------------------------  ----------------  ----------------------  ------------  ----------  -------------------  ---------------
16                        True              4                       outboundrule  Tcp         Succeeded            rg

VM からコネクションを 16 本張ってみる。

[tsunomur@VM ~]$ for i in `seq 16`; do nc nomupro.com 80&  done
[tsunomur@VM ~]$ netstat -an --tcp | grep 13.71
tcp        0      0 10.8.0.4:45922          13.71.134.177:80        ESTABLISHED
tcp        0      0 10.8.0.4:45934          13.71.134.177:80        ESTABLISHED
tcp        0      0 10.8.0.4:45930          13.71.134.177:80        ESTABLISHED
tcp        0      0 10.8.0.4:45918          13.71.134.177:80        ESTABLISHED
tcp        0      0 10.8.0.4:45936          13.71.134.177:80        ESTABLISHED
tcp        0      0 10.8.0.4:45914          13.71.134.177:80        ESTABLISHED
tcp        0      0 10.8.0.4:45932          13.71.134.177:80        ESTABLISHED
tcp        0      0 10.8.0.4:45912          13.71.134.177:80        ESTABLISHED
tcp        0      0 10.8.0.4:45916          13.71.134.177:80        ESTABLISHED
tcp        0      0 10.8.0.4:45928          13.71.134.177:80        ESTABLISHED
tcp        0      0 10.8.0.4:45908          13.71.134.177:80        ESTABLISHED
tcp        0      0 10.8.0.4:45910          13.71.134.177:80        ESTABLISHED
tcp        0      0 10.8.0.4:45920          13.71.134.177:80        ESTABLISHED
tcp        0      0 10.8.0.4:45906          13.71.134.177:80        ESTABLISHED
tcp        0      0 10.8.0.4:45926          13.71.134.177:80        ESTABLISHED
tcp        0      0 10.8.0.4:45924          13.71.134.177:80        ESTABLISHED

17 本目のコネクションを張ってみる。

[tsunomur@VM ~]$ nc nomupro.com 80
Ncat: Connection timed out.
[tsunomur@VM ~]$
[tsunomur@VM ~]$ netstat -an --tcp | grep 13.71
tcp        0      0 10.8.0.4:45922          13.71.134.177:80        ESTABLISHED
tcp        0      0 10.8.0.4:45934          13.71.134.177:80        ESTABLISHED
tcp        0      0 10.8.0.4:45930          13.71.134.177:80        ESTABLISHED
tcp        0      0 10.8.0.4:45918          13.71.134.177:80        ESTABLISHED
tcp        0      0 10.8.0.4:45936          13.71.134.177:80        ESTABLISHED
tcp        0      0 10.8.0.4:45914          13.71.134.177:80        ESTABLISHED
tcp        0      0 10.8.0.4:45932          13.71.134.177:80        ESTABLISHED
tcp        0      0 10.8.0.4:45912          13.71.134.177:80        ESTABLISHED
tcp        0      1 10.8.0.4:46108          13.71.134.177:80        SYN_SENT
tcp        0      0 10.8.0.4:45916          13.71.134.177:80        ESTABLISHED
tcp        0      0 10.8.0.4:45928          13.71.134.177:80        ESTABLISHED
tcp        0      0 10.8.0.4:45908          13.71.134.177:80        ESTABLISHED
tcp        0      0 10.8.0.4:45910          13.71.134.177:80        ESTABLISHED
tcp        0      0 10.8.0.4:45920          13.71.134.177:80        ESTABLISHED
tcp        0      0 10.8.0.4:45906          13.71.134.177:80        ESTABLISHED
tcp        0      0 10.8.0.4:45926          13.71.134.177:80        ESTABLISHED
tcp        0      0 10.8.0.4:45924          13.71.134.177:80        ESTABLISHED
[tsunomur@VM ~]$

f:id:mitsuki0820:20190730234326p:plain

f:id:mitsuki0820:20190730234213p:plain

あたり前ですが16本まで張れましたね。

まとめ

ということで、ドキュメントに書かれていることができるよね、というあたり前のことを試しただけですが Azure の NAT の理解が少し深まりました。
それにしてもネットワーク周りの検証ってやっぱり地味w

また今度機会と時間があれば

  • 複数のフロントエンド、複数の VM を設定した時にどういう動きになるか
  • Azure Firewall の SNAT の動きってどうなってるのか

のあたりを試してみようと思います。