しばやん雑記

Azure とメイドさんが大好きなフリーランスのプログラマーのブログ

ASP.NET 5 アプリケーションが Kudu によってどのようにデプロイされるのか調べた

Azure 界の抱かれたい男 No.1 こと @kosmosebi に「ASP.NET 5 を Websites で動かす方法を調べやがれ」と言われたので、Azure 界で生きていくために頑張って調べました。

No.1 とムッシュに逆らうと生きていけないのがこの業界の鉄則です。

1.0.0-beta1 がデフォルト

ASP.NET 5 への対応が行われたコミットを見つけたので、この差分を見るのが手っ取り早いと思いました。

Adding support for ASP.NET 5 projects in Kudu · projectkudu/kudu@d60eab7 · GitHub

No.1 が詰まっていた原因は 1.0.0-beta2 のランタイムを明示的に指定する必要があったという話でしたが、このコミットは 2 ヶ月以上前なので当然ながらデフォルトの KRE バージョンは 1.0.0-beta1 です。

ASP.NET 5 (CTP5) のプロジェクトを Azure Websites で動かす | ブチザッキ

とりあえずこれで動いたので良かったとなればいいんですが、そう甘くないのがこの業界。

Kudu のデプロイスクリプト

基本的に Git などの外部リポジトリ連携でデプロイした場合には、Kudu が自動的にプロジェクトの種類を判別し、デプロイスクリプトを生成して実際にデプロイを行います。難しいことないです。

ログに使われたスクリプトのパスが残るので、ASP.NET 5 に関係する部分だけを引っ張ってきました。

echo Handling ASP.NET 5 Web Application deployment.

:: Remove wwwroot if deploying to default location
IF "%DEPLOYMENT_TARGET%" == "%WEBROOT_PATH%" (
    FOR /F %%i IN ("%DEPLOYMENT_TARGET%") DO IF "%%~nxi"=="wwwroot" (
    SET DEPLOYMENT_TARGET=%%~dpi
    )
)

:: Remove trailing slash if present
IF "%DEPLOYMENT_TARGET:~-1%"=="\" (
    SET DEPLOYMENT_TARGET=%DEPLOYMENT_TARGET:~0,-1%
)

:: 1. Install KRE
call :ExecuteCmd PowerShell -NoProfile -NoLogo -ExecutionPolicy unrestricted -Command "[System.Threading.Thread]::CurrentThread.CurrentCulture = ''; [System.Threading.Thread]::CurrentThread.CurrentUICulture = '';& '%SCM_KVM_PS_PATH%' %*" install %SCM_KRE_VERSION% -%SCM_KRE_ARCH% -runtime %SCM_KRE_CLR% %SCM_KVM_INSTALL_OPTIONS%
IF !ERRORLEVEL! NEQ 0 goto error

IF EXIST "%USERPROFILE%\.kre\run-once.cmd" (
    CALL "%USERPROFILE%\.kre\run-once.cmd"
    DEL "%USERPROFILE%\.kre\run-once.cmd"
)

:: 2. Run KPM Restore
call kpm restore "%DEPLOYMENT_SOURCE%" %SCM_KPM_RESTORE_OPTIONS%
IF !ERRORLEVEL! NEQ 0 goto error

:: 3. Run KPM Pack
call kpm pack "D:\home\site\repository\src\WebApplication3\project.json" --runtime "%USERPROFILE%\.kre\packages\KRE-%SCM_KRE_CLR%-%SCM_KRE_ARCH%.%SCM_KRE_VERSION%" --out "%DEPLOYMENT_TEMP%" %SCM_KPM_PACK_OPTIONS%
IF !ERRORLEVEL! NEQ 0 goto error

:: 4. KuduSync
call %KUDU_SYNC_CMD% -v 50 -f "%DEPLOYMENT_TEMP%" -t "%DEPLOYMENT_TARGET%" -n "%NEXT_MANIFEST_PATH%" -p "%PREVIOUS_MANIFEST_PATH%" -i ".git;.hg;.deployment;deploy.cmd"
IF !ERRORLEVEL! NEQ 0 goto error
)

やっていることは結構シンプルで以下のような手順になってます。

  1. KRE をインストール
    • 初回は run-once.cmd も実行
  2. kpm restore を実行
    • NuGet / npm から必要なパッケージを復元
  3. kpm pack を実行し、結果をテンポラリに書きだす
  4. kuduSync でテンポラリから wwwroot にコピー

このデプロイスクリプトを作っているのは KuduScript というサブプロジェクトなので、そっちのリポジトリを見るとテンプレート化されたスクリプトを発見できます。

projectkudu/KuduScript: Kudu's deployment s... - GitHub

Kudu の他に xplat-cli でもこれが使われている感じです。

kpm pack について

デプロイスクリプトの中で割と謎だった kpm pack については GitHub Wiki に説明が書いてありました。

Home · aspnet/Home Wiki · GitHub

どうやら kpm pack を実行すると、アプリケーションの実行に必要なパッケージなどを全て含んだ形にしてくれるようです。実際に Visual Studio 2015 で発行した時にも、事前に kpm pack が実行されていました。

f:id:shiba-yan:20150124142156p:plain

kpm pack 後のファイルを Visual Studio ではデプロイしていたため、Git を使った時のようにバージョンの問題が発生しないという話でした。

ちなみに kpm pack した結果、アプリケーションはポータブルな形になるため、開発マシンから別のマシンにコピーしてコマンドで起動することが出来ます。

f:id:shiba-yan:20150124142548p:plain

CoreCLR を選んだ場合は .NET Framework のバージョンにも依存しなくなるはずです。

Azure Search がひっそりとアップデートされていた件

Azure Search がアップデートされて日本語などに対応した話 - しばやん雑記 で Azure Search について書きましたが、その後にひっそりとアップデートが行われていたのでまとめました。

API が追加された気がしたり、新ポータルでの使い勝手が向上しています。

リージョンに東アジアが追加

f:id:shiba-yan:20150113215052p:plain

これまでは東南アジアが一番近かったのですが、東アジアが追加されていました。去年の今頃までは東アジアしかなかったので、レイテンシ的に使えないということは無さそう。

Azure 界の抱かれたい男 No.1 が転職して開発するというシナリオが求められている。

スコアリングプロファイルの作成

f:id:shiba-yan:20150113220051p:plain

以前調べた時には無かった気がしましたが、スコアリングプロファイルを使うことで、現在地に近い店を上位に持ってくるといったことが出来るようになるみたいですね。MSDN にサンプルがありました。

Add scoring profiles to a search index (Azure Search API)

試す時間が無いので、また今度遊んでみたいと思います。

CORS 設定

f:id:shiba-yan:20150113220150p:plain

JavaScript のクライアントは公開されていませんが、CORS の設定を行えるようになったので、クエリキーと XHR を使ってクライアントサイドでの検索処理の実装も行えるようになりました。

SQL Database / DocumentDB 用 Indexer の追加

SQL Database や DocumentDB に格納したデータに対して、簡単に Azure Search でインデックスを作成できるようになったみたいです。

Indexer Operations (Azure Search Service REST API: 2014-10-20-Preview)

REST API で接続文字列を投げると、勝手に型をマッピングしてインデックスを作ってくれるようです。SQL Database と DocumentDB なのでムッシュとおーみさんに任せたいですね。

httpPlatformHandler をもっと面白く使っていきたい

Azure Web サイトが Java に対応した時に同時に追加された httpPlatformHandler ですが、何か面白い使い方があると思うので軽くまとめておきます。

まずはちょっと似てる ARR と httpPlatformHandler の違いから簡単に。

ARR の場合

ARR の役割はリバースプロキシとロードバランサーになるので、基本的には HTTP トラフィックをそのまま別の URL やサーバー群に振り分けます。

Application Request Routing : The Official Microsoft IIS Site

IIS に ARR をインストールすることで、nginx と同じような機能を実現することが出来ます。

httpPlatformHandler の場合

httpPlatformHandler の役割はローカルホストでサーバーを起動し、それに対して HTTP トラフィックを流し込むことです。ARR では柔軟に設定が行えましたが、httpPlatformHandler ではローカルホストで固定です。

説明用に某セミナー用に作った画像を引っ張り出してきました。

f:id:shiba-yan:20141221183536p:plain

ARR と決定的に異なるのが、httpPlatformHandler は指定された実行ファイルのプロセス管理までを行う点です。初回リクエストのタイミングで、httpPlatformHandler は Web.config で指定されているプロセスを起動し、同時にリッスンすべきポート番号を渡します。

この時 HTTP_PLATFORM_PORT 環境変数にポート番号が入っているので、起動されたプロセスではこのポートで接続を待機します。*1

プロセス管理を行うことから FastCGI っぽくも感じますが、通信自体は HTTP そのままです。

出来ること

上の図でも書いてありますが、Java のアプリケーションサーバーや Rack を使ったアプリケーションを、IIS を意識させることなく動作させることが出来ます。


既に Ruby や Go に関しては httpPlatformHandler を使うことで、CGI / FastCGI よりも実用的なパフォーマンスを出せることを確認しています。

あまり意味はないですが、Node.js に関しても iisnode を使うことなく httpPlatformHandler でホスティングが可能です。実際に例を挙げると以下のような Web.config を用意することで、起動するようになります。

<httpPlatform processPath="node.exe d:\home\site\wwwroot\hello.js" arguments="">
  <environmentVariables>
    <environmentVariable name="PORT" value="%HTTP_PLATFORM_PORT%" />
  </environmentVariables>
</httpPlatform>

環境変数 PORT を設定することで、process.env.PORT で実行時のポート番号を取れるようにできます。

出来ないこと

ARR を使った場合とは異なり、httpPlatformHandler はポートが 1 つで固定なため SSL なトラフィックは全てオフロードされて渡されます。つまり、SPDY とか HTTP/2 を流し込むといったことは出来ません。

あと、FastCGI や iisnode を使った場合とは異なり、常に 1 つのプロセスしか起動されないため、それぞれのプロセスに負荷分散を行うということも出来ません。負荷分散を行う場合には、さらにその下にプロセスを管理するレイヤーが必要になるでしょう。

おまけ : httpPlatform 設定について

元々は Java 向けに用意されたとあって、ドキュメントは基本的に Java 向けに書かれています。

Upload a custom Java website to Azure

Web.config で必要なテンプレート的な設定は以下のような感じです。

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
  <system.webServer>
    <handlers>
      <add name="httpPlatformHandler" path="*" verb="*" modules="httpPlatformHandler" resourceType="Unspecified" />
    </handlers>
    <httpPlatform processPath="startup.bat" arguments="">
      <environmentVariables>
        <environmentVariable name="PORT" value="%HTTP_PLATFORM_PORT%" />
      </environmentVariables>
    </httpPlatform>
  </system.webServer>
</configuration>

基本的には全てのパス、HTTP メソッドに対して有効なハンドラーを追加し、httpPlatform 設定で起動するプロセスのパスを追加する形になります。

設定可能な項目

  • processPath
    • 実際に起動される実行ファイル
  • arguments
    • 実行ファイルに渡されるコマンドライン引数
  • startupTimeLimit
    • アプリケーションが起動完了するまでの最大時間
  • startupRetryCount
    • 起動に失敗した時にやり直す回数
  • requestTimeout
    • HTTP リクエストのタイムアウト時間
  • rapidFailsPerMinute
    • ラピッドフェール保護が動くまでの 1 分間の失敗回数
  • stdoutLogEnabled
    • 標準出力、標準エラー出力に書きだされた内容をログとして保存するか
  • stdoutLogFile
    • ログファイルのパス
  • environmentVariables
    • 子プロセス起動時に追加で渡す環境変数

渡される環境変数

  • HTTP_PLATFORM_PORT
    • 実際の HTTP リクエストが流し込まれるポート番号
  • HTTP_PLATFORM_DEBUG_PORT
    • Java でリモートデバッグを行うための特殊なポート番号
    • Web.config 内で利用した場合のみ開かれるらしい*2

*1:それ以外のポートは Web サイトの制限で開くことは出来ません。

*2:https://github.com/Azure/azure-websites-java-remote-debugging

Azure Web サイトのファイルストレージに関して性能面で追加の検証をしてみた

Azure Web サイトのファイルストレージは想像以上に遅い - しばやん雑記 で帝国兵さんから細かいトランザクションに弱いと教えてもらったので、読み込みでどのくらい影響が出てくるのか調べました。

FSRM を使ったクオーター管理なら、書き込みと削除では大きな影響が出ても、読み込みはそこまでの差は出ないのではないかと考えていました。もうちょっと詳しく調べておかないと、Web サイトを使うときに考慮する必要が出てきそうです。

なのでサクッと適当に書き込み、読み込み、削除を繰り返すだけのコードを書きました。無駄に引数でファイル数と書き込むバイト数を指定出来るようにもしてます。

それでは前回と同じように各ストレージ領域で確認していきます。

永続化ストレージ (C:\)

C:\DWASFiles\Sites\#1shibayan\Temp>StorageBenchmark.exe 1000 0
WriteFile : 662ms
ReadFile : 179ms
DeleteFile : 300ms

C:\DWASFiles\Sites\#1shibayan\Temp>StorageBenchmark.exe 1000 512
WriteFile : 1114ms
ReadFile : 166ms
DeleteFile : 243ms

ローカルドライブ (D:\)

D:\Users\shibayan-api\Documents>StorageBenchmark.exe 1000 0
WriteFile : 785ms
ReadFile : 177ms
DeleteFile : 668ms

D:\Users\shibayan-api\Documents>StorageBenchmark.exe 1000 512
WriteFile : 1680ms
ReadFile : 169ms
DeleteFile : 600ms

Azure Web サイト共有ストレージ (D:\home)

D:\home\site\temp>StorageBenchmark.exe 1000 0
WriteFile : 2666ms
ReadFile : 2122ms
DeleteFile : 15965ms

D:\home\site\temp>StorageBenchmark.exe 1000 512
WriteFile : 6379ms
ReadFile : 2925ms
DeleteFile : 15475ms

正直、そこまでは影響はしないだろうと思っていた読み込みも、確認してみると 10 倍ぐらい遅いことが分かりました。アプリケーション起動時に読み込まれるファイルが多い場合には、Always On を有効にすると多少の改善が見込めますが、やはり多数の静的なコンテンツは Blob や CDN を使って配信すべきでしょうね。

この辺りは GoAzure 2015 のセッションで触れようかと思ってます。

Microsoft Azure のイベント |GoAzure 2015 ~Microsoft Azure 史上最大のコミュニティイベント~

あと、計測中に共有ストレージでは測定値が割と不安定だったので、ファイルサーバーをいろんなユーザーと共有している点だけ頭に入れておく必要がありそうです。

おまけ:MacBook Pro の SSD

C:\Temp>StorageBenchmark.exe 1000 0
WriteFile : 268ms
ReadFile : 64ms
DeleteFile : 226ms

C:\Temp>StorageBenchmark.exe 1000 512
WriteFile : 591ms
ReadFile : 41ms
DeleteFile : 204ms

当然ながらぶっちぎりで速いです。

Web サイトでも Premium Storage を使えるようになれば、さらに楽しめそうです。

Azure Web サイトのファイルストレージは想像以上に遅い

