将 Orleans 部署到 Azure 应用服务
本教程介绍如何将 Orleans 购物车应用部署到 Azure 应用服务。 本教程将引导你部署一个支持以下功能的示例应用程序:
购物车应用程序:一个简单的购物车应用程序,它使用 Orleans 的跨平台框架支持及其可缩放的分布式应用程序功能。
- 库存管理:编辑和/或创建产品库存。
- 商店库存:浏览可购产品并将其添加到购物车。
- 购物车:查看购物车中所有商品的摘要和管理这些商品;删除或更改每个商品的数量。
了解应用及其功能后,你将了解如何使用 GitHub Actions、.NET 和 Azure CLI 以及 Azure Bicep 将该应用部署到 Azure 应用服务。 此外,你将了解如何在 Azure 中为该应用配置虚拟网络。
在本教程中,你将了解如何执行以下操作:
- 将 Orleans 应用程序部署到 Azure 应用服务
- 使用 GitHub Actions 和 Azure Bicep 自动完成部署
- 在 Azure 中为应用配置虚拟网络
先决条件
- 一个 GitHub 帐户
- 阅读 Orleans 简介
- .NET 6 SDK
- Azure CLI
- .NET 集成开发环境 (IDE)
- 任意使用 Visual Studio 或 Visual Studio Code
在本地运行应用
若要在本地运行应用,请创建 Azure 示例:Azure 应用服务上的 Orleans 群集存储库的分支并将其克隆到本地计算机。 克隆后,在所选的 IDE 中打开解决方案。 如果使用的是 Visual Studio,请右键单击“Orleans.ShoppingCart.Silo”项目并选择“设为启动项目”,然后运行应用。 否则,可使用以下 .NET CLI 命令运行应用:
dotnet run --project Silo\Orleans.ShoppingCart.Silo.csproj
有关详细信息,请参阅 dotnet run。 运行应用后,可以四处导航,并可以自由测试其功能。 在本地运行时,该应用的所有功能依赖于内存中持久性和本地群集,它使用 Bogus NuGet 包来生成虚构产品。 通过在 Visual Studio 中选择“停止调试”选项或者在 .NET CLI 中按 Ctrl+C 停止应用。
购物车应用内部
Orleans 是用于生成分布式应用程序的可靠且可缩放的框架。 在本教程中,你会使用 Orleans 将一个简单的购物车应用部署到 Azure 应用服务。 该应用公开用于管理库存、在购物车中添加和删除商品以及购买产品的功能。 客户端是使用带有服务器托管模型的 Blazor 生成的。 该应用的体系结构如下:
上图显示了客户端是服务器端 Blazor 应用。 它由多个服务组成,这些服务使用相应的 Orleans grain。 每个服务与一个 Orleans grain 配对,如下所示:
InventoryService
:使用IInventoryGrain
,其中的库存按产品类别分区。ProductService
:使用IProductGrain
,其中的单个产品按Id
关联到单个 grain 实例。ShoppingCartService
:使用IShoppingCartGrain
,其中的单个用户只有单个购物车实例,而与使用方客户端无关。
该解决方案包含三个项目:
Orleans.ShoppingCart.Abstractions
:一个类库,定义应用的模型和接口。Orleans.ShoppingCart.Grains
:一个类库,定义实现应用业务逻辑的 grain。Orleans.ShoppingCart.Silos
:托管 Orleans silo 的服务器端 Blazor 应用。
客户端用户体验
购物车客户端应用具有多个页面,每个页面代表不同的用户体验。 应用的 UI 是使用 MudBlazor NuGet 包生成的。
主页
显示几个简单的短语让用户了解应用的用途,并为每个导航菜单项添加上下文。
商店库存页面
显示所有可购产品的页面。 可通过此页面将商品添加到购物车。
空购物车页面
如果你尚未在购物车中添加任何商品,该页面会显示一条消息,指出购物车中没有商品。
已在商店库存页面上将商品添加到购物车
如果已在商店库存页面上将商品添加到购物车,则应用会显示一条消息,指出商品已添加到购物车。
产品管理页面
用户可通过此页面管理库存。 可以在库存中添加、编辑和删除产品。
产品管理页面中的“新建”对话框
当用户单击“创建新产品”按钮时,应用会显示一个对话框让用户创建新产品。
购物车页面中的商品
将商品添加到购物车后,你可以查看这些商品,更改其数量,甚至可将其从购物车中删除。 将向用户显示购物车中商品的摘要和税前总金额。
重要
当此应用在开发环境中本地运行时,该应用将使用 localhost 群集、内存中存储和本地 silo。 它还会使用通过 Bogus NuGet 包自动生成的虚构数据来为库存播种。 此操作是为了演示功能而有意执行的。
部署到 Azure App Service
典型的 Orleans 应用程序由服务器进程 (silo) 群集(grain 驻留在其中)和一组客户端进程(通常是 Web 服务器,它们接收外部请求,将请求转换为 grain 方法调用并返回结果)组成。 因此,要运行 Orleans 应用程序,首先需要启动 silo 群集。 对于测试,群集可以由一个 silo 组成。
注意
要进行可靠的生产部署,需要在群集中包含多个 silo 以实现容错和缩放。
在部署应用之前,需要创建一个 Azure 资源组(或者可以选择使用现有的资源组)。 若要创建新的 Azure 资源组,请参阅以下文章之一:
请记下所选的资源组名称,因为稍后需要用它来部署应用。
创建服务主体
若要自动部署应用,需要创建一个服务主体。 这是一个有权代表你管理 Azure 资源的 Microsoft 帐户。
az ad sp create-for-rbac --sdk-auth --role Contributor \
--name "<display-name>" --scopes /subscriptions/<your-subscription-id>
创建的 JSON 凭据如下所示,但其中包含了客户端、订阅和租户的实际值:
{
"clientId": "<your client id>",
"clientSecret": "<your client secret>",
"subscriptionId": "<your subscription id>",
"tenantId": "<your tenant id>",
"activeDirectoryEndpointUrl": "https://login.microsoftonline.com/",
"resourceManagerEndpointUrl": "https://brazilus.management.azure.com",
"activeDirectoryGraphResourceId": "https://graph.windows.net/",
"sqlManagementEndpointUrl": "https://management.core.windows.net:8443/",
"galleryEndpointUrl": "https://gallery.azure.com",
"managementEndpointUrl": "https://management.core.windows.net"
}
将命令输出复制到剪贴板,然后继续执行下一步。
创建 GitHub 机密
GitHub 提供了用于创建加密机密的机制。 创建的机密可在 GitHub Actions 工作流中使用。 你将了解如何结合 Azure Bicep 使用 GitHub Actions 来自动部署应用。 Bicep 是一种特定于域的语言 (DSL),使用声明性语法来部署 Azure 资源。 有关详细信息,请参阅什么是 Bicep。 需要使用创建服务主体步骤的输出,创建名为 AZURE_CREDENTIALS
、包含 JSON 格式的凭据的 GitHub 机密。
在 GitHub 存储库中,选择“设置”>“机密”>“创建新机密”。 输入名称 AZURE_CREDENTIALS
,然后在“值”字段中粘贴在上一步骤中创建的 JSON 凭据。
有关详细信息,请参阅 GitHub:加密的机密。
准备 Azure 部署
需要打包该应用以进行部署。 在 Orleans.ShoppingCart.Silos
项目中,我们定义了在 Publish
步骤之后运行的 Target
元素。 这会将发布目录压缩成 silo.zip 文件:
<Target Name="ZipPublishOutput" AfterTargets="Publish">
<Delete Files="$(ProjectDir)\..\silo.zip" />
<ZipDirectory SourceDirectory="$(PublishDir)" DestinationFile="$(ProjectDir)\..\silo.zip" />
</Target>
可通过多种方式将 .NET 应用部署到 Azure 应用服务。 在本教程中,你将使用 GitHub Actions、Azure Bicep 以及 .NET 和 Azure CLI。 考虑 GitHub 存储库根目录中的 ./github/workflows/deploy.yml 文件:
name: Deploy to Azure App Service
on:
push:
branches:
- main
env:
UNIQUE_APP_NAME: cartify
AZURE_RESOURCE_GROUP_NAME: orleans-resourcegroup
AZURE_RESOURCE_GROUP_LOCATION: centralus
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup .NET 6.0
uses: actions/setup-dotnet@v3
with:
dotnet-version: 6.0.x
- name: .NET publish shopping cart app
run: dotnet publish ./Silo/Orleans.ShoppingCart.Silo.csproj --configuration Release
- name: Login to Azure
uses: azure/login@v1
with:
creds: ${{ secrets.AZURE_CREDENTIALS }}
- name: Flex bicep
run: |
az deployment group create \
--resource-group ${{ env.AZURE_RESOURCE_GROUP_NAME }} \
--template-file '.github/workflows/flex/main.bicep' \
--parameters location=${{ env.AZURE_RESOURCE_GROUP_LOCATION }} \
appName=${{ env.UNIQUE_APP_NAME }} \
--debug
- name: Webapp deploy
run: |
az webapp deploy --name ${{ env.UNIQUE_APP_NAME }} \
--resource-group ${{ env.AZURE_RESOURCE_GROUP_NAME }} \
--clean true --restart true \
--type zip --src-path silo.zip --debug
上述 GitHub 工作流将:
- 使用 dotnet publish 命令将购物车应用发布为 zip 文件。
- 使用创建服务主体步骤中创建的凭据登录到 Azure。
- 评估 main.bicep 文件,并使用 az deployment group create 启动部署组。
- 使用 az webapp deploy 将 silo.zip 文件部署到 Azure 应用服务。
推送到 main 分支会触发该工作流。 有关详细信息,请参阅 GitHub Actions 和 .NET。
提示
如果在运行工作流时遇到问题,可能需要验证是否为服务主体注册了所有必需的提供程序命名空间。 以下提供程序命名空间是必需的:
Microsoft.Web
Microsoft.Network
Microsoft.OperationalInsights
Microsoft.Insights
Microsoft.Storage
有关详细信息,请参阅解决资源提供程序注册错误。
Azure 对资源施加命名限制和约定。 需要更新以下各项的 deploy.yml 文件值:
UNIQUE_APP_NAME
AZURE_RESOURCE_GROUP_NAME
AZURE_RESOURCE_GROUP_LOCATION
将这些值设置为唯一的应用名称,以及 Azure 资源组名称和位置。
有关详细信息,请参阅 Azure 资源的命名规则和限制。
浏览 Bicep 模板
运行 az deployment group create
命令时,它会评估 main.bicep 文件。 此文件包含要部署的 Azure 资源。 此步骤的作用是预配所有部署资源。
重要
如果使用的是 Visual Studio Code,则使用 Bicep 扩展可以改善 Bicep 创作体验。
有许多 Bicep 文件,其中每个文件包含资源或模块(资源集合)。 main.bicep 文件是入口点,主要由 module
定义构成:
param appName string
param location string = resourceGroup().location
module storageModule 'storage.bicep' = {
name: 'orleansStorageModule'
params: {
name: '${appName}storage'
location: location
}
}
module logsModule 'logs-and-insights.bicep' = {
name: 'orleansLogModule'
params: {
operationalInsightsName: '${appName}-logs'
appInsightsName: '${appName}-insights'
location: location
}
}
resource vnet 'Microsoft.Network/virtualNetworks@2021-05-01' = {
name: '${appName}-vnet'
location: location
properties: {
addressSpace: {
addressPrefixes: [
'172.17.0.0/16'
]
}
subnets: [
{
name: 'default'
properties: {
addressPrefix: '172.17.0.0/24'
delegations: [
{
name: 'delegation'
properties: {
serviceName: 'Microsoft.Web/serverFarms'
}
}
]
}
}
]
}
}
module siloModule 'app-service.bicep' = {
name: 'orleansSiloModule'
params: {
appName: appName
location: location
vnetSubnetId: vnet.properties.subnets[0].id
appInsightsConnectionString: logsModule.outputs.appInsightsConnectionString
appInsightsInstrumentationKey: logsModule.outputs.appInsightsInstrumentationKey
storageConnectionString: storageModule.outputs.connectionString
}
}
上面的 Bicep 文件定义了以下内容:
- 对应于资源组名称和应用名称的两个参数。
storageModule
定义,用于定义存储帐户。logsModule
定义,用于定义 Azure Log Analytics 和 Application Insights 资源。vnet
资源,用于定义虚拟网络。siloModule
定义,用于定义 Azure 应用服务。
一个非常重要的 resource
是虚拟网络。 vnet
资源使 Azure 应用服务能够与 Orleans 群集通信。
每当在 Bicep 文件中遇到 module
时,都会通过另一个包含资源定义的 Bicep 文件对其进行评估。 遇到的第一个模块是 storageModule
,它在 storage.bicep 文件中定义:
param name string
param location string
resource storage 'Microsoft.Storage/storageAccounts@2021-08-01' = {
name: name
location: location
kind: 'StorageV2'
sku: {
name: 'Standard_LRS'
}
}
var key = listKeys(storage.name, storage.apiVersion).keys[0].value
var protocol = 'DefaultEndpointsProtocol=https'
var accountBits = 'AccountName=${storage.name};AccountKey=${key}'
var endpointSuffix = 'EndpointSuffix=${environment().suffixes.storage}'
output connectionString string = '${protocol};${accountBits};${endpointSuffix}'
Bicep 文件接受使用 param
关键字声明的参数。 同样,它们也可以使用 output
关键字来声明输出。 存储 resource
依赖于 Microsoft.Storage/storageAccounts@2021-08-01
类型和版本。 它作为 StorageV2
和 Standard_LRS
SKU 在资源组位置进行预配。 存储 Bicep 将其连接字符串定义为 output
。 此 connectionString
稍后由 silo Bicep 用来连接到存储帐户。
接下来,logs-and-insights.bicep 文件定义 Azure Log Analytics 和 Application Insights 资源:
param operationalInsightsName string
param appInsightsName string
param location string
resource appInsights 'Microsoft.Insights/components@2020-02-02' = {
name: appInsightsName
location: location
kind: 'web'
properties: {
Application_Type: 'web'
WorkspaceResourceId: logs.id
}
}
resource logs 'Microsoft.OperationalInsights/workspaces@2021-06-01' = {
name: operationalInsightsName
location: location
properties: {
retentionInDays: 30
features: {
searchVersion: 1
}
sku: {
name: 'PerGB2018'
}
}
}
output appInsightsInstrumentationKey string = appInsights.properties.InstrumentationKey
output appInsightsConnectionString string = appInsights.properties.ConnectionString
此 Bicep 文件定义 Azure Log Analytics 和 Application Insights 资源。 appInsights
资源的类型为 web
,logs
资源的类型为 PerGB2018
。 appInsights
资源和 logs
资源均在资源组位置进行预配。 appInsights
资源通过 WorkspaceResourceId
属性链接到 logs
资源。 此 Bicep 中定义了稍后将由应用服务 module
使用的两个输出。
最后,app-service.bicep 文件定义了 Azure 应用服务资源:
param appName string
param location string
param vnetSubnetId string
param appInsightsInstrumentationKey string
param appInsightsConnectionString string
param storageConnectionString string
resource appServicePlan 'Microsoft.Web/serverfarms@2021-03-01' = {
name: '${appName}-plan'
location: location
kind: 'app'
sku: {
name: 'S1'
capacity: 1
}
}
resource appService 'Microsoft.Web/sites@2021-03-01' = {
name: appName
location: location
kind: 'app'
properties: {
serverFarmId: appServicePlan.id
virtualNetworkSubnetId: vnetSubnetId
httpsOnly: true
siteConfig: {
vnetPrivatePortsCount: 2
webSocketsEnabled: true
netFrameworkVersion: 'v6.0'
appSettings: [
{
name: 'APPINSIGHTS_INSTRUMENTATIONKEY'
value: appInsightsInstrumentationKey
}
{
name: 'APPLICATIONINSIGHTS_CONNECTION_STRING'
value: appInsightsConnectionString
}
{
name: 'ORLEANS_AZURE_STORAGE_CONNECTION_STRING'
value: storageConnectionString
}
]
alwaysOn: true
}
}
}
resource appServiceConfig 'Microsoft.Web/sites/config@2021-03-01' = {
name: '${appService.name}/metadata'
properties: {
CURRENT_STACK: 'dotnet'
}
}
此 Bicep 文件将 Azure 应用服务配置为 .NET 6 应用程序。 appServicePlan
资源和 appService
资源均在资源组位置进行预配。 appService
资源配置为使用 S1
SKU,该 SKU 的容量为 1
。 此外,该资源配置为使用 vnetSubnetId
子网并使用 HTTPS。 此文件还配置了 appInsightsInstrumentationKey
检测密钥、appInsightsConnectionString
连接字符串和 storageConnectionString
连接字符串。 这些信息由购物车应用使用。
上述适用于的 Bicep 的 Visual Studio Code 扩展包含一个可视化工具。 下面是所有这些 Bicep 文件的可视化效果:
摘要
更新源代码并将更改 push
到存储库的 main
分支时,将运行 deploy.yml 工作流。 该工作流将预配 Bicep 文件中定义的资源并部署应用程序。 可以扩展该应用程序以包含新功能(例如身份验证)或支持应用程序的多个实例。 此工作流的主要目的是演示如何通过一个步骤来预配和部署资源。
除了 Bicep 扩展中的可视化工具外,在预配并部署应用程序后,会显示如以下示例所示的 Azure 门户资源组页面: