連載:コードから触るIIS 8

連載:コードから触るIIS 8

Webファームによる負荷分散(1): Webファームの基本構造と構成

2014年5月14日

ASP.NETアプリをスケーリングする方法の1つとして、複数のサーバーによる水平負荷分散を実現する「Webガーデン」というIIS機能について説明する。

株式会社グラニ 田中 孝佳
  • このエントリーをはてなブックマークに追加

 前回の記事ではWebガーデンによりIISのワーカープロセスを複数起動することによる負荷分散を紹介した。今回は、複数のサーバーによる水平負荷分散を実現するIISの機能として「Webファーム」について紹介する。

Webファームの概念

 Webファームは第2回で紹介したApplication Request Routing(以下、ARR)の機能の1つとしてインストールされる。ARRを使うことによって、別のサイトや同じIISの別のWebサイトにリバースプロキシする方法を紹介したが、Webファームでは同じアプリケーションがホストされている複数のIISのWebサイトに対してリバースプロキシすることにより、負荷分散を行う。

図1 リバースプロキシにより負荷分散をしている様子

 エンドユーザーのリクエストは、まず上の図でいうところの「webfarm-ctrl」に到達する。このサーバーは「webfarm-node1」もしくは「webfarm-node2」にリバースプロキシする役目を担っている。Webfarm-ctrlはリバースプロキシするだけであり、実質的な処理は各nodeサーバーが行う。そのため、単位時間当たりの1台あたりの処理数が同じでも全体での処理数を増やせるというのがWebファームのメリットである。

 WebファームはARRの機能である。そのため、リバースプロキシするサーバー(上の図のwebfarm-ctrl)が単一障害点となることを、Webファームの機能だけでは避けることが難しい。各nodeのサーバーが障害を起こした場合は、このサーバーをwebfarm-ctrlサーバーが負荷分散する対象から外せばよいが、webfarm-ctrl自体が障害を起こした場合、全てのリクエストが処理できなくなる。

 Webfarm-ctrl自体を冗長化するには、例えばWindows Serverの機能である「ネットワーク負荷分散(Network Load Balancing。以下、NLB)を使う方法がある(図2)。

図2 リバースプロキシ元のサーバーをNLBにより冗長化している様子

 OSI参照モデルという視点に立つと、ARRが「レイヤー7のアプリケーション層での情報を基に負荷分散する」のに対し、NLBは「レイヤー3のネットワーク層での情報を基に負荷分散する」という違いがある。本連載では、NLBによる負荷分散は対象とせず、ARRによる負荷分散によるWebファームについて解説する。

Webファームの構築

 今回は図1に描いた3台構成によるWebファームを実際に構築してみたいと思う。サーバー名は図の通り、「webfarm-ctrl」「webfarm-node1」「webfram-node2」とした。

全サーバーへのIIS&ARRモジュールのインストール

 まず、3台ともにIISおよびARRモジュールをインストールする。インストール手順は第2回の記事を参考にしてほしい。

nodeサーバーの構築

 次に、2台のnodeサーバーの構築を行う。これらのサーバーは通常のASP.NETアプリケーションをホストすることになるが、負荷分散という目的上、同じIISの設定で同じコンテンツをホストするようにする。今回は手作業で同じアプリケーションをデプロイするだけであるが、実際に運用する場合ではサーバー間で同じ状態になるような仕組みを導入するべきだろう。筆者の会社の環境では、複数のnodeサーバーにIISの設定やASP.NETアプリケーションをプッシュして同期させる仕組みを構築し、利用している。また、Microsoft Web Farm Frameworkという複数のサーバーを1台のプライマリーサーバーの設定およびコンテンツと同期させることができるツールが提供されているが、残念ながらこれはIIS 8以降をサポートしていない。

 今回はサンプルということで、ホストされているサーバーのコンピューター名を表示するだけのASP.NET MVCアプリケーションを作成し、デプロイした。実際のコードはGitHubに公開しているので、必要があれば各自ビルドして利用してほしい。

リバースプロキシ・サーバーの構築

 次に、リバースプロキシをする側のwebfarm-ctrlサーバーの構築を行う。ARRのインストールまで完了した状態で、IISマネージャーを開くと、左側の[接続]ペインに[Server Farms]が表示されている(図3)。ここからWebファームを作成する。

図3 IISマネージャーの[接続]ペインに表示された[Server Farms]

 [Server Farms]を右クリックし、(コンテキストメニューから)[Create Server Farm]を選択する。

 次の画面でWeb Farmの名前を入力する(図4)。今回はサーバーのホスト名と同じにしたが、任意の名前で構わない。