これは Azure Advent Calendar 2014 - Qiita の 24 日目の記事です。いつも通り Azure Web サイトネタで。

前から Azure Web サイトのファイルストレージはかなり遅いのではないかと思っていたので、アドベントカレンダーで良い機会なので簡単に調べてみました。特に最近は Ruby のランタイムをインストールしているので、同じような処理をするシェルスクリプトを書きました。

export PATH="$PATH:/d/7zip"

# ファイルのダウンロード
SECONDS=0

curl -LOs http://dl.bintray.com/oneclick/rubyinstaller/ruby-1.9.3-p551-i386-mingw32.7z

echo "curl : $SECONDS sec"

# 圧縮ファイルの展開
SECONDS=0

7za x ruby-1.9.3-p551-i386-mingw32.7z > nul

echo "7za x : $SECONDS sec"

rm -f ruby-1.9.3-p551-i386-mingw32.7z

# 多数のファイルの削除
SECONDS=0

rm -rf ruby-1.9.3-p551-i386-mingw32

echo "rm -rf : $SECONDS sec"

ファイルのダウンロードに関しては回線状況によって左右されるので、多少のバラつきが出ることは覚悟してます。一番確認しておきたいのは展開と削除の処理時間です。

それではスクリプトが用意できたので、実際に Azure Web サイト上でパスを変えて実行してみます。

まずは揮発性だと思われるシステムドライブ上で実行した結果です。ユーザープロファイルを読み込むように設定しておけば、現在のユーザー用に書き込み可能なディレクトリが用意されます。

D:\Users\shibayan-api\Documents>bash benchmark.sh
curl : 10 sec
7za x : 15 sec
rm -rf : 4 sec

揮発性なディスクは仮想マシンに物理的に接続されているドライブになるので、かなり高速ですね。

次は永続化されていると思われるローカルディスク上で実行した結果です。

C:\DWASFiles\Sites\#1shibayan\Temp>bash benchmark.sh
curl : 12 sec
7za x : 7 sec
rm -rf : 5 sec

永続化されているディスクの方がシステムのディスクより早いのは、恐らくキャッシュが効いているのでしょう。テンポラリとして特にパフォーマンスを心配することなく使えそうです。

最後に Azure Web サイト間で共有されているストレージ上で実行した結果です。

D:\home\temp>bash benchmark.sh
curl : 11 sec
7za x : 44 sec
rm -rf : 87 sec

アーキテクチャ的にパフォーマンスが悪いのは分かってはいましたが、正直ここまで差が出るとは思っていませんでした。単一ファイルへの書き込みは他とほぼ同じですが、沢山のファイルに対しての操作は最大で 20 倍ぐらい遅いときがあります。

これまでに Ruby on Rails のアプリケーションや Movable Type を Azure Web サイトで動かしてきた時、どうしても初回のパフォーマンスが改善しなかったのですが、単純に実行時に多くのファイルを読み込む必要のある LL に対して、Azure Web サイトは弱いという話でした。

考えてみると、ASP.NET アプリは基本的に dll という一つのファイルにまとめられるので、ストレージのパフォーマンスの影響を受けにくかったのでしょう。ASP.NET 5 で cs ファイルをそのまま置く時代になれば、めんどくさいことになりそうです。

追記

Twitter で帝国兵さんから重要な補足情報を頂きました。

ちなみに FSRM と言うのはファイル サーバー リソース マネージャ*1のことです。サイトごとの使用量を管理するために使っているのだと思われます。

細かいトランザクションに弱いのは、Blob をファイルサーバー経由で SMB マウントしてるからオーバーヘッドが大きいのが原因じゃないかと思っていましたが、ちょっと違っていたようです。

Azure Web サイトで楕円曲線暗号(ECC)を使った SSL 証明書を試してみた

