当我们完成 .NET Aspire 应用的开发后,下一步就是将其部署到云平台。Azure Container Apps 为 Aspire 应用提供了完美的托管环境,它是一个完全托管的无服务器平台,能够运行微服务和容器化应用程序。本文将深入介绍如何使用 Azure Developer CLI (azd) 将 Aspire 项目部署到 Azure Container Apps,涵盖从初始化配置到生产环境优化的完整流程。
一、Azure Container Apps 部署
Azure Container Apps 是专为云原生应用设计的托管服务,它与 .NET Aspire 的集成非常紧密。通过 Azure Developer CLI,我们可以实现从本地开发环境到云端生产环境的无缝过渡。整个部署过程的核心在于 azd 工具如何理解 Aspire 的应用模型,并将其转换为 Azure 资源。
在开始部署之前,我们需要确保安装了 Azure Developer CLI。在 Windows 系统上,可以使用 winget 命令快速安装。执行 winget install microsoft.azd 即可完成安装。对于 macOS 用户,可以使用 Homebrew 执行 brew tap azure/azd && brew install azd。Linux 用户则可以通过 curl 脚本完成安装:curl -fsSL https://aka.ms/install-azd.sh | bash。
安装完成后,我们需要在 Aspire 解决方案目录中初始化 azd 配置。打开终端,切换到包含 Aspire 解决方案文件的目录,然后执行 azd init 命令。这个命令会扫描当前目录结构,自动识别 Aspire AppHost 项目。当 azd 提示选择初始化方式时,选择"Use code in the current directory"选项。
azd 会检测到 Aspire 项目并显示找到的 AppHost 项目路径。例如,它可能会显示"Detected in: D:\source\repos\AspireSample\AspireSample.AppHost\AspireSample.AppHost.csproj"。确认后,azd 会提示输入环境名称,这个名称将用于命名 Azure 中的资源并管理不同的环境,比如 dev、staging 或 prod。
初始化过程会在项目目录中生成几个关键文件。首先是 azure.yaml 文件,它描述了应用的服务结构,并将这些服务映射到 Azure 资源。这个文件的内容相对简洁:
yaml
name: AspireSample
services:
app:
language: dotnet
project: .\AspireSample.AppHost\AspireSample.AppHost.csproj
host: containerapp
这个配置文件告诉 azd,我们的应用名为 AspireSample,使用 .NET 语言,AppHost 项目的路径,以及目标托管平台是 Container Apps。除此之外,azd 还会创建 .azure/config.json 文件来记录当前活动环境,以及 .azure/环境名/.env 文件来存储特定环境的配置覆盖。
完成初始化后,我们需要先进行 Azure 身份验证。执行 azd auth login 命令会启动浏览器进行身份验证。这一步至关重要,因为后续的资源创建都需要适当的权限。特别需要注意的是,为了将容器镜像推送到 Azure Container Registry,我们需要具有 Microsoft.Authorization/roleAssignments/write 权限。在某些情况下,可能需要在 Azure Portal 中手动启用 Container Registry 的管理员用户。
身份验证完成后,就可以执行部署了。azd up 命令是最常用的部署命令,它会一次性完成资源预配和应用部署。执行这个命令后,azd 会提示选择 Azure 订阅和地理位置。选择完成后,azd 会开始工作。
azd up 命令实际上是三个子命令的组合。第一步是 azd package,它会将应用项目及其依赖项打包成容器镜像。第二步是 azd provision,它会在 Azure 中创建应用所需的所有资源,包括 Container Apps Environment、Container Registry、Log Analytics Workspace 等。第三步是 azd deploy,它会将容器镜像推送到 Azure Container Registry,然后创建或更新 Container Apps 以使用这些镜像。
整个过程中,azd 会实时显示进度信息。我们可以看到每个资源的创建状态,例如"Done: Resource group: rg-aspire-sample"、"Done: Container Registry: acr..."、"Done: Container Apps Environment: cae..."等。部署完成后,azd 会输出访问已部署应用的 URL 链接。
在这个过程背后,azd 做了很多智能化的工作。当 azd 针对 Aspire 项目工作时,它首先会使用特殊命令运行 AppHost 项目来生成 Aspire 清单文件。具体来说,它执行 dotnet run --project AppHost.csproj --output-path manifest.json --publisher manifest。这个清单文件是一个 JSON 格式的应用模型快照,包含了所有服务、依赖关系和配置信息。
接下来,azd 的 provision 逻辑会读取这个清单文件,并在内存中生成 Bicep 模板。Bicep 是 Azure 的基础设施即代码语言,它定义了需要创建的所有 Azure 资源。生成 Bicep 后,azd 通过 Azure 的 ARM API 触发部署,在指定的订阅和资源组中创建资源。
资源配置完成后,deploy 逻辑开始工作。azd 调用 dotnet publish 并利用 .NET 的内置容器发布支持来生成容器镜像。这些镜像随后被推送到在预配阶段创建的 ACR 注册表。最后,azd 使用 ARM API 更新 Container Apps 资源,让它们开始使用新版本的容器镜像。
值得注意的是,Azure 对资源命名有特定要求。对于 Container Apps,名称必须是 2 到 32 个字符,只能包含小写字母、数字和连字符,必须以字母开头并以字母或数字结尾。在规划应用架构时,需要考虑这些命名约束。
二、部署清单文件(Manifest)
部署清单文件是 Aspire 应用模型的序列化表示,它在部署流程中扮演着关键角色。虽然默认情况下这个文件只在内存中生成,但我们可以显式生成它以便检查和调试。清单文件记录了应用的完整拓扑结构,包括所有项目、容器、依赖项、环境变量、绑定和其他配置。
当 AppHost 以清单发布模式运行时,它会遍历整个应用模型,将每个资源转换为清单格式。对于 .NET 项目,清单会记录项目路径、环境变量、HTTP 端点等信息。对于容器资源,它会记录镜像名称、端口映射、环境变量等。对于依赖资源如 Redis、PostgreSQL 等,清单会包含连接字符串表达式和特定配置。
azd 在读取清单后,会将这些抽象的资源定义转换为具体的 Azure 资源。例如,一个 Aspire 项目资源会被转换为 Azure Container App,包含适当的入口配置、环境变量和缩放规则。Redis 资源会被转换为 Azure Cache for Redis,PostgreSQL 资源会被转换为 Azure Database for PostgreSQL 等。
虽然清单文件提供了很好的抽象,但从 Aspire 9.2 开始,引入了 Publishers 的概念,这是一个新的扩展点,允许直接在应用模型上工作,而不必经过中间的清单格式。Publishers 可以插入到应用模型中,直接生成 Docker Compose 文件、Kubernetes 清单或 Azure 资源定义。这种方法提供了更丰富、更灵活的发布体验,使得目标特定的行为更容易实现。
对于需要精细控制基础设施的场景,azd 提供了显式生成 Bicep 文件的能力。通过启用 alpha 功能并执行 azd config set alpha.infraSynth on 和 azd infra synth,可以将内存中的 Bicep 模板输出到 infra 目录。这样我们就可以查看、修改和版本控制这些基础设施定义。
生成的基础设施文件包括几个部分。infra/main.bicep 是主入口点,它定义了整体部署的参数和模块引用。infra/main.parameters.json 包含部署参数的值,这些值通常映射到 .azure 目录中的环境变量。infra/resources.bicep 定义了应用所需的 Azure 资源,如 Container Apps Environment、Log Analytics、Application Insights 等。
每个 .NET 项目还会在其目录下生成 manifests/containerApp.tmpl.yaml 文件,这是该项目对应的 Container App 的定义模板。这个 YAML 文件使用 ARM 模板的语法,可以引用参数和变量。例如:
yaml
location: {{ .Env.AZURE_LOCATION }}
identity:
type: UserAssigned
userAssignedIdentities:
? {{ .Env.MANAGED_IDENTITY_ID }}
: {}
properties:
environmentId: {{ .Env.CONTAINER_APPS_ENVIRONMENT_ID }}
configuration:
ingress:
external: true
targetPort: 8080
transport: http
这种模板化的方式允许在不同环境之间复用相同的定义,只需改变环境变量的值。azd 在部署时会解析这些模板,替换变量,然后提交给 Azure。
需要注意的是,一旦生成了基础设施文件并进行了自定义修改,如果再次运行 azd infra synth,会提示是否覆盖现有文件。因此,对基础设施的定制应该在版本控制下进行,并建立清晰的工作流程来管理这些变更。
三、本地环境 vs 云端环境
Aspire 的一大优势是它在本地开发和云端部署之间提供了一致的体验,但两者之间仍然存在一些重要差异,理解这些差异对于构建健壮的应用至关重要。
在本地开发时,Aspire 通过 AppHost 协调所有服务的启动。当我们运行 AppHost 项目时,它会启动所有依赖的项目和容器,配置服务发现,建立网络连接。所有服务都在本地运行,使用 localhost 或容器名称进行通信。Aspire Dashboard 提供了实时的遥测数据,我们可以查看日志、跟踪、指标等。
云端环境则完全不同。每个 Aspire 项目或容器都成为独立的 Container App,运行在独立的容器实例中。服务之间通过 Azure 的网络基础设施通信,使用完全限定的域名。依赖项如 Redis、PostgreSQL 等被替换为对应的 Azure 托管服务,如 Azure Cache for Redis、Azure Database for PostgreSQL。
服务发现机制也不同。在本地,Aspire 使用基于 HTTP 的服务发现,AppHost 会注入环境变量来告诉每个服务如何找到其他服务。在云端,Container Apps 使用内置的服务发现功能,每个应用可以通过其名称访问同一环境中的其他应用。
配置管理也有所变化。本地开发时,配置通常来自 appsettings.json、用户机密或环境变量。云端部署时,敏感配置应该使用 Azure Key Vault,连接字符串可以通过托管身份自动注入,环境特定的配置通过 Container Apps 的环境变量或机密管理。
特别值得注意的是身份验证和授权。本地开发时,我们通常使用模拟器或开发用的连接字符串,可能会禁用某些安全检查以便于调试。生产环境必须使用托管身份进行身份验证,启用 HTTPS,实施适当的访问控制策略。例如,连接到 Azure Storage 时,本地可能使用 UseDevelopmentStorage=true,而云端应该使用托管身份:
csharp
builder.AddAzureBlobService("storage")
.WithReference(storageAccount);
在云端,这个引用会自动配置为使用托管身份,而不需要在代码中硬编码凭据。
资源限制和缩放也大不相同。本地环境受限于开发机器的资源,通常只运行单个实例。云端可以配置 CPU 和内存限制,设置最小和最大副本数,根据负载自动缩放。例如:
bicep
resource containerApp 'Microsoft.App/containerApps@2023-05-01' = {
properties: {
template: {
containers: [{
resources: {
cpu: json('1.0')
memory: '2.0Gi'
}
}]
scale: {
minReplicas: 2
maxReplicas: 10
rules: [{
name: 'http-requests'
http: {
metadata: {
concurrentRequests: '100'
}
}
}]
}
}
}
}
这个配置确保应用至少有 2 个实例在运行,最多可以扩展到 10 个实例,并且会根据并发请求数自动缩放。
监控和日志记录的实现方式也不同。本地使用 Aspire Dashboard 查看所有遥测数据。云端则集成 Azure Monitor、Application Insights 和 Log Analytics,提供了更强大的查询、警报和可视化功能。Aspire 项目自动配置 OpenTelemetry,将遥测数据发送到这些服务。
为了处理这些环境差异,Aspire 提供了条件逻辑支持。我们可以使用 builder.ExecutionContext.IsPublishMode 来检测当前是否处于发布模式,从而应用不同的配置:
csharp
var builder = DistributedApplication.CreateBuilder(args);
if (builder.ExecutionContext.IsPublishMode)
{
// 云端特定配置
builder.AddAzureContainerAppEnvironment("env");
}
var cache = builder.AddRedis("cache");
if (!builder.ExecutionContext.IsPublishMode)
{
// 本地开发时使用 Redis Commander
cache.WithRedisCommander();
}
这种模式允许我们在保持代码一致性的同时,针对不同环境进行优化。
四、云端资源配置
将 Aspire 应用部署到 Azure Container Apps 后,需要根据生产环境的要求进行资源配置和优化。虽然 azd 生成的默认配置可以工作,但通常需要针对安全性、性能和成本进行调整。
首先是网络隔离。默认情况下,Container Apps 使用公共网络,这对于开发和测试可能足够,但生产环境通常需要更严格的网络控制。我们可以配置 Container Apps Environment 使用虚拟网络:
bicep
resource containerAppsEnvironment 'Microsoft.App/managedEnvironments@2023-05-01' = {
name: environmentName
location: location
properties: {
vnetConfiguration: {
infrastructureSubnetId: subnetId
internal: true
}
workloadProfiles: [{
name: 'Consumption'
workloadProfileType: 'Consumption'
}]
}
}
这个配置将 Container Apps Environment 部署到指定的子网中,并设置为内部模式,这意味着应用只能在虚拟网络内访问。这对于构建多层应用架构特别有用,例如让前端应用可公开访问,而后端 API 和数据库只能从内部访问。
身份和访问管理是另一个关键领域。Container Apps 支持托管身份,这是在 Azure 中进行身份验证的推荐方式。我们应该为每个 Container App 配置用户分配的托管身份,并授予访问所需资源的最小权限。例如,如果应用需要从 Container Registry 拉取镜像:
bicep
resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = {
name: identityName
location: location
}
resource acrPullRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
scope: containerRegistry
name: guid(containerRegistry.id, managedIdentity.id, 'AcrPull')
properties: {
roleDefinitionId: subscriptionResourceId(
'Microsoft.Authorization/roleDefinitions',
'7f951dda-4ed3-4680-a7ca-43fe172d538d'
)
principalId: managedIdentity.properties.principalId
principalType: 'ServicePrincipal'
}
}
这个配置创建了一个托管身份,并授予它从 Container Registry 拉取镜像的权限。注意这里使用的是 AcrPull 角色,而不是更宽泛的权限,这遵循了最小权限原则。
资源大小和缩放配置直接影响应用的性能和成本。Container Apps 的资源配置包括 CPU 和内存限制,以及副本数量。我们需要根据应用的实际负载来调整这些参数:
bicep
resource containerApp 'Microsoft.App/containerApps@2023-05-01' = {
properties: {
configuration: {
ingress: {
external: true
targetPort: 8080
allowInsecure: false
transport: 'auto'
}
}
template: {
containers: [{
name: containerName
image: image
resources: {
cpu: json('1.0')
memory: '2.0Gi'
}
}]
scale: {
minReplicas: 2
maxReplicas: 10
rules: [{
name: 'http-requests'
http: {
metadata: {
concurrentRequests: '100'
}
}
}]
}
}
}
}
这个配置为每个容器分配 1 个 vCPU 和 2GB 内存,保持最少 2 个副本以确保可用性,最多扩展到 10 个副本来处理高负载,并且基于每个实例同时处理 100 个请求的阈值进行缩放。
对于不同的环境,我们可能需要不同的配置。使用参数化的方式可以让同一个基础设施定义适用于多个环境:
bicep
@allowed(['dev', 'staging', 'prod'])
param environmentType string = 'dev'
var tierConfigurations = {
dev: {
skuName: 'Consumption'
replicas: 1
cpu: json('0.5')
memory: '1.0Gi'
}
staging: {
skuName: 'Dedicated'
replicas: 2
cpu: json('1.0')
memory: '2.0Gi'
}
prod: {
skuName: 'Dedicated'
replicas: 3
cpu: json('2.0')
memory: '4.0Gi'
}
}
var currentTier = tierConfigurations[environmentType]
这种方式允许我们在开发环境使用较少的资源以节省成本,在生产环境使用更多资源以确保性能和可靠性。
监控和可观测性配置也至关重要。Container Apps 自动与 Log Analytics 和 Application Insights 集成,但我们需要确保正确配置这些组件:
bicep
resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2022-10-01' = {
name: logAnalyticsWorkspaceName
location: location
properties: {
retentionInDays: 30
sku: {
name: 'PerGB2018'
}
}
}
resource applicationInsights 'Microsoft.Insights/components@2020-02-02' = {
name: appInsightsName
location: location
kind: 'web'
properties: {
Application_Type: 'web'
WorkspaceResourceId: logAnalyticsWorkspace.id
DisableLocalAuth: true
}
}
resource containerApp 'Microsoft.App/containerApps@2023-05-01' = {
properties: {
template: {
containers: [{
env: [{
name: 'APPLICATIONINSIGHTS_CONNECTION_STRING'
value: applicationInsights.properties.ConnectionString
}]
}]
}
}
}
这个配置创建了 Log Analytics 工作区和 Application Insights 实例,并通过环境变量将连接字符串传递给 Container App。Aspire 项目会自动使用这个连接字符串来发送遥测数据。
安全性方面,我们应该禁用 HTTP,强制使用 HTTPS,配置最小 TLS 版本,并使用机密来存储敏感配置:
bicep
resource containerApp 'Microsoft.App/containerApps@2023-05-01' = {
properties: {
configuration: {
ingress: {
allowInsecure: false
transport: 'auto'
}
secrets: [{
name: 'connection-string'
value: connectionString
}]
}
template: {
containers: [{
env: [{
name: 'ConnectionStrings__Database'
secretRef: 'connection-string'
}]
}]
}
}
}
这个配置禁用了不安全的 HTTP 连接,并使用 Container Apps 的机密功能来存储数据库连接字符串,而不是直接将其作为环境变量暴露。
五、CI/CD 集成
持续集成和持续部署是现代应用开发的关键实践。Azure Developer CLI 为 Aspire 应用提供了出色的 CI/CD 集成支持,特别是在 Aspire 9.3 中引入的改进使得管道配置更加简单和安全。
azd 支持 GitHub Actions 和 Azure DevOps Pipelines 两种主要的 CI/CD 平台。配置过程通过 azd pipeline config 命令完成,这个命令会自动设置必要的身份验证和环境配置。
对于 GitHub Actions,首先需要确保已经登录 GitHub。然后在 Aspire 解决方案目录中执行 azd pipeline config。azd 会提示选择 Azure 订阅和位置,然后询问是否创建新的 GitHub 仓库。如果选择创建,azd 会在 GitHub 上创建一个私有仓库,并配置必要的机密。
azd 生成的 GitHub Actions 工作流文件位于 .github/workflows/azure-dev.yml,它定义了完整的 CI/CD 流程。工作流包含几个关键步骤:首先检出代码,然后设置 Azure Developer CLI,接着运行 azd provision 来预配资源,最后运行 azd deploy 来部署应用。
在 Aspire 9.3 之前,环境参数管理是一个痛点。所有配置都打包在一个名为 AZD_INITIAL_ENVIRONMENT_CONFIG 的巨大 JSON 变量中,难以理解和维护。Aspire 9.3 彻底改变了这一点,引入了智能参数处理机制。
现在,azd 直接从基础设施定义中提取参数,并将它们转换为命名的环境变量或机密。转换规则很简单:将 camelCase 转换为 SNAKE_CASE,将连字符替换为下划线,全部转为大写,并添加 AZURE_ 前缀。例如,Bicep 中的 openaiKey 参数会变成 AZURE_OPENAI_KEY 环境变量。
敏感参数自动被标记为机密,在 GitHub Actions 中通过 ${``{ secrets.AZURE_OPENAI_KEY }} 引用。非敏感参数则作为普通环境变量。这种方式让配置更加透明和易于管理。
当运行 azd pipeline config 时,如果检测到已存在的机密,azd 会交互式地询问如何处理。它会显示选项:"保留它"、"保留所有现有机密"、"覆盖它"或"覆盖所有机密"。这确保了我们不会意外覆盖重要的配置,同时也可以轻松更新过时的值。
对于不再使用的机密,azd 也会提示是否删除,帮助保持仓库配置的整洁。这种交互式的机密管理大大提高了安全性和可维护性。
生成的 GitHub Actions 工作流文件的基本结构如下:
yaml
name: Azure Dev
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
workflow_dispatch:
permissions:
id-token: write
contents: read
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install azd
uses: Azure/setup-azd@v0.1.0
- name: Log in to Azure
uses: azure/login@v1
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
- name: Provision Infrastructure
run: azd provision --no-prompt
env:
AZURE_ENV_NAME: ${{ vars.AZURE_ENV_NAME }}
AZURE_LOCATION: ${{ vars.AZURE_LOCATION }}
AZURE_SUBSCRIPTION_ID: ${{ vars.AZURE_SUBSCRIPTION_ID }}
- name: Deploy Application
run: azd deploy --no-prompt
env:
AZURE_ENV_NAME: ${{ vars.AZURE_ENV_NAME }}
这个工作流使用联合身份凭据进行身份验证,这是比使用服务主体机密更安全的方式。permissions 部分授予工作流所需的权限,id-token: write 允许工作流获取 OIDC 令牌用于身份验证。
对于多项目解决方案,特别是 AppHost 项目不在根目录的情况,可能需要配置工作目录。例如,如果 AppHost 在 src/MyApp.AppHost 目录,需要在相关步骤中添加 working-directory 参数:
yaml
- name: Provision Infrastructure
working-directory: ./src/MyApp.AppHost
run: azd provision --no-prompt
- name: Deploy Application
working-directory: ./src/MyApp.AppHost
run: azd deploy --no-prompt
对于 Azure DevOps Pipelines,配置过程类似,但使用 azd pipeline config --provider azdo 命令。需要先创建 Personal Access Token (PAT),并在 Azure DevOps 中设置相应的服务连接。azd 会生成 azure-pipelines.yml 文件,定义 Azure Pipelines 的构建和部署流程。
一个完整的 CI/CD 流程应该包含更多步骤,不仅仅是部署。例如,可以添加测试、代码质量检查、安全扫描等:
yaml
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup .NET
uses: actions/setup-dotnet@v3
with:
dotnet-version: '9.0.x'
- name: Restore dependencies
run: dotnet restore
- name: Build
run: dotnet build --no-restore
- name: Test
run: dotnet test --no-build --verbosity normal
- name: Install azd
uses: Azure/setup-azd@v0.1.0
- name: Log in to Azure
uses: azure/login@v1
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
- name: Provision Infrastructure
run: azd provision --no-prompt
env:
AZURE_ENV_NAME: ${{ vars.AZURE_ENV_NAME }}
AZURE_LOCATION: ${{ vars.AZURE_LOCATION }}
- name: Deploy Application
run: azd deploy --no-prompt
env:
AZURE_ENV_NAME: ${{ vars.AZURE_ENV_NAME }}
这个增强的工作流确保代码在部署前通过了所有测试,提高了部署的可靠性。
环境管理也是 CI/CD 的重要方面。通常我们会有多个环境,如开发、测试、预生产和生产。可以为每个环境创建单独的工作流文件,或者使用 GitHub 环境功能:
yaml
jobs:
deploy-to-dev:
environment: development
runs-on: ubuntu-latest
steps:
# ... 部署到开发环境
deploy-to-prod:
needs: deploy-to-dev
environment: production
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
# ... 部署到生产环境
这种配置实现了环境隔离,并且可以为不同环境设置不同的批准流程和机密。
六、部署最佳实践
在将 Aspire 应用部署到生产环境时,遵循一些最佳实践可以确保应用的安全性、可靠性和可维护性。这些实践涵盖基础设施管理、安全配置、监控、成本优化等多个方面。
第一个最佳实践是将基础设施作为代码管理。使用 azd infra synth 生成 Bicep 文件后,应该将这些文件提交到版本控制系统。这样可以跟踪基础设施的变更历史,在需要时回滚,并且团队成员可以审查和协作。基础设施代码应该和应用代码一样经过代码审查流程。
在生成基础设施文件后,通常需要进行定制以满足生产需求。重要的是建立一个迭代工作流:进行基础设施更改,在开发环境测试部署,提交到版本控制,记录定制的原因和影响。如果需要重新生成基础设施文件,应该准备好重新应用这些定制。
环境分离是另一个关键实践。应该为不同的环境使用独立的资源组,遵循一致的命名约定,使用参数化配置来处理环境差异,设置适当的访问控制来限制谁可以访问生产资源。例如,可以使用这样的资源组命名方案:rg-{appname}-{environment}-{region},如 rg-aspireapp-prod-eastus。
安全扫描应该集成到 CI/CD 流程中。可以使用工具如 Checkov 或 Azure Security Center 来扫描 Bicep 模板,查找常见的安全问题。在 GitHub Actions 中集成安全扫描:
yaml
- name: Run Checkov
uses: bridgecrewio/checkov-action@master
with:
directory: ./infra
framework: terraform
output_format: sarif
- name: Upload SARIF results
uses: github/codeql-action/upload-sarif@v2
with:
sarif_file: results.sarif
成本管理和优化也很重要。应该为资源添加标签以便跟踪成本,设置预算警报以在超支时收到通知,定期审查资源使用情况,对于非生产环境使用较小的资源规格或在非工作时间停止服务。资源标签可以在 Bicep 中统一定义:
bicep
var tags = {
'environment': environmentType
'application': applicationName
'cost-center': costCenter
'managed-by': 'azd'
}
resource containerApp 'Microsoft.App/containerApps@2023-05-01' = {
tags: tags
// ... 其他配置
}
监控和警报配置应该从一开始就设置好。配置 Application Insights 的可用性测试来持续检查应用健康状况,设置警报规则来在关键指标超过阈值时通知团队,使用 Log Analytics 查询来分析日志和指标,定期审查监控数据以识别性能问题和优化机会。
在 Bicep 中可以定义警报规则:
bicep
resource cpuAlert 'Microsoft.Insights/metricAlerts@2018-03-01' = {
name: '${containerAppName}-high-cpu'
location: 'global'
properties: {
description: 'Alert when CPU usage exceeds 80%'
severity: 2
enabled: true
scopes: [containerApp.id]
evaluationFrequency: 'PT5M'
windowSize: 'PT5M'
criteria: {
'odata.type': 'Microsoft.Azure.Monitor.SingleResourceMultipleMetricCriteria'
allOf: [{
name: 'CPU usage'
metricName: 'CpuPercentage'
operator: 'GreaterThan'
threshold: 80
timeAggregation: 'Average'
}]
}
actions: [{
actionGroupId: actionGroup.id
}]
}
}
灾难恢复计划也应该考虑。定期备份关键数据,在多个区域部署应用以提高可用性,测试故障转移流程,记录恢复步骤以便在紧急情况下快速响应。Azure Container Apps 支持跨区域部署,可以使用 Azure Front Door 或 Traffic Manager 来实现流量路由和故障转移。
性能优化是持续的过程。应该定期审查应用性能指标,根据实际负载调整资源配置,优化容器镜像大小以加快部署和启动速度,使用缓存来减少数据库负载。例如,可以在 Dockerfile 中使用多阶段构建来减小镜像大小:
dockerfile
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
WORKDIR /src
COPY ["MyApp/MyApp.csproj", "MyApp/"]
RUN dotnet restore "MyApp/MyApp.csproj"
COPY . .
WORKDIR "/src/MyApp"
RUN dotnet build "MyApp.csproj" -c Release -o /app/build
FROM build AS publish
RUN dotnet publish "MyApp.csproj" -c Release -o /app/publish
FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "MyApp.dll"]
这种多阶段构建确保最终镜像只包含运行时所需的文件,显著减小了镜像大小。
文档也是最佳实践的重要部分。应该维护部署文档,包括环境配置、部署步骤、故障排除指南。记录所有基础设施定制及其原因,保持架构图和依赖关系图的更新,为团队成员提供入职文档。良好的文档可以大大减少部署错误和解决问题的时间。
七、总结
将 .NET Aspire 应用部署到 Azure Container Apps 是一个完整的流程,涉及工具配置、环境初始化、资源预配、应用部署等多个环节。Azure Developer CLI 为这个过程提供了强大的支持,通过自动化和智能化的方式简化了大部分复杂性。
我们从安装和配置 azd 开始,学习了如何初始化 Aspire 项目的部署配置。azd init 命令会检测 AppHost 项目并生成必要的配置文件,包括 azure.yaml 和环境配置。接着,azd up 命令一次性完成了资源预配和应用部署,将本地的 Aspire 应用转换为云端的 Container Apps。
我们深入探讨了部署清单文件的作用,理解了 Aspire 如何将应用模型序列化为清单,以及 azd 如何读取清单并生成 Azure 资源。新的 Publishers 机制提供了更灵活的发布选项,可以直接在应用模型上工作,生成针对不同目标平台的部署配置。
本地开发环境和云端生产环境之间存在显著差异,包括服务发现机制、配置管理方式、身份验证方法、资源限制和监控工具。理解这些差异并适当处理是构建健壮云原生应用的关键。Aspire 的 IsPublishMode 属性允许我们在代码中处理这些差异。
云端资源配置需要考虑多个方面:网络隔离确保安全性,托管身份实现无密码身份验证,适当的资源大小和缩放规则平衡性能和成本,监控和日志配置提供可观测性。通过生成和定制 Bicep 文件,我们可以精确控制每个 Azure 资源的配置。
CI/CD 集成将部署流程自动化,特别是 Aspire 9.3 引入的改进使参数管理更加简单和安全。通过 azd pipeline config,我们可以快速设置 GitHub Actions 或 Azure DevOps Pipelines,实现从代码提交到生产部署的完整自动化。智能的参数处理和交互式的机密管理大大提高了配置的可维护性和安全性。
遵循最佳实践可以确保部署的质量和可维护性。将基础设施作为代码管理,实施环境分离,集成安全扫描,监控成本,设置完善的监控和警报,制定灾难恢复计划,持续优化性能,维护良好的文档。这些实践构成了一个完整的运维体系,支撑应用的长期稳定运行。
.NET Aspire 与 Azure Container Apps 的结合为云原生应用开发提供了理想的平台。Aspire 简化了本地开发和应用组合,Container Apps 提供了灵活的托管环境,azd 则连接了这两者,实现了从开发到生产的无缝过渡。随着工具和平台的不断演进,这个生态系统将继续为开发者提供更强大、更便捷的云原生应用开发体验。