使用用户分配的托管标识创建 Django Web 应用并将其部署到 Azure

在本教程中,将 Django Web 应用部署到Azure App 服务。 Web 应用使用用户分配的托管标识(无密码连接)和 Azure 基于角色的访问控制来访问Azure 存储Azure Database for PostgreSQL 灵活服务器资源。 该代码使用用于 Python 的 Azure 标识客户端库的 DefaultAzureCredential 类。 该DefaultAzureCredential类会自动检测App 服务是否存在托管标识,并使用它访问其他 Azure 资源。

在本教程中,你将创建用户分配的托管标识并将其分配给App 服务,以便它可以访问数据库和存储帐户资源。 有关使用系统分配的托管标识的示例,请参阅 使用系统分配的托管标识创建 Flask Python Web 应用并将其部署到 Azure。 建议使用用户分配的标识,因为它们可由多个资源使用,并且其生命周期与与其关联的资源生命周期分离。 有关使用托管标识的最佳做法的详细信息,请参阅 托管标识最佳做法建议

本教程介绍如何使用 Azure CLI 部署 Python Web 应用和创建 Azure 资源。 可以在安装 CLI 的任何环境中运行教程命令,例如本地环境、 Azure Cloud ShellGitHub Codespaces

获取示例应用

使用示例 Django 示例应用程序与本教程一起操作。 将示例应用程序下载或克隆到开发环境。

  1. 克隆示例。

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

    cd msdocs-django-web-app-managed-identity
    