Azure Web サイトで SHA-256 の証明書が使えるのか確認した - しばやん雑記 は Azure 界の抱かれたい男 No.1 こと @kosmosebi に脅されて書きましたが、そういえば Azure Web サイトは楕円曲線暗号を使った SSL 証明書にも対応していたはずなので、自己署名証明書を作成して試してみました。

Next Generation Cryptography now available with Azure Web Sites | Microsoft Azure Blog

ちなみに Azure Blog では ECC に対応と記述されてますが、試したことある人は少ない気がします。

自己署名証明書の作成

基本的には Azure Web サイトのドキュメントに書いてあるような流れになります。serverauth.cnf だけはドキュメント通りに作成しておきます。

Enable HTTPS for an Azure website - .NET Dev Center

せっかくなので署名アルゴリズムとして SHA-256 を使うために、default_keyfile の下あたりに default_md = sha256 を追加しておきました。

作成が終わったら openssl コマンドを使って秘密鍵を作成し、serverauth.cnf の設定にしたがって server.crt を作成していきます。ひょっとしたら秘密鍵を一緒に作成する方法があるのかもしれないですが、わからなかったので別々に作成します。

openssl ecparam -name prime256v1 -genkey -out private.key
openssl req -new -config serverauth.cnf -x509 -key private.key -out server.crt -days 365

server.crt の作成中にはコモンネームなどを聞かれるので、間違えないように入力します。

作成した private.key と server.crt を使って PFX 形式に変換するには、以下のようなコマンドを使います。

openssl pkcs12 -export -out server.pfx -inkey private.key -in server.crt

途中でパスワードを聞かれるので、間違えないように入力します。これで server.pfx が作成されます。

あとは管理ポータルから前回と同じように設定するだけなので省略します。

証明書の確認

openssl を使うために OS X で作業していたので、OS X 上の Google Chrome で確認してみました。

f:id:shiba-yan:20141217234017p:plain

自己署名証明書ですが、ちゃんとアルゴリズムが楕円曲線公開鍵になっています。署名アルゴリズムも「SHA-256 を使用する ECDSA 署名」になっているので、楕円曲線暗号が使われていることがわかります。

Windows 上の Internet Explorer 11 でも確認してみました。

f:id:shiba-yan:20141217235944p:plain

公開キーが ECC の 256bits になっていることが確認できます。

正直まだまだ使われることは少ないと思いますが、新しい暗号化形式に対応しているのは安心感があります。

Azure Web サイトで SHA-256 の証明書が使えるのか確認した

Twitter で Azure 界の抱かれたい男こと @kosmosebi から「Azure Web サイトで SHA-256 な SSL 証明書が使えるのか確認しろ」と脅されたので、震えながら確認しました。

結論としては問題なく SHA-256 な証明書が使えました。これからは確認した手順を書きます。

自己署名証明書の作成

最初は OS X の openssl を使って pfx を作ったところ、SSL バインド設定時にエラーになってしまったので諦めて Windows に戻って自己署名証明書を作りました。

以下の記事がとても参考になったのでありがたいです。と言うかほぼ丸パクリです。

Create a Self-Signed Certificate for Windows Azure Website | Monkey Weekend

記事から変更した点は sha1 を sha256 にしたぐらいです。

makecert -r -pe -n CN="shibayan.jp" -b 01/01/2010 -e 12/31/2019 -eku 1.3.6.1.5.5.7.3.1 -sky exchange -sp "Microsoft Enhanced RSA and AES Cryptographic Provider" -sy 24 -a sha256 -len 2048 -sv shibayan.jp.pvk shibayan.jp.cer

pvk2pfx -pvk shibayan.jp.pvk -spc shibayan.jp.cer -pfx shibayan.jp.pfx -po PassPhrase

このコマンドを実行すると shibayan.jp.pfx が生成されるので、後は管理ポータルから設定します。

Azure Web サイトへ設定

特に変わった点もなく、まずは証明書をアップロードします。エラーなどは表示されず、成功しました。

f:id:shiba-yan:20141217140650p:plain

後は SSL バインドの設定からドメインと証明書を関連付けるだけです。今回は SNI SSL で設定しました。

f:id:shiba-yan:20141217140719p:plain

これで保存時にエラーが出なければ設定完了です。

証明書の確認

後は実際にブラウザからアクセスして、証明書が本当に SHA-256 になっているのか確認します。

f:id:shiba-yan:20141217140614p:plain

署名ハッシュアルゴリズムが sha256 になっていることが確認出来ました。Azure Web サイトは SHA-256 な SSL 証明書に対応していることが分かったので、安心して使えますね。

Azure Web サイトの Custom Deployment Scripts を利用して PHP アプリケーション向けに Composer を自動実行する

f:id:shiba-yan:20141212012421p:plain

https://getcomposer.org/

Azure Web サイトに Ruby をインストールするデプロイスクリプトの謎挙動に悩まされっぱなしで疲れたので、少し気分を変えて PHP の Composer をデプロイ時に自動実行するスクリプトを作ってみました。

と言っても、基本的には Ruby の時と考え方は同じで、PHP 向けのデプロイスクリプトを生成して、それに php composer.phar install の実行を追加するだけです。

IF EXIST "%DEPLOYMENT_TARGET%\composer.json" (
  cd %DEPLOYMENT_TARGET%

  IF NOT EXIST "%DEPLOYMENT_TARGET%\composer.phar" (
    call curl -s http://getcomposer.org/installer | php > nul
    IF !ERRORLEVEL! NEQ 0 goto error
  )

  call php composer.phar install
  IF !ERRORLEVEL! NEQ 0 goto error
)

特に説明する必要すら感じないですが、wwwroot 以下に composer.json が存在している場合に composer.phar install を実行します。Composer 自体がインストールされていない場合は、サクッと自動でインストールするようになってます。

実際に Azure Web サイトの Git にデプロイした時の出力が以下のような感じ。

f:id:shiba-yan:20141212011535p:plain

パッケージのインストールが正常に終わっていることが確認できます。

一応 wwwroot 以下も確認しておくと、vendor ディレクトリが生成されており、その中には monolog や autoloader の設定が生成されていました。

f:id:shiba-yan:20141212012151p:plain

PHP は Azure Web サイトに最初からインストールされているので、挙動が安定している感じがあります。

今回作成した deploy.cmd を Gist に上げておきました。どうぞご自由にお使いください。

Custom Deployment Script for PHP composer

気分が良ければ適当にリポジトリをでっちあげるかもしれません。

Azure Web サイトの Custom Deployment Scripts がどのように実装されているのか調べた

Azure Web サイトに用意されている .deployment ファイルを使った Custom Deployment Scripts がどのように実装されているのか気になったのと、Ruby 周りが行き詰っていたので調べてみました。

結論から書いてしまうと、単純に Git に用意されているサーバーサイドフックを使って実現されていました。

サーバーサイドフック

クライアントサイドフックの他に、いくつかのサーバーサイドフックを使うこともできます。これは、システム管理者がプロジェクトのポリシーを強制させるために使うものです。これらのスクリプトは、サーバへのプッシュの前後に実行されます。pre フックをゼロ以外の値で終了させると、プッシュを却下してエラーメッセージをクライアントに返すことができます。つまり、プッシュに関するポリシーをここで設定することができるということです。

Git - Git フック

以上、終わり。

だと流石に中身が無いので、実際にどのようなに実行されているのか調べました。

デプロイ時の処理の流れ

Kudu は git push されると内部で git.exe を使ってコミットを取得します。思っているよりもいろいろと複雑なことをしてますが、とりあえず Process Explorer から実行時の引数を拾ってきました。

f:id:shiba-yan:20141211162555p:plain

この時に git が処理完了のタイミングで hooks/post-receive フックを起動します。

f:id:shiba-yan:20141211162501p:plain

sh.exe を使ってフックの処理を実行します。

