使用系统分配的托管身份创建 Flask Python Web 应用并将其部署到 Azure

在本教程中,你将部署 Python Flask 代码,以便创建和部署在 Azure 应用程序服务中运行的 Web 应用。 Web 应用使用其系统分配的托管身份(无密码连接)和基于 Azure 角色的访问控制来访问 Azure 存储Azure Database for PostgreSQL - 灵活服务器资源。 代码使用 Python 的 Azure 身份客户端库DefaultAzureCredential 类。 DefaultAzureCredential 类会自动检测应用程序服务是否存在托管身份,并使用它来访问其他 Azure 资源。

可以使用服务连接器配置与 Azure 服务的无密码连接,也可以手动进行配置。 本教程介绍如何使用服务连接器。 有关无密码连接的详细信息,请参阅 Azure 服务的无密码连接。 有关服务连接器的信息,请参阅服务连接器文档

本教程介绍如何使用 Azure CLI 来创建和部署 Python Web 应用。 本教程中编写的命令将在 Bash shell 中运行。 可以在任何安装了 CLI 的 Bash 环境(如本地环境或 Azure Cloud Shell)中运行教程命令。 只要稍加修改(例如对设置和使用环境变量),即可在 Windows 命令 shell 等其他环境中运行这些命令。 有关使用用户分配的托管身份的示例,请参阅使用用户分配的托管身份创建 Django Web 应用并将其部署到 Azure

获取示例应用

我们提供了一个使用 Flask 框架的 Python 应用程序示例来帮助你学习本教程。 请将一个示例应用程序下载或克隆到本地工作站。

  1. 在 Azure Cloud Shell 会话中克隆示例。

    git clone https://github.com/Azure-Samples/msdocs-flask-web-app-managed-identity.git
    
  2. 导航到应用程序文件夹。

    cd msdocs-flask-web-app-managed-identity
    

检查身份验证代码

示例 Web 应用需要对两个不同的数据存储进行身份验证:

  • Azure Blob 存储服务器,用于存储和检索评论者提交的照片。
  • Azure Database for PostgreSQL - 灵活服务器数据库,用于存储餐厅和评论。

它使用 DefaultAzureCredential 来对两个数据存储进行身份验证。 通过 DefaultAzureCredential,应用就可以根据运行环境配置为以不同服务主体身份运行,而无需修改代码。 例如,在本地开发环境中,应用能以已登录 Azure CLI 的开发人员身份运行,而在 Azure 中,如本教程所示,应用程序能以系统分配的托管身份运行。

在任何一种情况下,应用运行的安全主体必须在应用使用的每个 Azure 资源上都有一个角色,该资源允许其在资源上执行应用所需的操作。 在本教程中,将使用服务连接器在 Azure 中的应用程序上自动启用系统分配的托管身份,并在 Azure 存储帐户和 Azure Database for PostgreSQL 服务器上为该身份分配适当的角色。

启用系统分配的托管身份并在数据存储上分配适当的角色后,就可以使用 DefaultAzureCredential 来对所需的 Azure 资源进行身份验证。

以下代码用于创建一个 Blob 存储客户端,以上传 app.py 中的照片。 向客户端提供一个 DefaultAzureCredential 实例,客户端将使用该实例来获取访问令牌,以便对 Azure 存储执行操作。

from azure.identity import DefaultAzureCredential
from azure.storage.blob import BlobServiceClient

azure_credential = DefaultAzureCredential()
blob_service_client = BlobServiceClient(
    account_url=account_url,
    credential=azure_credential)

DefaultAzureCredential 的实例还用于获取 ./azureproject/get_conn.py 中 Azure Database for PostgreSQL 的访问令牌。 在这种情况下,可通过调用凭据实例上的 get_token 并传递适当的 scope 值来直接获取令牌。 然后,在返回给调用方的 PostgreSQL 连接 URI 中,令牌将代替密码。

azure_credential = DefaultAzureCredential()
token = azure_credential.get_token("https://ossrdbms-aad.database.windows.net")
conn = str(current_app.config.get('DATABASE_URI')).replace('PASSWORDORTOKEN', token.token)

