本教程介绍如何将示例 Orleans 购物车应用程序部署到 Azure 容器应用。 本教程扩展了部署到 Orleans Azure 应用服务中引入的示例Orleans购物车应用的功能。 示例应用将 Azure Active Directory (AAD) 企业到使用者(B2C)身份验证添加到 Azure 容器应用。
了解如何使用 GitHub Actions、.NET 和 Azure CLIs 和 Azure Bicep 进行部署。 此外,了解如何配置容器应用的 HTTP 入口。
本教程中,您将学习如何:
- 将 Orleans 应用程序部署到 Azure 容器应用
- 使用 GitHub Actions 和 Azure Bicep 自动部署
- 配置 HTTP 入口
先决条件
- GitHub 帐户
- 阅读简介 Orleans
- .NET 6 SDK
- Azure CLI
- .NET 集成开发环境 (IDE)
- 随意使用 Visual Studio 或 Visual Studio Code
在本地运行应用
若要在本地运行应用,请复制Azure 示例项目中的购物车:Orleans Azure 容器应用存储库并将其克隆到本地机器。 克隆后,在所选的 IDE 中打开解决方案。 如果使用 Visual Studio,请右键单击 。OrleansShoppingCart.Silo 项目,选择“设为启动项目”,然后运行应用。 否则,请使用以下 .NET CLI 命令运行应用:
dotnet run --project Silo\Orleans.ShoppingCart.Silo.csproj
有关详细信息,请参阅 dotnet run。 运行应用后,登陆页面将讨论应用的功能。 在右上角,可以看到登录按钮。 注册帐户或登录(如果已存在)。 登录后,导航并测试其功能。 在本地运行时的所有应用功能都依赖于内存中持久性和本地群集。 它还使用 Bogus NuGet 包生成假冒产品。 通过在 Visual Studio 中选择 “停止调试 ”选项或在 .NET CLI 中按 Ctrl+C 来停止应用。
AAD B2C
虽然教学身份验证概念超出了本教程的范围,但了解如何 创建 Azure Active Directory B2C 租户,然后 注册 Web 应用 以使用它。 对于该购物车示例应用,请在 B2C 租户中注册已部署容器应用生成的 URL。 有关详细信息,请参阅 ASP.NET Core Blazor 身份验证和授权。
重要
部署容器应用后,在 B2C 租户中注册应用的 URL。 在大多数生产方案中,应用的 URL 只需要注册一次,因为它不应更改。
若要帮助直观显示如何在 Azure 容器应用环境中隔离应用,请参阅下图:
在上图中,所有入站流量均通过安全的 HTTP 入口流向应用程序。 Azure 容器应用环境包含应用实例,应用实例包含 ASP.NET 核心主机,公开 Blazor 服务器和 Orleans 应用功能。
部署到 Azure 容器应用
若要将应用部署到 Azure 容器应用,存储库使用 GitHub Actions。 在进行此部署之前,需要一些 Azure 资源,并且必须正确配置 GitHub 存储库。
在部署应用之前,请创建 Azure 资源组(也可以使用现有资源组)。 若要创建新的 Azure 资源组,请使用以下文章之一:
记下所选的资源组名称;稍后需要它来部署应用。
创建服务主体
若要自动执行应用的部署,需要创建服务主体。 这是一个Microsoft帐户,有权代表你管理 Azure 资源。
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 工作流。 你将了解如何使用 GitHub Actions 配合 Azure Bicep 自动化应用的部署。 Bicep 是一种特定于域的语言 (DSL),使用声明性语法来部署 Azure 资源。 有关详细信息,请参阅什么是 Bicep?。 使用创建服务主体步骤的输出,你需要用 JSON 格式的凭据创建一个名为AZURE_CREDENTIALS
的 GitHub 密钥。
在 GitHub 存储库中,选择 “设置>机密>创建新机密”。 输入名称 AZURE_CREDENTIALS
并将上一步中的 JSON 凭据粘贴到 “值 ”字段中。
有关详细信息,请参阅 GitHub:加密的机密。
准备 Azure 部署
打包应用以供部署。 在 Orleans.ShoppingCart.Silos
项目中,Target
元素被定义为在 Publish
步骤之后运行。 此目标将发布目录压缩到 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 CLIs。 请考虑 GitHub 存储库根目录中的 ./github/workflows/deploy.yml 文件:
name: Deploy to Azure Container Apps
on:
push:
branches:
- main
env:
UNIQUE_APP_NAME: orleanscart
SILO_IMAGE_NAME: orleanscart-silo
AZURE_RESOURCE_GROUP_NAME: orleans-resourcegroup
AZURE_RESOURCE_GROUP_LOCATION: eastus
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 ACR Bicep
run: |
az deployment group create \
--resource-group ${{ env.AZURE_RESOURCE_GROUP_NAME }} \
--template-file '.github/workflows/flex/acr.bicep' \
--parameters ___location=${{ env.AZURE_RESOURCE_GROUP_LOCATION }}
- name: Get ACR Login Server
run: |
ACR_NAME=$(az deployment group show -g ${{ env.AZURE_RESOURCE_GROUP_NAME }} -n acr \
--query properties.outputs.acrName.value | tr -d '"')
echo "ACR_NAME=$ACR_NAME" >> $GITHUB_ENV
ACR_LOGIN_SERVER=$(az deployment group show -g ${{ env.AZURE_RESOURCE_GROUP_NAME }} -n acr \
--query properties.outputs.acrLoginServer.value | tr -d '"')
echo "ACR_LOGIN_SERVER=$ACR_LOGIN_SERVER" >> $GITHUB_ENV
- name: Prepare Docker buildx
uses: docker/setup-buildx-action@v1
- name: Login to ACR
run: |
access_token=$(az account get-access-token --query accessToken -o tsv)
refresh_token=$(curl https://${{ env.ACR_LOGIN_SERVER }}/oauth2/exchange -v \
-d "grant_type=access_token&service=${{ env.ACR_LOGIN_SERVER }}&access_token=$access_token" | jq -r .refresh_token)
# The null GUID 0000... tells the container registry that this is an ACR refresh token during the login flow
docker login -u 00000000-0000-0000-0000-000000000000 \
--password-stdin ${{ env.ACR_LOGIN_SERVER }} <<< "$refresh_token"
- name: Build and push Silo image to registry
uses: docker/build-push-action@v2
with:
push: true
tags: ${{ env.ACR_LOGIN_SERVER }}/${{ env.SILO_IMAGE_NAME }}:${{ github.sha }}
file: Silo/Dockerfile
- name: Flex ACA 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 }} \
acrName=${{ env.ACR_NAME }} \
repositoryImage=${{ env.ACR_LOGIN_SERVER }}/${{ env.SILO_IMAGE_NAME }}:${{ github.sha }} \
--debug
- name: Get Container App URL
run: |
ACA_URL=$(az deployment group show -g ${{ env.AZURE_RESOURCE_GROUP_NAME }} \
-n main --query properties.outputs.acaUrl.value | tr -d '"')
echo $ACA_URL
- name: Logout of Azure
run: az logout
前面的 GitHub 工作流执行以下作:
- 使用 dotnet publish 命令将购物车应用发布为 zip 文件。
- 使用 “创建服务主体 ”步骤中的凭据登录到 Azure。
- 评估 acr.bicep 文件,并使用 "az deployment group create" 创建部署组。
- 从部署组获取 Azure 容器注册表 (ACR) 登录服务器。
- 使用存储库的
AZURE_CREDENTIALS
机密登录到 ACR。 - 构建并将 silo 映像发布到 ACR。
- 评估 main.bicep 文件,并使用 az deployment group create 启动部署组。
- 部署发射井。
- 注销 Azure 的账号。
工作流在推送到 main
分支时触发。 有关详细信息,请参阅 GitHub Actions 和 .NET。
小窍门
如果在运行工作流时遇到问题,可能需要验证服务主体是否已注册所有必需的提供程序命名空间。 需要以下提供程序命名空间:
Microsoft.App
Microsoft.ContainerRegistry
Microsoft.Insights
Microsoft.OperationalInsights
Microsoft.Storage
有关详细信息,请参阅解决资源提供程序注册错误。
Azure 对资源施加命名限制和约定。 更新以下环境变量的 deploy.yml 文件中的值:
UNIQUE_APP_NAME
SILO_IMAGE_NAME
AZURE_RESOURCE_GROUP_NAME
AZURE_RESOURCE_GROUP_LOCATION
将这些值设置为唯一的应用名称和 Azure 资源组名称和位置。
有关详细信息,请参阅 Azure 资源的命名规则和限制。
浏览 Bicep 模板
az deployment group create
命令运行时,它会评估给定的.bicep 文件引用。 此文件包含详细说明要部署的 Azure 资源的声明性信息。 将此步骤视为 预配 用于部署的所有资源。
重要
如果您使用 Visual Studio Code,搭配 Bicep 扩展 可以增强您的 Bicep 编写体验。
评估的第一个 Bicep 文件是 acr.bicep 文件。 此文件包含 Azure 容器注册表(ACR)登录服务器资源详细信息:
param ___location string = resourceGroup().___location
resource acr 'Microsoft.ContainerRegistry/registries@2021-09-01' = {
name: toLower('${uniqueString(resourceGroup().id)}acr')
___location: ___location
sku: {
name: 'Basic'
}
properties: {
adminUserEnabled: true
}
}
output acrLoginServer string = acr.properties.loginServer
output acrName string = acr.name
此 Bicep 文件输出 ACR 登录服务器和相应的名称。 遇到的下一个 Bicep 文件包含的不仅仅是单个 resource
文件。 请考虑 main.bicep 文件,这个文件主要由委派module
定义组成:
param appName string
param acrName string
param repositoryImage string
param ___location string = resourceGroup().___location
resource acr 'Microsoft.ContainerRegistry/registries@2021-09-01' existing = {
name: acrName
}
module env 'environment.bicep' = {
name: 'containerAppEnvironment'
params: {
___location: ___location
operationalInsightsName: '${appName}-logs'
appInsightsName: '${appName}-insights'
}
}
var envVars = [
{
name: 'APPINSIGHTS_INSTRUMENTATIONKEY'
value: env.outputs.appInsightsInstrumentationKey
}
{
name: 'APPLICATIONINSIGHTS_CONNECTION_STRING'
value: env.outputs.appInsightsConnectionString
}
{
name: 'ORLEANS_AZURE_STORAGE_CONNECTION_STRING'
value: storageModule.outputs.connectionString
}
{
name: 'ASPNETCORE_FORWARDEDHEADERS_ENABLED'
value: 'true'
}
]
module storageModule 'storage.bicep' = {
name: 'orleansStorageModule'
params: {
name: '${appName}storage'
___location: ___location
}
}
module siloModule 'container-app.bicep' = {
name: 'orleansSiloModule'
params: {
appName: appName
___location: ___location
containerAppEnvironmentId: env.outputs.id
repositoryImage: repositoryImage
registry: acr.properties.loginServer
registryPassword: acr.listCredentials().passwords[0].value
registryUsername: acr.listCredentials().username
envVars: envVars
}
}
output acaUrl string = siloModule.outputs.acaUrl
前面的 Bicep 文件执行以下操作:
-
existing
引用 ACR 资源。 有关详细信息,请参阅 Azure Bicep:现有资源。 - 定义一个
module env
委托给 environment.bicep 定义文件。 - 定义一个
module storageModule
,指向 storage.bicep 定义文件。 - 宣布由筒仓模块使用的多个共享
envVars
。 - 将
module siloModule
的定义委托给 container-app.bicep 定义文件。 - 输出 ACA URL(可能用于更新现有 AAD B2C 应用注册的重定向 URI)。
main.bicep 调用若干其他 Bicep 文件。 第一个是 environment.bicep 文件:
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'
}
}
}
resource env 'Microsoft.App/managedEnvironments@2022-03-01' = {
name: '${resourceGroup().name}env'
___location: ___location
properties: {
appLogsConfiguration: {
destination: 'log-analytics'
logAnalyticsConfiguration: {
customerId: logs.properties.customerId
sharedKey: logs.listKeys().primarySharedKey
}
}
}
}
output id string = env.id
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 文件定义了供 Container Apps module
稍后使用的三个输出。 接下来,请考虑 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 文件定义以下内容:
- 资源组名称和应用名称的两个参数。
-
resource storage
存储帐户的定义。 - 一个
output
构造存储帐户的连接字符串。
最后一个 Bicep 文件是 container-app.bicep 文件:
param appName string
param ___location string
param containerAppEnvironmentId string
param repositoryImage string = 'mcr.microsoft.com/azuredocs/containerapps-helloworld:latest'
param envVars array = []
param registry string
param registryUsername string
@secure()
param registryPassword string
resource containerApp 'Microsoft.App/containerApps@2022-03-01' = {
name: appName
___location: ___location
properties: {
managedEnvironmentId: containerAppEnvironmentId
configuration: {
activeRevisionsMode: 'multiple'
secrets: [
{
name: 'container-registry-password'
value: registryPassword
}
]
registries: [
{
server: registry
username: registryUsername
passwordSecretRef: 'container-registry-password'
}
]
ingress: {
external: true
targetPort: 80
}
}
template: {
revisionSuffix: uniqueString(repositoryImage, appName)
containers: [
{
image: repositoryImage
name: appName
env: envVars
}
]
scale: {
minReplicas: 1
maxReplicas: 1
}
}
}
}
output acaUrl string = containerApp.properties.configuration.ingress.fqdn
上述适用于 Bicep 的 Visual Studio Code 扩展包括可视化工具。 这些 Bicep 文件都可按如下方式可视化:
概要
随着源代码的更新和存储库分支的更改push
main
,deploy.yml工作流运行。 它预配 Bicep 文件中定义的 Azure 资源并部署应用程序。 修订会自动在 Azure 容器注册表中注册。
除了 Bicep 扩展中的可视化工具外,Azure 门户资源组页面在预配和部署应用程序后看起来与以下示例类似: