注
これは、この記事の最新バージョンではありません。 現在のリリースについては、この記事の .NET 9 バージョンを参照してください。
Warnung
このバージョンの ASP.NET Core はサポート対象から除外されました。 詳細については、 .NET および .NET Core サポート ポリシーを参照してください。 現在のリリースについては、この記事の .NET 9 バージョンを参照してください。
Von Bedeutung
この情報はリリース前の製品に関する事項であり、正式版がリリースされるまでに大幅に変更される可能性があります。 Microsoft は、ここで提供される情報に関して明示的または黙示的な保証を行いません。
現在のリリースについては、この記事の .NET 9 バージョンを参照してください。
この記事では、ASP.NET Core を使用して、サーバー側の Blazor アプリ (Blazor Web App および Blazor Server アプリ) をホストおよび展開する方法について説明します。
ホストの構成値
サーバー側の Blazor アプリでは、 汎用ホスト構成値を受け取ることができます。
デプロイメント
サーバー側ホスティング モデルを使用すると、 Blazor は、ASP.NET Core アプリ内からサーバー上で実行されます。 UI の更新、イベント処理、および JavaScript 呼び出しは、 SignalR 接続を介して処理されます。
ASP.NET Core アプリをホストできる Web サーバーが必要です。 Visual Studio には、サーバー側アプリ プロジェクト テンプレートが含まれています。 Blazor プロジェクト テンプレートについて詳しくは、「ASP.NET Core Blazor プロジェクトの構造」をご覧ください。
リリース構成でアプリを発行し、 bin/Release/{TARGET FRAMEWORK}/publish
フォルダーの内容を展開します。ここで、 {TARGET FRAMEWORK}
プレースホルダーはターゲット フレームワークです。
スケーラビリティ
1 台のサーバーのスケーラビリティ (スケールアップ) を検討する場合、アプリで使用できるメモリは、ユーザーの要求の増加に伴ってアプリが消費する最初のリソースである可能性があります。 サーバーで使用可能なメモリは、次の影響を受けます。
- サーバーがサポートできるアクティブ回線の数。
- クライアントでの UI 待機時間。
セキュリティで保護されたスケーラブルなサーバー側 Blazor アプリの構築に関するガイダンスについては、次のリソースを参照してください。
各回線では、 Hello World スタイルの最小アプリに約 250 KB のメモリが使用されます。 回線のサイズは、アプリのコードと各コンポーネントに関連付けられている状態メンテナンス要件によって異なります。 アプリとインフラストラクチャの開発中にリソースの需要を測定することをお勧めしますが、次のベースラインがデプロイ ターゲットの計画の開始点になる可能性があります。アプリで 5,000 人の同時ユーザーをサポートすることが予想される場合は、アプリに少なくとも 1.3 GB のサーバー メモリ (またはユーザーあたり約 273 KB) の予算を設定することを検討してください。
SignalR 構成
SignalRのホスティングとスケーリングの条件は、SignalRを使用するBlazor アプリに適用されます。
構成ガイダンスなど、Blazor アプリのSignalRの詳細については、ASP.NET Core BlazorSignalR ガイダンスを参照してください。
トランスポート
Blazorは、待機時間の短縮、信頼性の向上、セキュリティの向上により、WebSocket をSignalRトランスポートとして使用する場合に最適です。 長いポーリング は、WebSocket が使用できない場合、または長いポーリングを使用するようにアプリが明示的に構成されている場合に、 SignalR によって使用されます。
ロングポーリングが使用されている場合は、コンソールの警告が表示されます。
ロング ポーリング フォールバック トランスポートを使用して WebSocket 経由で接続できませんでした。 これは、VPN またはプロキシが接続をブロックしていることが原因である可能性があります。
グローバルデプロイと接続エラー
地理的データ センターへのグローバルデプロイの推奨事項:
- ほとんどのユーザーが存在するリージョンにアプリをデプロイします。
- 大陸全体のトラフィックの待機時間の増加を考慮してください。 再接続 UI の外観を制御するには、 ASP.NET Core BlazorSignalR ガイダンスを参照してください。
- Azure SignalR サービスの使用を検討してください。
Azure App Service
Azure App Service でのホスティングには、WebSocket とセッション アフィニティ (アプリケーション要求ルーティング (ARR) アフィニティとも呼ばれます) の構成が必要です。
注
Azure App Service の Blazor アプリでは、 Azure SignalR Service は必要ありません。
Azure App Service でのアプリの登録に対して次を有効にします。
- WebSocket トランスポートを機能させることができる WebSockets。 既定の設定は [オフ] です。
- ユーザーからの要求を同じ App Service インスタンスにルーティングするためのセッション アフィニティ。 既定の設定は [オン] です。
- Azure portal の [App Services] にある Web アプリに移動します。
- [設定]>[構成] を開きます。
- [Web ソケット] を [オン] に設定します。
- [セッション アフィニティ] が [オン] に設定されていることを確認します。
Azure SignalR サービス
任意の Azure SignalR Service は、アプリの SignalR ハブと連携して、サーバー側のアプリを大規模なコンカレント接続にスケールアップします。 さらに、サービスのグローバル リーチとハイパフォーマンスのデータ センターは、地理的条件による待機時間の短縮に役立ちます。
このサービスは、Azure App Service または Azure Container Apps でホストされている Blazor アプリには必要ありませんが、他のホスティング環境で役立つ場合があります。
- 接続規模の拡大を容易にするために。
- グローバル分散を行う。
Azure
アプリが WebSocket の代わりに長いポーリングを使用する場合、または長いポーリングにフォールバックする場合は、最大ポーリング間隔 (MaxPollIntervalInSeconds
、既定値: 5 秒、制限: 1 ~ 300 秒) を構成する必要があります。これは、Azure SignalR Service での長いポーリング接続に許可される最大ポーリング間隔を定義します。 次のポーリング要求が最大ポーリング間隔内に到着しない場合、サービスはクライアント接続を閉じます。
運用デプロイに依存関係としてサービスを追加する方法のガイダンスについては、「 ASP.NET Core SignalR アプリを Azure App Service に発行する」を参照してください。
詳細については、以下を参照してください。
- Azure SignalR サービス
- Azure SignalR Service とは
- ASP.NET Core SignalR 運用環境のホスティングとスケーリング
- Azure App Service に ASP.NET Core SignalR アプリを発行する
Azure コンテナー アプリ
Azure Container Apps サービスでのサーバー側 Blazor アプリのスケーリングの詳細については、「Azure での コア アプリ ASP.NET スケーリング」を参照してください。 このチュートリアルでは、Azure Container Apps でアプリをホストするために必要なサービスを作成して統合する方法について説明します。 このセクションでは、基本的な手順についても説明します。
Azure Container Apps のセッション アフィニティ (Azure ドキュメント) のガイダンスに従って、 セッション アフィニティ用に Azure Container Apps サービスを構成します。
ASP.NET Core Data Protection (DP) サービスは、すべてのコンテナー インスタンスがアクセスできる一元的な場所にキーを保持するように構成する必要があります。 キーは Azure Blob Storage に格納し、Azure Key Vault で保護できます。 DP サービスでは、キーを使用して Razor コンポーネントを逆シリアル化します。 Azure Blob Storage と Azure Key Vault を使用するように DP サービスを構成するには、次の NuGet パッケージを参照します。
Azure.Identity
: Azure ID およびアクセス管理サービスを操作するクラスを提供します。Microsoft.Extensions.Azure
: Azure のコア構成を実行するための便利な拡張方法を提供します。Azure.Extensions.AspNetCore.DataProtection.Blobs
: ASP.NET Core Data Protection キーを Azure Blob Storage に格納して、Web アプリの複数のインスタンス間でキーを共有できるようにします。Azure.Extensions.AspNetCore.DataProtection.Keys
: Azure Key Vault のキー暗号化/ラッピング機能を使用して、保存中のキーを保護できるようにします。
注
.NET アプリへのパッケージの追加に関するガイダンスについては、「パッケージ利用のワークフロー」 (NuGet ドキュメント) の "パッケージのインストールと管理" に関する記事を参照してください。 NuGet.org で正しいパッケージ バージョンを確認します。
次の強調表示されているコードを使用して、
Program.cs
を更新します。using Azure.Identity; using Microsoft.AspNetCore.DataProtection; using Microsoft.Extensions.Azure; var builder = WebApplication.CreateBuilder(args); var BlobStorageUri = builder.Configuration["AzureURIs:BlobStorage"]; var KeyVaultURI = builder.Configuration["AzureURIs:KeyVault"]; builder.Services.AddRazorPages(); builder.Services.AddHttpClient(); builder.Services.AddServerSideBlazor(); builder.Services.AddAzureClientsCore(); builder.Services.AddDataProtection() .PersistKeysToAzureBlobStorage(new Uri(BlobStorageUri), new DefaultAzureCredential()) .ProtectKeysWithAzureKeyVault(new Uri(KeyVaultURI), new DefaultAzureCredential()); var app = builder.Build(); if (!app.Environment.IsDevelopment()) { app.UseExceptionHandler("/Error"); app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); app.UseAuthorization(); app.MapRazorPages(); app.Run();
上記の変更により、アプリは、一元化されたスケーラブルなアーキテクチャを使用して DP サービスを管理できます。 DefaultAzureCredential は、コードが Azure にデプロイされた後にコンテナー アプリのマネージド ID を検出し、それを使用して BLOB ストレージとアプリのキー コンテナーに接続します。
コンテナー アプリのマネージド ID を作成し、BLOB ストレージとキー コンテナーへのアクセス権を付与するには、次の手順を実行します。
- Azure Portal で、コンテナー アプリの概要ページに移動します。
- 左側のナビゲーションから [Service Connector ] を選択します。
- 上部のナビゲーションから [ + 作成 ] を選択します。
- [ 接続の作成 ] ポップアップ メニューで、次の値を入力します。
- コンテナー: アプリをホストするために作成したコンテナー アプリを選択します。
- サービスの種類: [Blob Storage] を選択します。
- サブスクリプション: コンテナー アプリを所有するサブスクリプションを選択します。
- 接続名:
scalablerazorstorage
の名前を入力します。 - クライアントの種類: .NET を選択し、[ 次へ] を選択します。
- [システム割り当てマネージド ID] を選択し、[次へ] を選択します。
- 既定のネットワーク設定を使用し、[ 次へ] を選択します。
- Azure によって設定が検証された後、[作成] を選択します。
キー コンテナーに対して上記の設定を繰り返します。 [ 基本 ] タブで、適切なキー コンテナー サービスとキーを選択します。
注
前の例では、 DefaultAzureCredential を使用して認証を簡略化し、Azure ホスティング環境で使用される資格情報とローカル開発で使用される資格情報を組み合わせて Azure にデプロイするアプリを開発しています。 運用環境に移行する場合は、 ManagedIdentityCredentialなど、別の選択肢を選択することをお勧めします。 詳細については、「 システム割り当てマネージド ID を使用して Azure リソースに対して Azure でホストされる .NET アプリを認証する」を参照してください。
IIS
IIS を使用する場合は、次の機能を有効にします。
詳細については、「ASP.NET Core アプリを IIS に 発行する」のガイダンスと外部 IIS リソースのクロスリンクを参照してください。
Kubernetes
次の セッション アフィニティ用 Kubernetes 注釈を使用して、イングレス定義を作成します。
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: <ingress-name>
annotations:
nginx.ingress.kubernetes.io/affinity: "cookie"
nginx.ingress.kubernetes.io/session-cookie-name: "affinity"
nginx.ingress.kubernetes.io/session-cookie-expires: "14400"
nginx.ingress.kubernetes.io/session-cookie-max-age: "14400"
Nginx を使用した Linux
「ASP.NET Core SignalR アプリ」のガイダンスに従って、次のように変更します。
location
パスを/hubroute
(location /hubroute { ... }
) からルート パス/
(location / { ... }
) に変更します。- プロキシバッファリング(
)の構成を削除します。この設定は にのみ適用され、 アプリのクライアントとサーバー間の通信には関係ないためです。
詳細および構成のガイダンスについては、次のリソースを参照してください。
- ASP.NET Core SignalR 運用環境のホスティングとスケーリング
- Nginx 搭載の Linux で ASP.NET Core をホストする
- プロキシ サーバーとロード バランサーを使用するために ASP.NET Core を構成する
- WebSocket プロキシとしての NGINX
- WebSocket プロキシ
- Microsoft 以外のサポート フォーラムで開発者に相談する:
Apache を使用した Linux
Linux 上の Apache の背後で Blazor アプリをホストするには、HTTP トラフィックと WebSocket トラフィックの ProxyPass
を構成します。
次に例を示します。
- Kestrel サーバーがホスト コンピューター上で実行されています。
- アプリはポート 5000 でトラフィックをリッスンします。
ProxyPreserveHost On
ProxyPassMatch ^/_blazor/(.*) http://localhost:5000/_blazor/$1
ProxyPass /_blazor ws://localhost:5000/_blazor
ProxyPass / http://localhost:5000/
ProxyPassReverse / http://localhost:5000/
次のモジュールを有効にします。
a2enmod proxy
a2enmod proxy_wstunnel
ブラウザー コンソールで WebSocket エラーを確認します。 エラーの例:
- Firefox でサーバーへの接続を確立できない ws://the-domain-name.tld/_blazor?id=XXX
- エラー: トランスポート 'WebSockets' を開始できませんでした: エラー: トランスポートにエラーが発生しました。
- エラー: トランスポート 'LongPolling' を開始できませんでした: TypeError: this.transport は未定義です
- エラー: 使用可能なトランスポートを使用してサーバーに接続できません。 WebSocket が失敗しました
- エラー: 接続が "接続済み" 状態でない場合、データを送信できません。
詳細および構成のガイダンスについては、次のリソースを参照してください。
- プロキシ サーバーとロード バランサーを使用するために ASP.NET Core を構成する
- Apache のドキュメント
- Microsoft 以外のサポート フォーラムで開発者に相談する:
ネットワーク待ち時間を測定する
次の例に示すように、JS相互運用機能を使用してネットワーク待機時間を測定できます。
MeasureLatency.razor
:
@inject IJSRuntime JS
<h2>Measure Latency</h2>
@if (latency is null)
{
<span>Calculating...</span>
}
else
{
<span>@(latency.Value.TotalMilliseconds)ms</span>
}
@code {
private DateTime startTime;
private TimeSpan? latency;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
startTime = DateTime.UtcNow;
var _ = await JS.InvokeAsync<string>("toString");
latency = DateTime.UtcNow - startTime;
StateHasChanged();
}
}
}
@inject IJSRuntime JS
<h2>Measure Latency</h2>
@if (latency is null)
{
<span>Calculating...</span>
}
else
{
<span>@(latency.Value.TotalMilliseconds)ms</span>
}
@code {
private DateTime startTime;
private TimeSpan? latency;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
startTime = DateTime.UtcNow;
var _ = await JS.InvokeAsync<string>("toString");
latency = DateTime.UtcNow - startTime;
StateHasChanged();
}
}
}
@inject IJSRuntime JS
<h2>Measure Latency</h2>
@if (latency is null)
{
<span>Calculating...</span>
}
else
{
<span>@(latency.Value.TotalMilliseconds)ms</span>
}
@code {
private DateTime startTime;
private TimeSpan? latency;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
startTime = DateTime.UtcNow;
var _ = await JS.InvokeAsync<string>("toString");
latency = DateTime.UtcNow - startTime;
StateHasChanged();
}
}
}
@inject IJSRuntime JS
<h2>Measure Latency</h2>
@if (latency is null)
{
<span>Calculating...</span>
}
else
{
<span>@(latency.Value.TotalMilliseconds)ms</span>
}
@code {
private DateTime startTime;
private TimeSpan? latency;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
startTime = DateTime.UtcNow;
var _ = await JS.InvokeAsync<string>("toString");
latency = DateTime.UtcNow - startTime;
StateHasChanged();
}
}
}
@inject IJSRuntime JS
@if (latency is null)
{
<span>Calculating...</span>
}
else
{
<span>@(latency.Value.TotalMilliseconds)ms</span>
}
@code {
private DateTime startTime;
private TimeSpan? latency;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
startTime = DateTime.UtcNow;
var _ = await JS.InvokeAsync<string>("toString");
latency = DateTime.UtcNow - startTime;
StateHasChanged();
}
}
}
適切な UI エクスペリエンスを得るには、250 ミリ秒以下の UI 待機時間を維持することをお勧めします。
ASP.NET Core