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 は有料です。
とは言え、機能や今後の方向性を考えると、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
ということで、送信規則を作って割り当ててみました。
送信規則を作ってみる
ざっくりとした流れとしては、
- Standard Load Balancer を作る
- バックエンドプールを作って仮想マシンを指定する
- 送信規則を作る
という感じです。
バックエンドプールを作って仮想マシンを指定するところまでは、ポータルからできます。
現状、送信規則を作るには、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 では、メトリックを見ることができるので変化を見てみました。
- Allocated SNAT Ports
- Used SNAT Ports
ちゃんと割り当てポートが8になり、使用済みも8になってますね。
※ちなみに Allocated SNAT Portsは、プロトコル毎(TCPとUDP)でそれぞれ設定できるのですが、今回はあらかじめ 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 ~]$
- Allocated SNAT Ports
- Used SNAT Ports
あたり前ですが16本まで張れましたね。