そして post-receive フックでは何をやっているかですが、基本的には kudu.exe をさらに実行します。

#!/bin/sh
read i
echo $i > pushinfo
"$KUDU_EXE" "$KUDU_APPPATH" "$KUDU_MSBUILD" "$KUDU_DEPLOYER"

kudu.exe は実際に Git リポジトリから wwwroot へのデプロイを行うためのアプリケーションで、ASP.NET アプリケーションの場合には MSBuild を起動してコンパイルを行ったりしています。

f:id:shiba-yan:20141211162440p:plain

その kudu.exe が内部で .deployment を読み込んで、実際のデプロイスクリプトの実行を行っています。ちなみに ASP.NET や Node.js アプリケーションだけを Git でデプロイした場合にも、内部的にはそれぞれのプラットフォーム向けにデプロイスクリプトが作成されています。

そしてデプロイスクリプトは starter.cmd というバッチファイル経由で実際に実行されます。

@%*

starter.cmd の中身は、引数に渡されたコマンドを実行するだけのシンプルなものです。

f:id:shiba-yan:20141211162450p:plain

.deployment で bash deploy.sh に書くと、cmd.exe の下に bash.exe が作られることになります。

そんなこんなの流れによって、プロセスのツリーが大変なことになる訳です。

f:id:shiba-yan:20141211170912p:plain

ちなみに管理ポータルから再デプロイを実行した場合には git 周りが大幅にスキップされるのと、Kudu の管理下で実行されるので starter.cmd からになります。

Azure Web サイトのリモートデバッグを使って SCM 側にデバッガーをアタッチする

Azure Web サイトへのリモートデバッグは Visual Studio 2013 Update 4 とか Azure SDK 2.5 を入れている場合には、サーバーエクスプローラーから右クリックするだけの簡単操作で行えます。

f:id:shiba-yan:20141210202625p:plain

今のところサーバーエクスプローラーからのリモートデバッグだと、公開側の w3wp.exe へのアタッチしか出来ません。ASP.NET アプリケーションをデプロイしてデバッグする以上、そういった仕様になっています。

しかし、世の中には SCM 側で動いている任意のプロセスにデバッガーをアタッチしたいことも度々出てくると思います。例えば Kudu 本体のデバッグを行いたい場合とかです。

f:id:shiba-yan:20141210200134p:plain

実際にはリモートデバッグを有効にした場合、SCM 側の w3wp.exe にもリモートデバッグモニターがぶら下がるので、サーバーエクスプローラーを使わなければ簡単にアタッチできます。

とりあえず対象の Web サイトのリモートデバッグを有効にします。

f:id:shiba-yan:20141210202243p:plain

最近は Visual Studio で設定変更が出来るのでとても楽ですね。

リモートデバッグを有効にしたら「ツール」から「プロセスにアタッチ」を選択し、修飾子に {sitename}.scm.azurewebsites.net:4018 を入力して資格情報を入力します。

f:id:shiba-yan:20141210204601p:plain

この場合は kinmugi-ruby-1.scm.azurewebsites.net:4018 を指定しました。資格情報はサイトレベルとユーザーレベルのどちらでも大丈夫です。

資格情報に問題が無ければ、アタッチ可能なプロセス一覧が表示されます。

f:id:shiba-yan:20141210205525p:plain

今は公開側の w3wp.exe が動いていないので 1 つしか見えていないですが、複数ある場合には Kudu の Process Explorer でプロセス ID を調べます。

Kudu 本体にアタッチしたいので w3wp.exe を選択してアタッチしました。

f:id:shiba-yan:20141210210132p:plain

デバッグウィンドウに Kudu 関連のアセンブリが読み込まれているログが出力されています。

基本的に標準でインストールされている Kudu には pdb などが含まれていないので、実際に本格的なデバッグを行う場合には GitHub から Kudu のソースコードを落としてきて、プライベートな Kudu としてデプロイした方が良い気がしました。