図4 サーバーファームの名前を入力するフォーム

 次の画面ではサーバーを追加する(図5~6)。今回はテストということで、2つのnodeサーバーのパブリックアドレスである「webfarm-node1.cloudapp.net」と「webfarm-node2.cloudapp.net」を追加した(これらのサーバーは全てMicrosoft Azure上の仮想マシンとして構築した)。構築する環境に合わせて、プライベートなアドレスやIPアドレスを使用することも可能である。また、追加する際、[Online]というチェックボックスはチェックが入った状態にしておいてほしい(確認した環境ではデフォルトで入っていた)。このチェックを外すと、サーバーをOfflineとして、登録はするものの、リバースプロキシの対象から除外することができる。この操作は後で確認する。

図5~6 サーバー名を追加してサーバーを追加しようとしている画面(図5)と、2台のサーバーを追加し終わった画面(図6)

 サーバーを2台追加したところで、[終了]ボタンを押すと、[Rewrite Rules]というダイアログボックスが表示される。

図7 Webファームのための単純なURL書き換えルールを追加するかどうかを確認するダイアログ
図7 Webファームのための単純なURL書き換えルールを追加するかどうかを確認するダイアログ

 これは、今作成したWebファームに対して標準的なリバースプロキシのルールを設定するかどうかを確認するダイアログである。Routingのルールというのは、第2回で紹介したARRによるURL書き換えのルールである。本番環境では、ここで作成されるルールをそのまま使うことは少ないと思われるので[いいえ(No)]でよいが、今回は検証であるため[はい(Yes)]を選択する。以上でWebファーム の構築は完了である。

動作確認

 さっそく、リバースプロキシ元のサーバーにアクセスしてみよう。今回の例では、「webfarm-ctrl.cloudapp.net」にブラウザーでアクセスしてみる。すると図8のように、node1もしくはnode2のマシン名が表示されているはずである。

図8 「http://webfarm-ctrl.cloudapp.net」にブラウザーでアクセスした画面

「webfarm-node1」がレスポンスを返していることが確認できる。

Webファーム設定内容の確認

 実際に何が起こっているか、IISマネージャーに戻って確認してみよう。

 まず、[接続]ペインの[Server Farms]の下に作成した[webfarm-ctrl.cloudapp.net]という項目が追加されているはずである。

 次に、[接続]ペインの([Default Web Apps]ではなく)マシン名が表示されている項目を選択し(図9)、[URL 書き換え]をダブルクリックして(図10)、追加された書き換えルールを確認してみる。「ARR_webfarm-ctrl.cloudapp.net_loadbalance」という名前のルールが追加されているはずである(図11)。さらにこれをダブルクリックして詳細を表示してみよう。

図9 [接続]ペインに作成したWebファームが追加されている
図9 [接続]ペインに作成したWebファームが追加されている
マシン名が表示されているIIS全体の設定(図10)

[URL 書き換え]をダブルクリック

図10~11 マシン名が表示されているIIS全体の設定(図10)から、[URL 書き換え]を選択して表示された画面(図11)

 図12のように全てのURLパターンに対して、「サーバーファームにルーティング」というルールが設定されていることが確認できる。これが先ほどのダイアログで追加されたルールである。

図12 作成された、Webファームのための単純なルールの詳細画面

1つのnodeサーバーをオフラインにしてみる

 さて、[接続]ペインの[webfarm-ctrl.cloudapp.net]というサーバーファームを開き、その下の[Servers]を選択しよう(図13)。追加したサーバー一覧がリスト表示されている。表示されているサーバーのうち、先ほどブラウザー上に表示されたマシン名のサーバー(今回の場合だと、「webfarm-node1.cloudapp.net」)を右クリックして、[Take Server Offline]を選択してみよう(図14)。サーバーがOfflineとして表示される(図15)。

図13~15 [接続]ペインの[webfarm-ctrl.cloudapp.net]というWebファームを開き、[Servers]を選択してサーバーの一覧を表示した画面(図13)。および、そこからサーバーを選択して右クリックした画面(図14)と[Take Server Offline]を選択してOfflineにした後の画面(図15)

 この状態で再度、ブラウザーで「webfarm-ctrl.cloudapp.net」にアクセスしてみると、表示されるマシン名がnode2になっているはずである(図16)。これは、node1がOfflineになったため、node2にのみリバースプロキシするようになったためである。

図16 webfarm-node1をOfflineにし、webfarm-node2のみがOnlineな状態で「http://webfarm-ctrl.cloudapp.net」にアクセスした画面

