本文通过一个示例,介绍如何使用Azure Developer CLI v1.20.0的新增功能azd publish和分层基础设施(Alpha特性),在Azure容器应用(Azure Container Apps)中实现“一次构建,随处部署”的模式。你将学会如何将相同的容器化应用程序部署到多个环境,并实现适当的关注点分离。
这是Azure Developer CLI系列的第三篇文章,建立在我们之前的探索之上:Azure应用服务和GitHub Actions、Azure DevOps管道。
一次构建,随处部署
我们所解决的挑战
如果你在生产环境中使用过容器,很可能遇到过这种情况:azd deploy将所有操作捆绑在一起——构建容器、推送到注册表、以及部署——一气呵成。这对于开发来说非常方便,但给生产场景带来了一些麻烦:
- 你希望在所有环境中使用同一个Azure容器注册表
- 你需要一次构建,随处部署,无需重建容器
- 你需要安全控制来管理哪些特定容器版本可以部署到生产环境
- 你需要灵活性,以便在不同环境中使用不同的配置部署相同的容器
从本系列之前的文章中学习
在我们前两篇关于Azure应用服务开发到生产模式的博客文章之后,我们意识到azd对Azure容器应用的支持存在一些限制,阻碍了团队有效实施相同的“一次构建,随处部署”模式。azd团队在最近的版本中解决了这些问题。
Azure Developer CLI v1.20.0引入了两项解决这些挑战的能力:
- 分离的容器操作
azd publish:构建容器并将其推送到注册表
azd deploy --from-package:将特定的容器版本部署到环境(无需重建)
- 分层基础设施(Alpha特性)
- 按顺序分层部署基础设施,并进行适当的依赖管理
- 在环境中共享如ACR等资源,同时将特定于环境的内容分开
- 早期层的输出自动成为后续层的输入
我将通过一个从Azure应用服务迁移到Azure容器应用的Flask应用示例,向你展示其工作原理。
示例应用
我们构建的内容
示例应用是一个简单的基于Flask的文件管理器,用于演示关键概念:
- 功能:上传文件、列出文件和查看文件(所有操作都基于Azure Blob存储)
- 安全方法:使用Azure托管标识(不存储任何连接字符串)
基础设施的组织方式
与其将所有内容塞进一个大模板,我使用了分层方法来组织,将共享内容与特定于环境的资源分开:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
┌─────────────────────────────────────────────────────────────────┐
│ Shared Resources │
│ ┌─────────────────────────────────────────────────────────────┐│
│ │ Resource Group: rg-acr-shared ││
│ │ ┌─────────────────────────────────────────────────────────┐ ││
│ │ │ Azure Container Registry (Basic SKU) │ ││
│ │ │ - Stores container images for all environments │ ││
│ │ │ - Single source of truth for application containers │ ││
│ │ └─────────────────────────────────────────────────────────┘ ││
│ └─────────────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ Development Environment │
│ ┌─────────────────────────────────────────────────────────────┐│
│ │ Resource Group: rg-dev-environment ││
│ │ ┌─────────────────────────────────────────────────────────┐ ││
│ │ │ Container Apps Environment │ ││
│ │ │ ┌─────────────────────────────────────────────────────┐ │ ││
│ │ │ │ Container App (Flask Application) │ │ ││
│ │ │ │ - Managed Identity for ACR access │ │ ││
│ │ │ │ - Auto-scaling enabled │ │ ││
│ │ │ └─────────────────────────────────────────────────────┘ │ ││
│ │ └─────────────────────────────────────────────────────────┘ ││
│ │ Azure Storage Account | Key Vault | Application Insights ││
│ └─────────────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ Production Environment │
│ ┌─────────────────────────────────────────────────────────────┐│
│ │ Resource Group: rg-prod-environment ││
│ │ ┌─────────────────────────────────────────────────────────┐ ││
│ │ │ Container Apps Environment (VNET-integrated) │ ││
│ │ │ ┌─────────────────────────────────────────────────────┐ │ ││
│ │ │ │ Container App (Same Image as Dev) │ │ ││
│ │ │ │ - Enhanced security configuration │ │ ││
│ │ │ │ - Production-grade scaling rules │ │ ││
│ │ │ └─────────────────────────────────────────────────────┘ │ ││
│ │ └─────────────────────────────────────────────────────────┘ ││
│ │ VNET | Storage | Key Vault | App Insights | Monitoring ││
│ └─────────────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────────────┘
|
分层基础设施配置
以下是azure.yaml文件中定义的层序列:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
# Azure Container Apps Demo: "Build Once, Deploy Everywhere" with Shared ACR
name: dev-prod
# Layered Infrastructure Deployment Strategy
infra:
layers:
# Layer 1: Foundation - Core infrastructure for each environment
- name: foundation
path: infra/foundation
# Layer 2: Shared ACR - Single registry for all environments
- name: shared-acr
path: infra/shared-acr
# Layer 3: ACR Role Assignment - Security configuration
- name: acr-role
path: infra/acr-role
# Layer 4: Container App - Application deployment
- name: container-app
path: infra/container-app
services:
app:
project: .
host: containerapp
language: python
|
这种分层方法解决了容器部署中经典的“先有鸡还是先有蛋”问题。开发和生成都需要共享同一个ACR。你的生产容器应用需要从ACR拉取的权限,但在容器应用标识和ACR实际存在之前,你无法分配这些权限。通过以正确的顺序配置资源,我们确保每个资源都能获得所需的权限。
以下是各层的工作方式:
- 基础层:根据你的
AZURE_ENV_TYPE设置核心资源——这包括容器应用环境和托管标识。
- 共享ACR层:创建你的集中式容器注册表(除非你已有一个)。
- ACR角色分配层:这是魔法发生的地方——给你的托管标识赋予正确的权限(开发环境获得推送+拉取,生产环境仅获得拉取权限)。
- 容器应用层:最终部署你的应用程序,现在它拥有适当的ACR访问权限。
每一层都会输出后续层需要的东西——资源ID、端点等——azd会自动将这些输出作为输入传递给下一层。
例如,如果你查看infra/acr-role/main.parameters.json,你会看到AZURE_CONTAINER_REGISTRY_NAME如何从共享ACR层流入ACR角色分配层:
"AZURE_CONTAINER_REGISTRY_NAME": { "value": "${AZURE_CONTAINER_REGISTRY_NAME}"}
尝试一下
生产环境现实检查
虽然我向你展示了如何使用azd up进行本地部署,但对于生产部署,请使用CI/CD管道。我这里演示的本地工作流非常适合快速原型设计和开发,但对于任何重要的事务,你都需要适当的CI/CD控制。
先决条件
- Azure Developer CLI v1.20.0或更高版本(在此下载)
- Docker(用于本地容器测试)
1. 克隆示例仓库
1
|
azd init -t https://github.com/puicchan/azd-dev-prod-aca-storage
|
2. 设置你的开发环境
开发环境设置使用你可能已经熟悉的azd up工作流:
1
2
3
4
5
6
7
8
9
|
# 启用分层基础设施的Alpha特性
azd config set alpha.layers on
# 创建并配置开发环境
azd env new myapp-dev
azd env set AZURE_ENV_TYPE dev
# 部署所有内容:基础设施 + 构建 + 推送 + 部署
azd up
|
3. 准备你的生产基础设施
现在,你需要设置生产环境基础设施。这通常是在设置CI/CD管道之前的一次性操作:
1
2
3
4
5
6
7
8
9
10
|
# 创建生产环境
azd env new myapp-prod
azd env set AZURE_ENV_TYPE prod
# 引用现有的共享ACR(替换为来自开发部署的实际值)
azd env set ACR_RESOURCE_GROUP_NAME rg-shared-acr-resource-group-name
azd env set AZURE_CONTAINER_REGISTRY_ENDPOINT shared-acr-endpoint
# 仅预配基础设施(不构建/推送/部署)
azd provision
|
关于基础设施的关键说明
- 我在这里本地使用
azd provision来在应用上线前设置基础设施。在你的CI/CD管道中,绝不应该运行azd provision——只使用azd deploy。生产环境的基础设施变更应通过适当的审批流程,因为意外修改可能导致服务中断。
- 当
envType = 'prod'时,基础设施会自动包含VNET集成。出于演示目的(便于测试),我在aca-environment.bicep第42行设置了internal: false,因此你的应用保持公开可访问,同时计算是隔离的。对于真正私有的环境,你应将其切换为internal: true并添加反向代理。
4. 设置你的CI/CD管道
现在是精彩部分——让我们看看管道如何运作!进行一个简单的代码更改并提交。
例如,修改index.html中的<h1>标签,然后运行:
1
2
3
4
|
# 选择你的开发环境并配置管道
azd env select myapp-dev
# 当提示时,确保选择GitHub作为管道提供商
azd pipeline config
|
以下是需要关注的地方:
- GitHub Actions选项卡:前往你仓库的Actions选项卡
- 构建阶段:观察容器如何以唯一标签构建
- 开发部署:看到它自动部署到开发环境
- 同一容器,各处部署:检查两个环境——它们运行的是完全相同的容器镜像
GitHub Actions工作流的运作方式
工作流遵循清晰的三阶段模式:构建 → 部署-开发 → 部署-生产
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
|
┌─────────────────────────────────────────────────────────────────┐
│ GitHub Actions Workflow │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ Job 1: BUILD │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ 1. Enable alpha features (layered infrastructure) │ │
│ │ 2. Set environment names (dev/prod) │ │
│ │ 3. Log in with Azure (Federated Credentials) │ │
│ │ 4. Provision Infrastructure (dev environment) │ │
│ │ 5. Build & Publish Container to ACR │ │
│ │ └─ azd publish app │ │
│ │ └─ Get image: azd env get-value SERVICE_APP_IMAGE_NAME │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ Outputs: │
│ • container-image: crXXXX.azurecr.io/app:azd-deploy-123456 │
│ • dev-env-name: myapp-dev │
│ • prod-env-name: myapp-prod │
└──────────────────────────────┬──────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ Job 2: DEPLOY-DEV │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ 1. Enable alpha features (layered infrastructure) │ │
│ │ 2. Log in with Azure (Federated Credentials) │ │
│ │ 3. Deploy to Development │ │
│ │ └─ azd deploy app --from-package <container-image> │ │
│ │ 4. Validate Application │ │
│ │ └─ Run validation tests, smoke tests │ │
│ └─────────────────────────────────────────────────────────────┘ │
└──────────────────────────────┬──────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ Job 3: DEPLOY-PROD │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ 1. Enable alpha features (layered infrastructure) │ │
│ │ 2. Log in with Azure (Federated Credentials) │ │
│ │ 3. Deploy to Production │ │
│ │ └─ azd deploy app --from-package <same container-image> │ │
│ │ └─ Uses shared ACR from environment variables │ │
│ └─────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
Key: Same container image (built once) deployed to both environments
|
你可以在仓库的azure-dev.yml文件中查看完整的工作流实现。
需要注意的关键点:
- 容器在构建阶段仅构建一次
azd env get-value SERVICE_APP_IMAGE_NAME获取已发布的镜像名称
- 开发和生成部署完全相同的容器镜像
- 验证步骤充当阶段之间的质量关卡
注意:此工作流使用GitHub Actions作业输出在作业之间传递容器镜像名称。这只适用于GitHub托管的运行器。如果你使用自托管运行器,则需要不同的方法——也许将镜像名称存储在制品中,或使用其他方法在作业之间共享数据。
总结
本文介绍了如何使用Azure Developer CLI v1.20.0中的新功能,在Azure容器应用中实现“一次构建,随处部署”的模式。建立在我们之前关于Azure应用服务的文章之上,这种容器应用方法展示了相同的核心原则如何在不同的Azure计算服务中发挥作用。
分层基础设施和分离的容器操作(azd publish + azd deploy --from-package)的结合,为你提供了一个坚实的基础,当你准备好超越azd up的简单性,但仍希望保持熟悉的azd开发者体验时。
我们涵盖的内容:
- 容器应用集成:azd如何开箱即用地与Azure容器应用协同工作
- 分层基础设施:具有适当依赖管理的顺序部署
- 环境分离:在保持开发便利性的同时,添加生产就绪的控制
并没有一种放之四海而皆准的生产部署方法,根据组织的需求,甚至有更复杂的方法。高级网络、复杂的合规要求、不同的部署策略等;具体的实现将根据你团队的情况而有所不同。我们希望这能给你一个起点和一个可供遵循的示例。
我们将继续探索和验证Azure Developer CLI的生产部署场景,确保随着你的应用程序从开发发展到生产,azd能够提供可靠的模式。
有关实现的问题或想分享你自己的方法?请在此加入讨论。
如需更多Azure Developer CLI内容,请关注Azure Developer CLI博客并查看官方文档。