要了解有关使用 Azure 服务验证应用的详细信息,请参阅使用适用于 Python 的 Azure SDK 向 Azure 服务验证 Python 应用。 要了解有关 DefaultAzureCredential 的详细信息,包括如何为环境自定义评估凭证链,请参阅 DefaultAzureCredential 概述

创建 Azure PostgreSQL 服务器

  1. 设置教程所需的环境变量。

    LOCATION="eastus"
    RAND_ID=$RANDOM
    RESOURCE_GROUP_NAME="msdocs-mi-web-app"
    APP_SERVICE_NAME="msdocs-mi-web-$RAND_ID"
    DB_SERVER_NAME="msdocs-mi-postgres-$RAND_ID"
    ADMIN_USER="demoadmin"
    ADMIN_PW="ChAnG33#ThsPssWD$RAND_ID"
    

    重要

    ADMIN_PW 必须包含 8 至 128 个字符,并应从以下三个类别中选择:英文大写字母、英文小写字母、数字和非字母字符。 创建用户名或密码时不要使用 $ 字符。 稍后,将使用这些值创建环境变量,其中 $ 字符在用于运行 Python 应用的 Linux 容器中具有特殊含义。

  2. 使用“az group create”命令创建资源组。

    az group create --location $LOCATION --name $RESOURCE_GROUP_NAME
    
  3. 使用 az postgres flexible-server create 命令创建 PostgreSQL 服务器。 (此命令和后续命令使用 Bash Shell 的行延续字符 ('\')。 如果需要,请为 shell 更改行继续符。)

    az postgres flexible-server create \
      --resource-group $RESOURCE_GROUP_NAME \
      --name $DB_SERVER_NAME \
      --location $LOCATION \
      --admin-user $ADMIN_USER \
      --admin-password $ADMIN_PW \
      --sku-name Standard_D2ds_v4
    

    sku-name 是定价层和计算配置的名称。 有关详细信息,请参阅 Azure Database for PostgreSQL 定价。 要列出可用的 SKU,请使用 az postgres flexible-server list-skus --location $LOCATION

  4. 使用 az postgres flexible-server execute 命令创建名为 restaurant 的数据库。

    az postgres flexible-server execute \
      --name $DB_SERVER_NAME \
      --admin-user $ADMIN_USER \
      --admin-password $ADMIN_PW \
      --database-name postgres \
      --querytext 'create database restaurant;'
    

创建 Azure 应用程序服务并部署代码

  1. 使用 az webapp up 命令创建应用程序服务。

    az webapp up \
      --resource-group $RESOURCE_GROUP_NAME \
      --name $APP_SERVICE_NAME \
      --runtime PYTHON:3.9 \
      --sku B1
    

    sku 定义应用程序服务计划的大小(CPU、内存)和成本。 B1(基本)服务计划会在订购的 Azure 服务中产生少量费用。 有关应用服务计划的完整列表,请查看应用服务定价页。

  2. 使用 az webapp config set 命令配置应用程序服务,以使用存储库中的 start.sh

    az webapp config set \
      --resource-group $RESOURCE_GROUP_NAME \
      --name $APP_SERVICE_NAME \
      --startup-file "start.sh"
    

创建连接 Azure 资源的无密码连接器

服务连接器命令可配置用于 PostgreSQL 资源的 Azure 存储和 Azure 数据库,以使用托管身份和基于 Azure 角色的访问控制。 这些命令会在应用程序服务中创建应用设置,将 Web 应用连接到这些资源。 这些命令的输出列出了服务连接器为启用无密码功能而采取的操作。

  1. 使用 az webapp connection create postgres-flexible 命令来添加 PostgreSQL 服务连接器。 系统分配的托管身份用于验证 Web 应用与目标资源(本例中为 PostgreSQL)的连接。

    az webapp connection create postgres-flexible \
      --resource-group $RESOURCE_GROUP_NAME \
      --name $APP_SERVICE_NAME \
      --target-resource-group $RESOURCE_GROUP_NAME \
      --server $DB_SERVER_NAME \
      --database restaurant \
      --client-type python \
      --system-identity
    
  2. 使用 az webapp connection create storage-blob 命令来添加存储服务连接器。

    此命令还会添加一个存储帐户,并将角色为存储 Blob 数据参与者的 Web 应用添加到存储帐户。

    STORAGE_ACCOUNT_URL=$(az webapp connection create storage-blob \
      --new true \
      --resource-group $RESOURCE_GROUP_NAME \
      --name $APP_SERVICE_NAME \
      --target-resource-group $RESOURCE_GROUP_NAME \
      --client-type python \
      --system-identity \
      --query configurations[].value \
      --output tsv)
    STORAGE_ACCOUNT_NAME=$(cut -d . -f1 <<< $(cut -d / -f3 <<< $STORAGE_ACCOUNT_URL))
    

在存储帐户中创建容器

Python 应用示例将评论者提交的照片以 Blob 形式存储在存储帐户的容器中。

  • 当用户在评论中提交照片时,示例应用会使用系统分配的托管身份进行身份验证和授权,以便将图片写入容器。 你在上一节中配置了这一功能。

  • 当用户查看一家餐厅的评论时,应用会为每条评论返回一个链接,链接到 Blob 存储中与之相关的照片。 要显示照片,浏览器必须要能够访问存储帐户中的照片。 Blob 数据必须可通过匿名(未经身份验证)访问来公开读取。

为提高安全性,在创建存储帐户时已默认禁止匿名访问 blob 数据。 在本部分中,将在存储帐户上启用匿名读取访问权限,然后创建一个名为 photos 的容器,该容器提供对其 Blob 的公共(匿名)访问。

  1. 使用 az storage account update 命令更新存储帐户,以便允许匿名读取 Blob。

    az storage account update \
      --name $STORAGE_ACCOUNT_NAME \
      --resource-group $RESOURCE_GROUP_NAME \
      --allow-blob-public-access true
    

    在存储帐户上启用匿名访问不会影响对单个 Blob 的访问。 必须在容器层级显式启用对 Blob 的公共访问。

  2. 使用 az storage container create 命令在存储帐户中创建一个名为 photos 的容器。 允许匿名读取(公开)范文新创建的容器中的 Blob。

    az storage container create \
      --account-name $STORAGE_ACCOUNT_NAME \
      --name photos \
      --public-access blob \
      --account-key $(az storage account keys list --account-name $STORAGE_ACCOUNT_NAME \
          --query [0].value --output tsv) 
    

    注意

    为简洁起见,此命令使用存储帐户密钥对存储帐户进行授权。 对于大多数场景,Microsoft 推荐的方法是使用 Microsoft Entra ID 和 Azure (RBAC) 角色。 有关一系列快速说明,请参阅快速入门:使用 Azure CLI 创建、下载和列出 Blob。 请注意,有几种 Azure 角色允许在存储帐户中创建容器,包括“所有者”、“参与者”、“存储 Blob 数据所有者”和“存储 Blob 数据参与者”。

要了解有关 Blob 数据匿名读取访问的详细信息,请参阅配置对容器和 Blob 的匿名读取访问权限

在 Azure 中测试 Python Web 应用

示例 Python 应用使用了 azure.identity 包及其 DefaultAzureCredential 类。 当应用在 Azure 中运行时,DefaultAzureCredential 会自动检测应用程序服务是否存在托管身份,如果存在,则使用该身份访问其他 Azure 资源(本例中为存储和 PostgreSQL)。 访问这些资源无需向应用程序服务提供存储密钥、证书或凭证。

  1. 浏览 URL http://$APP_SERVICE_NAME.azurewebsites.net 中已部署的应用程序。

    应用可能需要一两分钟才能启动。 如果看到的默认应用页面不是默认示例应用页面,请稍等片刻并刷新浏览器。

  2. 添加一家餐厅和一些带有餐厅照片的评论,测试示例应用的功能。

    餐厅和评论信息存储在 Azure PostgreSQL 数据库中,而照片存储在 Azure 存储中。 以下是示例屏幕截图:

    示例应用的屏幕截图,其中显示了使用 Azure 应用程序服务、Azure PostgreSQL 数据库和 Azure 存储实现的餐厅评论功能。

清理

在本教程中,所有 Azure 资源都是在同一个资源组中创建的。 使用 az group delete 命令删除资源组会删除资源组中的所有资源,这也是删除应用使用的所有 Azure 资源的最快方法。

az group delete  --name $RESOURCE_GROUP_NAME 

可以选择性地添加 --no-wait 参数,以允许命令在操作完成之前返回。

后续步骤