はじめに
2020年10月現在、Azure Stack Hub 上の App Service は、ASP.NET (.NET Framework / Core), PHP, Java, クラシックASP に対応しています。
参考:App Service on Azure Stack Hub 2020 Q3 のリリース ノート
App Service 自体は Microsoft が Azure Stack Hub のリソースプロバイダとして提供する「ソフトウェア」です。App Service を動かす IaaS レイヤーの管理は、Azure Stack Hub Operator の役割です。折角そこを管理している(管理する必要がある)のだから、好きなプラットフォームを追加して、おれおれ App Service を作りたくなるのが人情というものです。
環境
この記事は以下の環境でのものです。
- Azure Stack Hub 1910
- App Service Update 7 (84.0.2.10)
これと異なる環境では同じようにならない可能性が、大いにあります。
App Service の IaaS レイヤー
App Service の裏側は以下のようなリソースで構成されています。
https://blog.aimless.jp/archives/2019-03-05-install-appservice-resource-provider-to-azurestack より引用(Azure Stack Hub 上の App Service 構築については、こちらが大変参考になります)
このうち、Worker Tier が実際に Web アプリケーションをホストする役割を担っており、仮想マシンスケールセット (VMSS) で構成されています。
通常の Worker Tier 作成
Worker Tier の作成自体は、管理者用の Web ポータルから普通に出来ます。
ただし、使用するイメージは「2016-Datacenter」以外には選択出来ません。
この時点で真っ当なやり方では難しそうです。
しかしながら、Worker Tier の実体が VMSS である事が判っています。VMSS は任意の仮想マシンイメージから作成する事が出来る事から、この VMSS を弄ってみます。
手順
一旦「2016-Datacenter」で Worker Tier を作成してから、VMSS のイメージを変更します。
手順としては以下のようになります。
- カスタムイメージの作成
- 「2016-Datacenter」で Worker Tier を作成
- WorkerTierVmss のイメージを変更
- VMSS インスタンスの追加と PricingTier の作成
カスタムイメージの作成
Azure Stack Hub 管理者として、2016-Datacenter で仮想マシンを作成します。
通常のカスタムイメージ作成手順と変わりありません。イメージを登録する際に OS ディスクの Blob URI が必要なので、マネージドディスクを使わない方が楽だと思います。
参考:Azure Stack Hub に対してカスタム VM イメージを追加または削除する
イメージを登録する際は以下を指定します。
項目 | 値 |
---|---|
Publisher | MicrosoftWindowsServer |
Offer | WindowsServer |
Sku | 任意 |
Version | 任意 |
Blob URI | カスタムイメージ用 OS ディスクのURL |
注意すべきは Publisher と Offer です。
VMSS を更新する際にはこれらを変更する事は出来ないので、「2016-Datacenter」と同じものにする必要があります。
また、仮想ディスクが格納されている Blob コンテナを読取アクセス可能にしておく事も忘れないでください。
ステータスが「Succeeded」になればイメージは完成です。
プラットフォームやミドルウェアには、今回は以下をインストールしておきます。この辺のチョイスはただの趣味です。
- ASP.NET 5
- Go
- Oracle Data Provider for .NET 11.2.0.3
Worker Tier の作成
管理者ポータルから「2016-Datacenter」の Worker Tier を作成します。
App Service のリソースグループ内には VMSS が作られている事も確認できます。
VMSS の変更
管理者用の Powershell モジュールを利用して、VMSS を変更します。
$vmss = Get-AzureRmVmss -ResourceGroupName appservice.local -VMScaleSetName CustomWorkerTierScaleSet
$vmss.VirtualMachineProfile.StorageProfile.ImageReference.Sku = "2016-Custom"
$vmss.VirtualMachineProfile.StorageProfile.ImageReference.Version = "0.0.1"
Update-AzureRmVmss -ResourceGroupName appservice.local -VMScaleSetName CustomWorkerTierScaleSet -VirtualMachineScaleSet $vmss
OS イメージの Sku に上で作成したカスタムイメージ(必要に応じて Version も)を指定して、Update-AzureRmVmss
で更新します。
更新が終了すると、App Service の Worker Tier ブレードでも image が変更されている事が確認できます。
VMSS インスタンスの追加と PricingTier の作成
あとは普通に VMSS のインスタンスと App Service の Sku を作成して、ユーザーテナントで利用出来るようにします。
カスタム Worker Tier を指定して Sku を作成。
Sku を作成すると Pricing Tier が作られます。
これで準備が整いました。
Web App の作成
ユーザーテナントで Web App を作成します。
App Service Plan を作成する際にカスタムワーカーの Pricing Tier を選択します。
カスタムワーカーの App Service Plan と Web App が作成されます。
動作確認
ASP.NET 5
ASP.NET MVC アプリをデプロイして実行します。
Environment.Version
と Environment.MachineName
の値を表示してみます。
@{
ViewData["Title"] = "Home Page";
}
<div class="text-center">
<h1 class="display-4">Welcome</h1>
<p>Learn about <a href="https://docs.microsoft.com/aspnet/core">building Web apps with ASP.NET Core</a>.</p>
<div>
.NET Version: @Environment.Version
</div>
<div>
Machine Name: @Environment.MachineName
</div>
</div>
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
</PropertyGroup>
</Project>
.NET 5 のランタイムで動作している事が分かります。
ちなみに、通常の Web App にデプロイするとちゃんとこけます。
Go
以下の記事で紹介されているコードをそのままお借りして動かします。
Azure Web サイトで Go 言語を httpPlatformHandler を使って動かしてみた - しばやん雑記
package main
import (
"fmt"
"net/http"
"os"
"runtime"
)
func viewHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, World, " + runtime.Version())
}
func main() {
http.HandleFunc("/", viewHandler)
http.ListenAndServe(":" + os.Getenv("HTTP_PLATFORM_PORT"), nil)
}
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.webServer>
<handlers>
<add name="httpPlatformHandlerMain" modules="httpPlatformHandler" path="*" verb="*" resourceType="Unspecified" />
</handlers>
<httpPlatform processPath="C:\Go\bin\go.exe" arguments="run C:\home\site\wwwroot\server.go" startupTimeLimit="60" startupRetryCount="5" />
</system.webServer>
</configuration>
動きました!
ちなみに、HttpPlatformHandler は Worker Tier 構築時にインストールされます。
.NET Framework 2.0 + ODP.NET アンマネージドドライバー
つまりレガシーなアプリケーションです。
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="WebForm1.aspx.cs" Inherits="dotnetfw2ora.WebForm1" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
Runtime Version: <asp:Label runat="server" ID="lblRuntimeVersion" />
</div>
<div>
Oracle Client: <asp:Label runat="server" ID="lblOracleVersion" />
</div>
<div>
<asp:GridView runat="server" ID="grdData" />
</div>
</form>
</body>
</html>
protected void Page_Load(object sender, EventArgs e)
{
lblRuntimeVersion.Text = Assembly.GetExecutingAssembly().ImageRuntimeVersion;
var connStr = ConfigurationManager.AppSettings["ConnectionString"];
var query = "select * from all_tables where owner='SYS'";
var conn = new OracleConnection(connStr);
var adapter = new OracleDataAdapter(query, conn);
var ds = new DataSet();
adapter.Fill(ds);
lblOracleVersion.Text = Assembly.GetAssembly(conn.GetType()).FullName;
grdData.DataSource = ds;
grdData.DataMember = ds.Tables[0].TableName;
this.DataBind();
}
ODP.NET のランタイムが読み込まれ、Oracle データベースでのクエリの実行にも成功しました。
カスタム Worker Tier 削除時の注意
カスタム Worker Tier を削除する場合は、VMSS イメージを「2016-Datacenter」に戻してから削除します。こうしないと VMSS だけが削除され、Worker Tier は残ったままになります。
詳しくはこちらを。
おわりに
かなり強引ではありますが、独自のプラットフォームに対応させた Web App を作る事に成功しました。
最新のプラットフォームでの開発環境などには使えるのではないでしょうか。
また、PaaS 化など望めないようなレガシーな Web アプリケーションであっても、アプリ自体に手を入れる事なく、スケーリングやデプロイスロットといった Web App の恩恵を受ける事が出来るのは大きなメリットだと思います。
まともな方法でサポートされる事を期待したいです。