Auto Scaling Group を使って Windows Server + ASP.NET Web Forms を起動させてみた

2023.07.10

いわさです。

Windows Server の IIS 上でホスティングした ASP.NET Web Forms アプリケーションをオートスケーリングさせる機会があったので検証結果を紹介したいと思います。
今回の構成では Active Directory に参加しない Windows Server を前提に評価しています。

結論としてはこの構成では問題なくオートスケーリング出来ました。
他の Web アプリケーションと同じようにセッションをどこで保持するか注意するくらいでしょうか。(デフォルトはインメモリ)

ウェブアプリケーションの作成

今回は .NET Web アプリケーションを評価したかったのですが、.NET にはいくつかプロジェクトタイプがあります。
今回は最もレガシーな ASP.NET Web Forms で評価してみることにしました。これで問題なければ他のほとんどのプロジェクトタイプでも問題ないであろうと考えています。

Visual Studio で ASP.NET Web Forms の新規アプリケーションを作成します。
今回はデータストアに SQL Server を使う構成にしたかったので、認証機能がセットアップ済みの「Individual Accounts」を選択します。

実行してみると次のようなデフォルトのウェブアプリケーションが実行されます。

今回実装方法は本題ではないので省略しますが、次の 2 点の実装を追加しています。

  • オートスケーリング時に対象インスタンスがわかるようにサーバーのプライベート IP アドレスを画面に表示されるようにした
  • 複数台構成でのセッション状態を確認するために画面入力内容をセッションに保持する機能を追加

作成したアプリケーションをデプロイします。
Publish 機能を使ってフォルダに出力します。後ほどゴールデンイメージ作成用の EC2 へコピーします。

AWS 側のインフラを作成

ここも本題とはあまり関係ないのでハイライト的に紹介します。
冒頭の構成図のようなインフラ環境を用意したいので、VPC 一式と RDS for SQL Server を用意します。

VPC はパブリックサブネットとプライベートサブネット、かつマルチ AZ 構成のものを用意します。
今の VPC 作成ウィザードめちゃ便利だなぁ。

データベースは SQL Server を使いたいので、RDS for SQL Server を使ってみます。
インスタンスのみここでは作成しますが、データベースは Web アプリケーション側のオートマイグレーション機能で DB 作成から全部行ってくれます。

ゴールデンイメージ作成

オートスケーリンググループでは起動テンプレートで指定された AMI をキャパシティに応じて起動します。
そのため事前に AMI を作成しておきます。

Windows Server 2016 の EC2 を作成します。

IIS と ASP.NET を有効化しておきます。

先程作成し、公開用に出力されたアプリケーション一式をデフォルトウェブサイトのディレクトリにコピーします。
実際には仮想ディレクトリ切ったり、アプリケーションプール作成したり色々やるのですが、今回は検証用なのでそのままデフォルトを上書きします。

デフォルトのウェブアプリケーションはローカル DB を使うように構成されているので、先程作成した RDS へ接続するように Web.config の ConnectionStrings を編集しておきます。

Web.config

<?xml version="1.0" encoding="utf-8"?>
<!--
  For more information on how to configure your ASP.NET application, please visit
  https://go.microsoft.com/fwlink/?LinkId=169433
  -->
<configuration>
  <configSections>
    <!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 -->
    <section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
  </configSections>
  <connectionStrings>
    <!--<add name="DefaultConnection" connectionString="Data Source=(LocalDb)\MSSQLLocalDB;AttachDbFilename=|DataDirectory|\aspnet-hoge0710webforms-20230710025329.mdf;Initial Catalog=aspnet-hoge0710webforms-20230710025329;Integrated Security=True" providerName="System.Data.SqlClient" /> -->
    <add name="DefaultConnection" connectionString="Data Source=hoge0710webforms.cmwpejrsdzfj.ap-northeast-1.rds.amazonaws.com,1433;Initial Catalog=aspnet-hoge0710webforms-20230710025329;User ID=admin;Password=hogehoge" providerName="System.Data.SqlClient" />
  </connectionStrings>
  <system.web>
    <authentication mode="None" />

:

実行してみると RDS をデータストアとしてユーザーデータの登録とログイン機能が使えます。
また、カスタマイズ機能の IP アドレス表示と入力内容のセッション保存も正しく機能していそうです。

軽く機能が動作していることが確認出来たのでイメージを作成します。

オートスケーリング構成

イメージが作成出来たら、起動テンプレートを作成します。
ちなみに、以前は起動構成というものも使えましたが、現在は起動テンプレートに移行されました。

起動テンプレートで先程作成したゴールデンイメージを選択します。

オートスケーリンググループで作成した起動テンプレートを指定します。
まずは希望するキャパシティを 1 にしてみます。

自動で EC2 インスタンスが立ち上がりウェブアプリケーションにアクセスすることが出来ました。

スケールアウトさせてみる

希望するキャパシティを変更し、スケールアウトさせてみましょう。

EC2 がもう 1 台追加で自動起動されたことが確認出来ました。
また、ターゲットグループ上で自動起動された EC2 のヘルスチェックが正常になったら、ロードバランサーによる振り分けが開始されます。

ラウンドロビンかつスティッキーセッションを無効化したままなので、アクセスするごとに交互にプライベート IP アドレスが変わることが確認出来ると思います。

交互に EC2 が切り替わりますが、セッションがどのように管理されているか試してみます。
先程の画面で値を入力すると、値がセッションに格納され、別画面で値を確認することが出来ました。

しかし、この画面で更新を行うとセッションから保存した値が表示される場合と表示されない場合が交互に確認出来ると思います。

SessionStateMode の調整が必要

今回の現象は ASP.NET Web Forms のセッション管理がデフォルトでは InProc (インメモリ) となっているためです。
回避策としてはロードバランサーのスティッキーセッションを有効化し、クッキーベースでアクセスインスタンスを固定する方法があります。
他にはセッション管理を SQL Server あるいは別の SessionStateProvider を使うことで Redis などのセッションストアを使うことで複数台の EC2 インスタンスにスケールアウトした場合でもセッションデータを共有することが出来ます。

Web.config

:

    <roleManager>
      <!--
            ASP.NET Membership Role is disabled in this template. Please visit the following link https://go.microsoft.com/fwlink/?LinkId=301889 to learn about the ASP.NET Membership support in this template
        -->
      <providers>
        <clear />
      </providers>
    </roleManager>
    <!--
            If you are deploying to a cloud environment that has multiple web server instances,
            you should change session state mode from "InProc" to "Custom". In addition,
            change the connection string named "DefaultConnection" to connect to an instance
            of SQL Server (including SQL Azure and SQL  Compact) instead of to SQL Server Express.
      -->
    <sessionState mode="InProc" customProvider="DefaultSessionProvider">
      <providers>
        <add name="DefaultSessionProvider" type="System.Web.Providers.DefaultSessionStateProvider, System.Web.Providers, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" connectionStringName="DefaultConnection" />
      </providers>
    </sessionState>
  </system.web>

:

さいごに

本日はAuto Scaling Group で Windows Server + ASP.NET Web Forms を起動させてみました。

他の Web アプリケーションと同じで、セッションだけ気をつければ動作しそうですね。
今回試したシステム構成であれば、オートスケーリングにあたって .NET Web アプリケーション固有の制限はなさそうです。