快速入门:使用 Bicep 通过 azd Python Web 模板部署的服务

使用 Python Web azd 模板 可以快速创建新的 Web 应用程序并将其部署到 Azure。 这些 azd 模板旨在使用低成本的 Azure 服务选项。 毫无疑问,你需要调整方案模板中定义的每个服务的服务级别(或 SKU)。

在本快速入门中,你将更新相应的 bicep 模板文件,以纵向扩展现有服务并将新服务添加到部署。 然后,你将运行 azd provision 该命令并查看对 Azure 部署所做的更改。

先决条件

Azure 订阅 - 免费创建订阅

必须在本地计算机上安装以下各项:

部署模板

首先,需要一个工作 azd 部署。 就位后,可以修改模板生成的 azd Bicep 文件。

  1. 按照快速入门文章中的步骤 1 到 7 进行操作。 在步骤 2 中 azure-django-postgres-flexible-appservice ,使用模板。 为方便起见,下面是从命令行发出的整个命令序列:

    mkdir azdtest
    cd azdtest
    azd init --template azure-django-postgres-flexible-appservice
    azd auth login
    azd up
    

    完成后azd up,打开Azure 门户,导航到新资源组中部署的Azure App 服务并记下App 服务定价计划(请参阅App 服务计划的“概述”页“概要”部分“定价计划”值)。

  2. 在快速入门文章的步骤 1 中,已指示你创建 azdtest 文件夹。 在 Visual Studio Code 中打开该文件夹。

  3. 在“资源管理器”窗格中,导航到 基础结构 文件夹。 观察基础结构文件夹中的子文件夹和文件。

    main.bicep 文件协调执行azd upazd provision操作时部署的所有服务的创建。 它调用其他文件,例如 db.bicepweb.bicep,后者又调用了 \core 子文件夹中包含的文件。

    \core 子文件夹是包含许多 Azure 服务的 bicep 模板的深度嵌套文件夹结构。 \core 子文件夹中的某些文件由三个顶级 bicep 文件(main.bicep、db.bicepweb.bicep)引用,有些文件根本不用于此项目中。

通过修改服务 Bicep 属性来缩放服务

可以通过更改部署中的现有资源 SKU 来缩放现有资源。 为了演示这一点,你将将App 服务计划从“基本服务计划”(专为流量要求较低且不需要高级自动缩放和流量管理功能的应用设计)更改为“标准服务计划”,该计划专为运行生产工作负荷而设计。

注意

事实之后,并非所有 SKU 更改都可以进行。 可能需要进行一些研究才能更好地了解缩放选项。

  1. 打开 web.bicep 文件并找到appService模块定义。 具体而言,请查找属性设置:

       sku: {
          name: 'B1'
       }
    

    将值从 B1 以下值更改为 S1

       sku: {
          name: 'S1'
       }
    

    重要

    由于此变化,每小时的价格将略有上升。 有关不同服务计划及其相关成本的详细信息,请参阅App 服务定价页

  2. 假设已在 Azure 中部署了应用程序,请使用以下命令将更改部署到基础结构,同时不重新部署应用程序代码本身。

    azd provision
    

    不应提示输入位置和订阅。 这些值保存在 .azure<environment-name.env> 文件中,该文件<environment-name>中提供azd init的环境名称。

  3. 完成后 azd provision ,确认 Web 应用程序仍然有效。 另请找到资源组的App 服务计划,并确认定价计划已设置为标准服务计划(S1)。

使用 Bicep 添加新的服务定义

可以通过对项目中的 Bicep 进行更大的更改,将新资源添加到部署。 为了演示这一点,你将将 Azure Cache for Redis 的实例添加到现有部署,以准备计划在将来添加的虚构新功能。

重要

由于此更改,你将为 Azure Redis 缓存实例付费,直到删除Azure 门户中的资源或使用azd down。 有关不同服务计划及其相关成本的详细信息,请参阅 Azure Redis 缓存定价页

  1. 在名为 redis.bicep基础结构文件夹中创建新文件。 将以下代码复制并粘贴到新文件中:

    param name string
    param location string = resourceGroup().location
    param tags object = {}
    param keyVaultName string
    param connStrKeyName string
    param passwordKeyName string
    param primaryKeyKeyName string
    
    @allowed([
      'Enabled'
      'Disabled'
    ])
    param publicNetworkAccess string = 'Enabled'
    
    @allowed([
      'C'
      'P'
    ])
    param skuFamily string = 'C'
    
    @allowed([
      0
      1
      2
      3
      4
      5
      6
    ])
    param skuCapacity int = 1
    @allowed([
      'Basic'
      'Standard'
      'Premium'
    ])
    param skuName string = 'Standard'
    
    param saveKeysToVault bool = true
    
    resource redis 'Microsoft.Cache/redis@2020-12-01' = {
      name: name
      location: location
      properties: {
        sku: {
          capacity: skuCapacity
          family: skuFamily
          name: skuName
        }
        publicNetworkAccess: publicNetworkAccess
        enableNonSslPort: true    
      }
      tags: tags
    }
    
    resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = {
      name: keyVaultName
    }
    
    resource redisKey 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = if (saveKeysToVault) {
      name: primaryKeyKeyName
      parent: keyVault
      properties: {
        value: redis.listKeys().primaryKey
      }
    }
    
    resource redisConnStr 'Microsoft.KeyVault/vaults/secrets@2018-02-14' = if (saveKeysToVault) {
      name: connStrKeyName
      parent: keyVault
      properties: {
        value: '${name}.redis.cache.windows.net,abortConnect=false,ssl=true,password=${redis.listKeys().primaryKey}'
      }
    }
    resource redisPassword 'Microsoft.KeyVault/vaults/secrets@2018-02-14' = if (saveKeysToVault) {
      name: passwordKeyName
      parent: keyVault
      properties: {
        value: redis.listKeys().primaryKey
      }
    }
    
    output REDIS_ID string = redis.id
    output REDIS_HOST string = redis.properties.hostName
    
  2. 修改 main.bicep 文件以创建“redis”资源的实例。

    在 main.bicep 文件中,将以下代码添加到与 Web 前端节和机密节上方关联的结束大括号下方

    // Caching server
    module redis 'redis.bicep' = {
      name: 'redis'
      scope: resourceGroup
      params: {
        name: replace('${take(prefix, 19)}-rds', '--', '-')
        location: location
        tags: tags
        keyVaultName: keyVault.outputs.name
        connStrKeyName: 'RedisConnectionString'
        passwordKeyName: 'RedisPassword'
        primaryKeyKeyName: 'RedisPrimaryKey'
        publicNetworkAccess: 'Enabled'
        skuFamily: 'C'
        skuCapacity: 1
        skuName: 'Standard'
        saveKeysToVault: true
      }
    }
    
  3. 将输出值添加到文件底部:

    output REDIS_ID string = redis.outputs.REDIS_ID
    output REDIS_HOST string = redis.outputs.REDIS_HOST
    
  4. 确认整个 main.bicep 文件与以下代码相同:

    targetScope = 'subscription'
    
    @minLength(1)
    @maxLength(64)
    @description('Name which is used to generate a short unique hash for each resource')
    param name string
    
    @minLength(1)
    @description('Primary location for all resources')
    param location string
    
    @secure()
    @description('DBServer administrator password')
    param dbserverPassword string
    
    @secure()
    @description('Secret Key')
    param secretKey string
    
    @description('Id of the user or app to assign application roles')
    param principalId string = ''
    
    var resourceToken = toLower(uniqueString(subscription().id, name, location))
    var prefix = '${name}-${resourceToken}'
    var tags = { 'azd-env-name': name }
    
    resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = {
      name: '${name}-rg'
      location: location
      tags: tags
    }
    
    // Store secrets in a keyvault
    module keyVault './core/security/keyvault.bicep' = {
      name: 'keyvault'
      scope: resourceGroup
      params: {
        name: '${take(replace(prefix, '-', ''), 17)}-vault'
        location: location
        tags: tags
        principalId: principalId
      }
    }
    
    module db 'db.bicep' = {
      name: 'db'
      scope: resourceGroup
      params: {
        name: 'dbserver'
        location: location
        tags: tags
        prefix: prefix
        dbserverDatabaseName: 'relecloud'
        dbserverPassword: dbserverPassword
      }
    }
    
    // Monitor application with Azure Monitor
    module monitoring 'core/monitor/monitoring.bicep' = {
      name: 'monitoring'
      scope: resourceGroup
      params: {
        location: location
        tags: tags
        applicationInsightsDashboardName: '${prefix}-appinsights-dashboard'
        applicationInsightsName: '${prefix}-appinsights'
        logAnalyticsName: '${take(prefix, 50)}-loganalytics' // Max 63 chars
      }
    }
    
    // Web frontend
    module web 'web.bicep' = {
      name: 'web'
      scope: resourceGroup
      params: {
        name: replace('${take(prefix, 19)}-appsvc', '--', '-')
        location: location
        tags: tags
        applicationInsightsName: monitoring.outputs.applicationInsightsName
        keyVaultName: keyVault.outputs.name
        appCommandLine: 'entrypoint.sh'
        pythonVersion: '3.12'
        dbserverDomainName: db.outputs.dbserverDomainName
        dbserverUser: db.outputs.dbserverUser
        dbserverDatabaseName: db.outputs.dbserverDatabaseName
      }
    }
    
    // Caching server
    module redis 'redis.bicep' = {
      name: 'redis'
      scope: resourceGroup
      params: {
        name: replace('${take(prefix, 19)}-rds', '--', '-')
        location: location
        tags: tags
        keyVaultName: keyVault.outputs.name
        connStrKeyName: 'RedisConnectionString'
        passwordKeyName: 'RedisPassword'
        primaryKeyKeyName: 'RedisPrimaryKey'
        publicNetworkAccess: 'Enabled'
        skuFamily: 'C'
        skuCapacity: 1
        skuName: 'Standard'
        saveKeysToVault: true
      }
    }
    
    var secrets = [
      {
        name: 'DBSERVERPASSWORD'
        value: dbserverPassword
      }
      {
        name: 'SECRETKEY'
        value: secretKey
      }
    ]
    
    @batchSize(1)
    module keyVaultSecrets './core/security/keyvault-secret.bicep' = [for secret in secrets: {
      name: 'keyvault-secret-${secret.name}'
      scope: resourceGroup
      params: {
        keyVaultName: keyVault.outputs.name
        name: secret.name
        secretValue: secret.value
      }
    }]
    
    output AZURE_LOCATION string = location
    output AZURE_KEY_VAULT_ENDPOINT string = keyVault.outputs.endpoint
    output AZURE_KEY_VAULT_NAME string = keyVault.outputs.name
    output APPLICATIONINSIGHTS_NAME string = monitoring.outputs.applicationInsightsName
    output BACKEND_URI string = web.outputs.uri
    
    output REDIS_ID string = redis.outputs.REDIS_ID
    output REDIS_HOST string = redis.outputs.REDIS_HOST
    
  5. 请确保保存所有更改,然后使用以下命令更新 Azure 上的预配资源:

    azd provision
    

    注意

    根据许多因素,将 Azure Cache for Redis 的实例添加到现有部署可能需要很长时间。 在测试中,我们经历了超过 20 分钟的执行时间。 只要未看到任何错误,就可以允许该过程在完成之前继续。

  6. 完成后azd provision,打开Azure 门户,导航到部署的资源组,并在服务列表中确认你现在有 Azure Redis 缓存实例。

本快速入门总结了快速入门,但有许多 Azure 服务可帮助你构建更具可缩放性和生产就绪的应用程序。 开始的一个好地方是了解 Azure API 管理Azure Front DoorAzure 内容分发网络Azure 虚拟网络,例如几个。

清理资源

通过运行 azd down 命令清理模板创建的资源。

azd down

azd down 命令将删除 Azure 资源和 GitHub Actions 工作流。 出现提示时,同意删除与资源组关联的所有资源。

还可以删除 azdtest 文件夹,或者通过修改项目的文件将其用作你自己的应用程序的基础。