Azure Load Balancer による Virtual Machine の負荷分散と Network Security Group の関係

掲載内容は個人の見解であり、所属する企業を代表するものではありません.


はじめに

目新しい話では全くないのですが、直感的にわかりにくいなあと思っていた仕様について念の為検証してみました、という記事になります。

この構成の Web システムに対して「左のサブネットからしか接続を受け付けない」という接続元制限の要件があった場合、 中央のロードバランサーが設置されたサブネットに対して NSG : Network Security Group を設置したくなりませんか?

ところが こちらのドキュメント にあるとおり、右のサブネットにはロードバランサー(中央のサブネット)からではなく、クライアントの仮想マシン(左のサブネット)からパケットが飛んでくる、 つまりロードバランサーによって SNAT はされないということですね。

負荷分散プール内の仮想マシン: 適用されるソース ポートおよびアドレス範囲は、元のコンピューターからのもので、ロード バランサーではありません。 宛先ポートとアドレス範囲は、ロード バランサーのものではなく、宛先コンピューターのものになります。

よって NSG による接続元制限に関しては以下のように設計する必要があります

という内容を実際に試してみた、というお話です。 Azure IaaS に慣れ親しんでいる方であれば周知のことかもしれませんが、絵を描いてしまうと逆に分かりにくくなってしまうなあと思い、整理のために検証してみました。

実機検証

検証の準備として、以下の構成を実施しました。

検証1 : NSG を設置しない

まずは負荷分散構成がちゃんと動いていることを確認するために NSG 無しで試します。 コレはまあ動作します。

Web サーバー側のログで接続元の Client IP アドレスを確認すると、以下の2つのクライアントからの HTTP Request が確認出来ます。

検証2 : 右のサブネットに NSG を設置し、Azure Load Balancer のみを許可する

今度は右のサブネットに NSG を設置し、Azure Load Balancer 以外からの通信を全て拒否してみます。 NSG は 優先度 65000 以降に既定のルールとして同じ VNET からの任意の通信を許可するルールが入っていますので、 それより優先度の高い 2048 で全ての通信を拒否し、優先度 1024 で Azure Load Balancer サービスタグだけを許可する構成とします。 これで Load Balancer からの通信は総て許可しているため、負荷分散できそうだと思いませんか? (管理のため SSH の Port 22 も解放しています)

さて、フロントエンド IP が配置されている中央のサブネットには NSG による接続制御はかけていないのですが、クライアント側では curl が Load Balancer 192.168.8.52:80 に接続できずにエラーが発生していることがわかります。 また Web サーバーが配置されている右側のサブネットの NSG では Load Balancer からの任意の通信を許可しているのですが、こちらもログを確認すると 168.63.129.16 からの正常性プローブ以外の通信が確認出来ません。 まずここが直感的にわかりにくいポイントじゃないかと思います。

検証3 : 右のサブネットの NSG でクライアント仮想マシンからの接続を許可する

それでは右のサブネットの NSG で「クライアント仮想マシンからの HTTP リクエストを許可」してみましょう。 ポイントは以下の2点です。

右側の NSG でポートを解放すると、すでに正常性プローブが動作していたため Load Balancer による振り分けはすぐに成功し、正常に負荷分散し始めることがわかります。 ここまでの設定で一般的な要件は満たせていると思います。

検証4 : 中央のサブネットの NSG で接続制限をかける

ところで中央の Load Balancer が配置されたサブネットに対して NSG による接続制限を掛けた場合はどうなるんでしょう?

コレもまた直感に反して通信が通ってしまうんですね。 NSG が仕事してないような気持ちで若干不安になってしまいます。

Azure 仮想ネットワークの裏側

このあたりのカラクリは宇田先生のサポート エンジニアが Azure Networking をじっくりたっぷり語りつくす会の資料が詳しいので、 こちらを読んでいただくとイメージが湧くかもしれません。 (詳しすぎて私には理解が追いつかない部分が多いのですが)

Azure Virtual Network には実体がなく、Azure 基盤の物理サーバー上で動作する VFP : Virtual Filtering Platform で仮想的に実現された SDN : Software Defined Network である、とのことです。 そして Azure Load Balancer や Network Security Group は VFP で実現された1機能に過ぎませんので、こちらも仮想アプライアンス製品のような「実体」があるわけではないということですね。

さて何を説明しているのか私にもよくわからないので、もう少しパケットの気持ちになって考えてみましょう。 上記資料の P19、20 にある図で考えるとわかりやすいと思います。 送信側では Load Balancer の設定が適用されたのち、カプセル化されて転送、受信側ではカプセル化解除ののち、Load Balancer の設定が適用され、NSG の設定が適用される流れになるわけです。

それでは前述の検証した構成に当てはめてみます。 (アンダーレイ側の処理の順番などの詳細までは正確ではないかもしれません)

以上の挙動により、ロードバランサーのフロントエンド IP アドレスが設置されている中央サブネットの NSG がバイパスされているように見える、ということだと思います。

まとめ

Azure Load Balancer や Network Security Group は仮想アプライアンスのような「実体」がなく、仮想ネットワーク内でパケットを転送する際のルールを定めただけのもの、と考えるとわかりやすいような気がします。 上記のような論理的な構成図を書くとシステム全体が俯瞰できて分かりやすいのですが、実際の挙動と直感がミスマッチを起こすという落とし穴があります。 私以外にも落とし穴にハマる方がいないことを祈りつつ、不幸にもハマってしまった方の参考になると良いなと思います。

(補足)外部ロードバランサーの場合は NSG が必須

検証1で NSG 無しで動作確認を取っていますが、コレが動作するのは Public IP を持たない「内部ロードバランサー」を使用しているからです。 これが外部ロードバランサーの場合は NSG が必要になります。

Standard Load Balancer と標準のパブリック IP アドレスは、ネットワーク セキュリティ グループによって開かれない限り、インバウンド接続に対して閉じられています。 NSG は、トラフィックを明示的に許可するために使用されます。 お使いの仮想マシン リソースのサブネットまたは NIC に NSG がない場合、トラフィックはこのリソースに到達することを許可されません。

Standard SKU の外部ロードバランサーでは NSG の受信規則によって、負荷分散用のポートを許可しない限り、トラフィックがホワイトリストとして登録されず負荷分散の通信が許可されません。

検証 1.5 : Azure Load Balancer サービスタグを許可しなかった場合

検証1と2の狭間で右側のサブネットで Azure Load Balancer からの正常性プローブすら拒否していたタイミングがありました。 NSG の受信規則が複雑になってくるとこういうミスもやりがちではないでしょうか。 正常性プローブが通らなければバックエンドプールの仮想マシンはダウンしている状態と判断されますので、検証2で追加したように HTTP を解放していたとしても、負荷分散が行われません。 Load Balancer を経由せずに直接 Web サーバーへ Curl してみると動いているのですが、なぜ負荷分散が出来なくなったのかがわからず(設定ミスに気が付かず)時間を取られました。

プローブが HTTP であれば仮想マシン内に出力されているであろう 通常の Web サーバーのログ解析で確認できるのですが、プローブが HTTP ではなく TCP や UDP だと若干面倒な気がします。 ただ Azure プラットフォームがやってることなので、そちら側からも正常性プローブの成功・失敗が確認出来ます。 確認方法としては Azure Monitor メトリックになるのですが、下図のように「分析情報」をみると分かりやすいと思います。 ご参考まで。