前回の記事:Web Role をデプロイすると何が起こっているのか では、
Web Role 上のアプリケーションが、IIS上でどこのディレクトリ、設定で
動作しているのかを、VM上で確認した。
その後、MVCアプリケーションのViewsフォルダの物理位置を他のフォルダへ移動し、
Viewsフォルダは、仮想パスとして、移動した先のフォルダを設定した。
前回、これ等はVM上にリモートデスクトップで入って手作業で変更したので、
手間が掛かり、クラウドとして運用を考えた場合はダメダメ過ぎる。
但し、MVC動作として、Azure上でも問題無く動作出来る事が分かった事は、
収穫に値するでしょう(当たり前の事なんだけどね)。
今回は、ファイルコピー、仮想フォルダ設定を自動化させる為のモジュールを生成します。
デプロイ後に、VMへリモートデスクトップで更に設定・・というのは当然ナシ!
本題の前に、Startupタスクについて軽く説明します。
Web Role のプロジェクトで、OS設定をする方法は、幾く通りか方法があります。
ざっと思いつくのは、
・Windows サービスを入れちゃう(サービス作って、sc.exeで入れ込むとか)
・プログラム作ってゴニョゴニョするw
どちらも、Startupタスクで実行させる事は共通です。
※あるOSSで、RoleEntryPointで何かの設定をしているコードを見かけてるので、
RoleEntryPointでもゴニョゴニョ出来るかもしれません。
Windows Azure での、Startupタスクの使用方法は、
ServiceDefinition.csdef に
<Startup>
<Task commandLine="実行させたいバッチファイル.cmd" />
</Startup>
簡単に言えばこれだけ。他のオプションもあります。
例えば、実行させたいバッチを、startup.cmd としましょう。
Webプロジェクト内に、startup.cmdというテキストファイルを用意し、
中身は、**.exe でも call でも、普通に行いたいバッチを書きます。
.cmdとしましたが、.batでも良いし、.vbsとしてVBスクリプトを記述しても良いです。
デプロイを行った瞬間に、そのバッチが動くというのが、Startupタスク です。
それでは、本題に。
IISの設定を行うには、いろいろ方法があります。
設定するアプリケーションを作成するか、vbs設定、PowerShellスクリプト設定など。
私は、スクリプトはあまり得意ではないので、アプリケーションを作成します。
更に、ここで利用するモジュールは、
Microsoft.Web.Administration.dll です。
このファイルは、IISをインストールしたWindowsなら、
<システムフォルダ>\inetsrv の中に見つかるはずです。
では、IISを設定するアプリケーションを作成します。
※ デプロイするWeb Role のプロジェクト名と、ローカルストレージ名が決定している事!
プロジェクト名は、startupInfrastructure (任意)とします。
プロジェクトは、C#、コンソールアプリケーション、.NET Framework 4
コンソールでのデフォルトのフレームワークは、.NET Framework 4 Client Profile ですが、
Microsoft.Web.Administration で利用する名前空間がみつからないので、
.NET Framework 4 とします。
プログラムの流れは、
・IISサイトの確認
・ローカルストレージフォルダの確認
・フォルダ作成、ファイルコピー
・フォルダ・ファイルのアクセス制限の設定
・仮想ディレクトリの設定
となります。
また、Startupタスクの動作は、バックグラウンドとして動作させ、
この、IIS設定アプリケーションでは、設定できるまでリトライループを掛けます。
これは、Startupタスクの動きを確認すると分かるのですが、
通常(フォアグランド)で動作させると、Startupタスクのバッチが完了してから、
IIS上へデプロイされるので、IISの設定が出来ないからです。
バックグラウンド動作であれば、バッチ起動と同時にデプロイされるので、
設定ができますが、両方が一緒に動作するので、
デプロイ設定されるまで、Waitをかけたループを行います。
プロジェクトの先頭は、
using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Security.Principal;
using System.Security.AccessControl;
using Microsoft.Web.Administration;
namespace startupInfrastructure
{
class Program
{
private const string LOCAL_RESOURCE_ROOT = @"C:\Resources\Directory";
private static string webProjectName = "MvcApplication1";
private static string storageName = "WebContentsStorage";
static void Main(string[] args)
{
こんな感じで始まります。
webProjectName、storageNameは、WebRoleに合わせてください。
では、IISサイトの確認のメソッド
IISSiteCheck(webProjectName); の様に呼ぶ
public static void IISSiteCheck(string siteName)
{
while (true)
{
bool isLoop = true;
try
{
ServerManager serverManager = new ServerManager();
foreach (Site site in serverManager.Sites)
{
if (site.Name.StartsWith(siteName))
{
isLoop = false;
break;
}
}
}
catch
{
isLoop = true;
}
if (isLoop)
{
Thread.Sleep(1000);
}
else
{
break;
}
}
}
ServerManagerクラスは、Microsoft.Web.Administration 内のクラスです。
IISのサイト一覧に、Web Role のプロジェクト名が現れたら、ループを抜けます。
スマートでは無いですが、Startupタスクで行うとこのようになるのは仕方がないようです。
ローカルストレージフォルダの確認
string localResourceRootPath =
GetLocalStorageDirectory(webProjectName, storageName); の様に呼ぶ
public static string GetLocalStorageDirectory(string projectName, string storageName)
{
string result = string.Empty;
while (string.IsNullOrEmpty(result))
{
try
{
string[] directorys = Directory.GetDirectories(LOCAL_RESOURCE_ROOT);
foreach (string s in directorys)
{
int find = s.IndexOf(string.Format(".{0}.{1}", projectName, storageName));
if (find >= 0)
{
result = s;
}
}
}
catch
{
}
if (string.IsNullOrEmpty(result))
{
Thread.Sleep(1000);
}
}
return result;
}
ローカルストレージにフォルダ作成
以下のような感じで、ローカルストレージ内に、Viewsフォルダを作成する。
string path = Path.Combine(localResourceRootPath, “Views”);
Directory.CreateDirectory(path);
FileSystemAccessRule rule = new FileSystemAccessRule(
new NTAccount(“Everyone”),
FileSystemRights.Modify,
InheritanceFlags.ObjectInherit | InheritanceFlags.ContainerInherit,
PropagationFlags.None,
AccessControlType.Allow);
DirectorySecurity security = Directory.GetAccessControl(path);
security.SetAccessRule(rule);
Directory.SetAccessControl(path, security);
次にIISのフォルダを設定します。
ServerManager serverManager = new ServerManager();
Application webApplication = null;
VirtualDirectory appDirectory = null;
VirtualDirectory viewsDirectory = null;
foreach (Site site in serverManager.Sites)
{
if (site.Name.StartsWith(webProjectName))
{
foreach (Application app in site.Applications)
{
if (app.Path == "/")
{
webApplication = app;
foreach (VirtualDirectory virtualDirectory in app.VirtualDirectories)
{
if (virtualDirectory.Path.ToLower() == "/")
{
appDirectory = virtualDirectory;
continue;
}
if (virtualDirectory.Path.ToLower() == "/views")
{
viewsDirectory = virtualDirectory;
continue;
}
}
break;
}
}
break;
}
}
string server_rootDirectoryPath = appDirectory.PhysicalPath;
string server_viewsDirectoryPath = Path.Combine(server_rootDirectoryPath, "Views");
if (Directory.Exists(server_viewsDirectoryPath))
{
// 実ディレクトリから、ローカルストレージへフォルダ階層毎にコピーする。
// コピーしたフォルダ・ファイルのアクセス制限も設定します。:ソースコードは割愛します。
}
if (viewsDirectory == null)
{
webApplication.VirtualDirectories.Add("/Views", localResourceViewsPath);
}
else
{
viewsDirectory.PhysicalPath = localResourceViewsPath;
}
serverManager.CommitChanges();
上記の、VirtualDirectory変数には、対象とするIISのフォルダ情報が入ります。
仮想ディレクトリの情報は、PhysicalPathメンバで設定します。
以上で、設定プログラムは完了。
Azureパッケージに入れるため、Web Role アプリケーションからプロジェクト参照します。
また、App.configは、startupInfrastructure.exe.configとして存在する必要があるので、
Web Roleプロジェクトに、startupInfrastructure.exe.configを作成し、
App.configの内容を転記しておきます。
Microsoft.Web.Administration.dll は、出力ディレクトリにコピーされるようにしてください。
startup.cmdの内容は、
----
@echo off
startupInfrastructure.exe
----
これだけ。
Startupタスクの定義は、
<Startup>
<Task commandLine="startup.cmd" executionContext="elevated" taskType="background" />
</Startup>
となります。
適切に設定されていれば、デプロイ完了と同時に、
Viewsフォルダがローカルストレージに配置され、仮想ディレクトリとして参照されます。
関根:
57613c86-180f-4a72-b574-f46ea5cd7d46|1|5.0

February 2, 2011 16:05 by
Sekine
Azure SDK 1.3 には新しい機能が追加されましたが、
その中に、Full IIS というのがあります。
仮想OS上のIISとして、ホストするという事なのですが、
当たり前と言えば当たり前。
ここに、大きな落とし穴があります。
実行時の RoleEntryPoint は別アプリケーションドメインで実行されるという事。
Visual Studio でWebアプリケーションを作成すると、
WebRole.cs (.vb) 内で、
RoleEntryPoint 継承クラス、OnStartメソッド内で、
CloudStorageAccount.SetConfigurationSettingPublisher の設定やら、
ログ転送のための、DiagnosticMonitorの設定など行っていました。
アプリケーションの初期処理なども、ここに記述していたかと思います。
それが普通だったのですが、SDK 1.3 & Full IIS では、
Webアプリケーションとは別のアプリケーションドメインで、RoleEntryPoint が実行されるので、
RoleEntryPoint . OnStart で設定しても、Webアプリケーションの動作には反映されなくなります。
解決方法は、
アプリケーション側、パイプライン処理の Global.asax で Application_Start に、全処理を移す事です。
SDK 1.2 で動作していたのを、1.3 で書きなおしたときに動作がおかしいと思ったら見直してみましょ。
以下せつめい
Full IIS と、Hosted Web Core について、
SDK 1.2 までは、Hosted Web Core (WaWebHost.exe)が Web Role をホストして、Webアプリケーションを動作させていました。
SDK 1.3 の登場と Full IIS 設定では、IIS (w3wp.exe)が Web Role をホストします。
但し、Web Role を動作させる際に読まれる RoleEntryPoint を処理するのは、WaIISHost.exe という別のプロセスが処理を行います。
わかりましたね!!(?)
簡単言えば、別々のプロセスなので、RoleEntryPoint で初期設定をしても無駄ってことです。
Visual Studio + SDK 1.3 では、Azure アプリケーション Web ロールを作成すると、
ServiceDefinition.csdef は、デフォルトで Full IIS の設定になります。
こんな感じ、
<?xml version="1.0" encoding="utf-8"?>
<ServiceDefinition name="WindowsAzureSample"
xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceDefinition">
<WebRole name="WebRole1">
<!-- ここから Full IIS 設定 -->
<Sites>
<Site name="Web">
<Bindings>
<Binding name="Endpoint1"
endpointName="Endpoint1" />
</Bindings>
</Site>
</Sites>
<!-- ここまで -->
<Endpoints>
<InputEndpoint name="Endpoint1"
protocol="http"
port="80" />
</Endpoints>
<Imports>
<Import moduleName="Diagnostics" />
</Imports>
<ConfigurationSettings>
<Setting name="DataConnectionString" />
</ConfigurationSettings>
</WebRole>
</ServiceDefinition>
Hosted Web Core で動作させたい場合は、Sites要素をバッサリ削除します。
さて、
Global.asax に処理は移し、
Application_Start を確認したく、ブレークポイントを張った。
デバッガが止まってくれない....
どうやらこれは、デバッガがアタッチするタイミングに問題があるという事なので、
protected void Application_Start()
{
System.Diagnostics.Debugger.Break();
..
と、することで、ちゃんとブレークに引っかかります。
関根:
5cc722ec-1e84-4c5f-8558-b99ae4b6da01|0|.0