C#コードによるWebファームの構築

 最後にWebファームへのサーバーの追加、およびサーバーのステータスを更新する処理をC#コードから実行するサンプルを紹介したい。

 このコードを実行するためのVisual Studioプロジェクトの作成および設定も、第2回を参考にしてほしい。またこのサンプルもGitHubに「WebFarmTool」というフォルダー以下にアップロードしている。なお、コード例では存在しないアドレスのサーバーを追加しているので注意されたい。

C#
using System.Linq;
using Microsoft.Web.Administration;

namespace WebFarmTool
{
  class Program
  {
    static void Main(string[] args)
    {
      var program = new Program();
      // onlineServerというアドレスのサーバーをWebFarmに追加
      program.AddServer("webfarm-ctrl.cloudapp.net", "onlineServer");
      // onlineServerというアドレスのサーバーをWebFarmにOffline状態で追加
      program.AddServer("webfarm-ctrl.cloudapp.net", "offlineServer", false);
      // webfarm-node1.cloudapp.netサーバーをOnline状態にする
      program.UpdateServerStatus("webfarm-ctrl.cloudapp.net", "webfarm-node1.cloudapp.net", true);
      // webfarm-node2.cloudapp.netサーバーをOffline状態にする
      program.UpdateServerStatus("webfarm-ctrl.cloudapp.net", "webfarm-node2.cloudapp.net", false);
    }

    void AddServer(string webFarmName, string serverName, bool isOnline = true)
    {
      using (var manager = new ServerManager())
      {
        // ApplicationHostの設定を取得
        var appHostConfig = manager.GetApplicationHostConfiguration();

        // WebFarmの設定リストから指定したWebFarm名の要素を取得
        var webFarmsSection = appHostConfig.GetSection("webFarms");
        var webFarmSection = webFarmsSection.GetCollection().First(e => e["name"] as string == webFarmName);
        
        // 子要素のserver要素のリストを取得
        var serversCollection = webFarmSection.GetCollection();
        // 新規のserver要素を作成して追加
        var newServer = serversCollection.CreateElement("server");
        newServer["address"] = serverName;
        newServer["enabled"] = isOnline;
        serversCollection.Add(newServer);

        manager.CommitChanges();
      }
    }


    void UpdateServerStatus(string webFarmName, string serverName, bool isOnline)
    {
      using (var manager = new ServerManager())
      {
        // ApplicationHostの設定を取得
        var appHostConfig = manager.GetApplicationHostConfiguration();

        // WebFarmの設定リストから指定したWebFarm名の要素を取得
        var webFarmsSection = appHostConfig.GetSection("webFarms");
        var webFarmSection = webFarmsSection.GetCollection().First(e => e["name"] as string == webFarmName);
        
        // 子要素のserver要素のリストを取得
        var serversCollection = webFarmSection.GetCollection();
        // 指定したサーバー名の要素を取得して属性を更新
        var server = serversCollection.First(e => e["address"] as string == serverName);
        server["enabled"] = isOnline;

        manager.CommitChanges();
      }
    }
  }
}
Webファームにサーバーを追加するメソッド(AddServer)と、サーバーの状態を更新するメソッド(UpdateServerStatus)

 今回は、Webファームの基本的な構造を紹介し、実際に3台構成のWebファームを構成してみた。次回以降は、ヘルスチェック、負荷分散の種類、セッションアフィニティなどの機能を紹介したい。

※以下では、本稿の前後を合わせて5回分(第7回~第11回)のみ表示しています。
 連載の全タイトルを参照するには、[この記事の連載 INDEX]を参照してください。

7. RedisをBackplaneとしたSignalRのスケールアウト

SignalRアプリをスケールアウトする際の注意点と、それを回避するためのBackplane機構について説明。さらにRedisをBackplaneとして活用する方法を解説する。

8. Webガーデンによるアプリケーションプールのマルチプロセス化

ASP.NET アプリのスケーリング方法を解説。今回は、Webサイトを1つのアプリケーションプール上の複数のワーカープロセスで動かす「Webガーデン」について説明する。

9. 【現在、表示中】≫ Webファームによる負荷分散(1): Webファームの基本構造と構成

ASP.NETアプリをスケーリングする方法の1つとして、複数のサーバーによる水平負荷分散を実現する「Webガーデン」というIIS機能について説明する。

10. Webファームによる負荷分散(2): IISマネージャー/コードによる操作

Webファームに備えられている機能とは? IISマネージャーによるWebファームの操作や、コードによる操作について説明する。

11. Webファームによる負荷分散(3):HTTPSとWebファームの関連

IISで構築したWebファーム内における複数台のサーバーでHTTPSを有効にする場合、どんな方法があるのか? 各方法によるサーバー構成を、PowerShellを用いて行う方法を解説する。連載最終回。

サイトからのお知らせ

Twitterでつぶやこう!