创建 Azure PostgreSQL 灵活服务器

  1. 设置本教程所需的环境变量,并使用 az group create 命令创建资源组。

    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"
    
    az group create --location $LOCATION --name $RESOURCE_GROUP_NAME
    

    重要

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

  2. 使用 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 \
      --active-directory-auth Enabled \
      --public-access 0.0.0.0
    

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

  3. 使用 az postgres flexible-server ad-admin create 命令将 Azure 帐户添加为服务器的 Microsoft Entra 管理员。

    ACCOUNT_EMAIL=$(az ad signed-in-user show --query userPrincipalName --output tsv)
    ACCOUNT_ID=$(az ad signed-in-user show --query id --output tsv)
    echo $ACCOUNT_EMAIL, $ACCOUNT_ID
    az postgres flexible-server ad-admin create \
      --resource-group $RESOURCE_GROUP_NAME \
      --server-name $DB_SERVER_NAME \
      --display-name $ACCOUNT_EMAIL \
      --object-id $ACCOUNT_ID \
      --type User
    
  4. 使用 az postgres flexible-server firewall-rule create 命令在服务器上配置防火墙规则。 此规则允许本地环境访问连接到服务器。 (如果使用 Azure Cloud Shell,可以跳过此步骤。

    IP_ADDRESS=<your IP>
    az postgres flexible-server firewall-rule create \
       --resource-group $RESOURCE_GROUP_NAME \
       --name $DB_SERVER_NAME \
       --rule-name AllowMyIP \
       --start-ip-address $IP_ADDRESS \
       --end-ip-address $IP_ADDRESS
    

    使用任何显示 IP 地址的工具或网站在命令中替换 <your IP> 。 例如,可以使用 “我的 IP 地址 ”网站。

  5. 使用 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 App 服务并部署代码

在示例应用的根文件夹中运行这些命令,以创建App 服务并向其部署代码。

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

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

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

  2. 将App 服务配置为将示例存储库中的 start.sh 与 az webapp config set 命令配合使用

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

创建存储帐户和容器

示例应用将图像以 blob 的形式存储在Azure 存储中。 存储帐户配置为允许公共访问容器。 应用使用托管标识和 DefaultAzureCredential 访问存储帐户。

  1. 使用 az storage create 命令创建存储帐户。

    STORAGE_ACCOUNT_NAME="msdocsstorage$RAND_ID"
    az storage account create \
      --name $STORAGE_ACCOUNT_NAME \
      --resource-group $RESOURCE_GROUP_NAME \
      --location $LOCATION \
      --sku Standard_LRS \
      --allow-blob-public-access true
    
  2. 使用 az storage container create 命令在存储帐户中创建名为照片容器。

    az storage container create \
      --account-name $STORAGE_ACCOUNT_NAME \
      --name photos \
      --public-access blob \
      --auth-mode login
    

创建用户分配的托管标识

创建用户分配的托管标识并将其分配给App 服务。 托管标识用于访问数据库和存储帐户。

  1. 使用 az identity create 命令创建名为“UAManagedIdentityPythonTest”的用户分配的托管标识,并将客户端 ID 输出到变量以供以后使用。

    UAClientID=$(az identity create --name UAManagedIdentityPythonTest --resource-group $RESOURCE_GROUP_NAME --query clientId --output tsv)
    echo $UAClientID
    
  2. 使用 az account show 命令获取订阅 ID 并将其输出到可用于构造托管标识的资源 ID 的变量。

    SUBSCRIPTION_ID=$(az account show --query id --output tsv)
    RESOURCE_ID="/subscriptions/$SUBSCRIPTION_ID/resourceGroups/$RESOURCE_GROUP_NAME/providers/Microsoft.ManagedIdentity/userAssignedIdentities/UAManagedIdentityPythonTest"
    echo $RESOURCE_ID
    
  3. 使用 az webapp identity assign 命令将托管标识分配到App 服务。

    export MSYS_NO_PATHCONV=1
    az webapp identity assign \
        --resource-group $RESOURCE_GROUP_NAME \
        --name $APP_SERVICE_NAME \
        --identities $RESOURCE_ID
    
  4. 使用 az webapp config appsettings set 命令创建包含托管标识的客户端 ID 和其他配置信息的App 服务应用设置。

    az webapp config appsettings set \
      --resource-group $RESOURCE_GROUP_NAME \
      --name $APP_SERVICE_NAME \
      --settings AZURE_CLIENT_ID=$UAClientID \
        STORAGE_ACCOUNT_NAME=$STORAGE_ACCOUNT_NAME \
        STORAGE_CONTAINER_NAME=photos \
        DBHOST=$DB_SERVER_NAME \
        DBNAME=restaurant \
        DBUSER=UAManagedIdentityPythonTest
    

示例应用使用环境变量(应用设置)来定义数据库和存储帐户的连接信息,但这些变量不包括密码。 相反,身份验证是使用 DefaultAzureCredential.

显示的存储库代码使用 DefaultAzureCredential 类构造函数,而无需将用户分配的托管标识客户端 ID 传递给构造函数。 在此方案中,回退是针对AZURE_CLIENT_ID环境变量检查,该环境变量设置为应用设置。

如果AZURE_CLIENT_ID环境变量不存在,则会在配置托管标识时使用系统分配的托管标识。 有关详细信息,请参阅 DefaultAzureCredential 简介。

为托管标识创建角色

在本部分中,将为托管标识创建角色分配,以启用对存储帐户和数据库的访问。

  1. 使用 az role assignment create 命令为托管标识创建角色分配,以便访问存储帐户

    export MSYS_NO_PATHCONV=1
    az role assignment create \
    --assignee $UAClientID \
    --role "Storage Blob Data Contributor" \
    --scope "/subscriptions/$SUBSCRIPTION_ID/resourcegroups/$RESOURCE_GROUP_NAME"
    

    该命令指定分配给资源组的角色分配的范围。 有关详细信息,请参阅 “了解角色分配”。

  2. 使用 az postgres flexible-server execute 命令连接到 Postgres 数据库,并运行相同的命令将角色分配给托管标识。

    ACCOUNT_EMAIL_TOKEN=$(az account get-access-token --resource-type oss-rdbms --output tsv --query accessToken)
    az postgres flexible-server execute \
      --name $DB_SERVER_NAME \
      --admin-user $ACCOUNT_EMAIL \
      --admin-password $ACCOUNT_EMAIL_TOKEN \
      --database-name postgres \
      --querytext "select * from pgaadauth_create_principal('UAManagedIdentityPythonTest', false, false);select * from pgaadauth_list_principals(false);"
    

    如果在运行命令时遇到问题,请确保已将用户帐户添加为 PosgreSQL 服务器的 Microsoft Entra 管理员,并且已允许访问防火墙规则中的 IP 地址。 有关详细信息,请参阅“创建 Azure PostgreSQL 灵活服务器”部分

在 Azure 中测试 Python Web 应用

示例 Python 应用使用 azure.identity 包及其 DefaultAzureCredential 类。 DefaultAzureCredential自动检测App 服务存在托管标识,并使用此标识访问其他 Azure 资源(在本例中为存储和 PostgreSQL)。 无需向App 服务提供存储密钥、证书或凭据即可访问这些资源。

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

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

  2. 通过添加餐厅以及一些带有餐厅照片的评论来测试示例应用的功能。

    餐厅和评论信息存储在 Azure Database for PostgreSQL 中,照片存储在Azure 存储中。 下面是示例屏幕截图:

    Screenshot of the sample app showing restaurant review functionality using Azure App Service, Azure PostgreSQL Database, and Azure Storage.

清理

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

az group delete  --name $RESOURCE_GROUP_NAME 

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

后续步骤