使用用户分配的托管标识创建 Django Web 应用并将其部署到 Azure
在本教程中,会将 Django Web 应用部署到 Azure 应用程序服务。 Web 应用使用用户分配的托管身份(无密码连接)和基于 Azure 角色的访问控制来访问 Azure 存储和 Azure Database for PostgreSQL - 灵活服务器资源。 代码使用 Python 的 Azure 身份客户端库的 DefaultAzureCredential 类。 DefaultAzureCredential
类会自动检测应用程序服务是否存在托管身份,并使用它来访问其他 Azure 资源。
在本教程中,将创建一个由用户分配的托管身份,并将其分配给应用程序服务,以便它可以访问数据库和存储帐户资源。 有关使用系统分配的托管身份的示例,请参阅使用系统分配的托管身份创建 Flask Python Web 应用程序并将其部署到 Azure。 推荐使用用户分配的托管身份,因为它们可以被多个资源使用,而且其生命周期与与其相关的资源生命周期是分离的。 有关使用托管身份的最佳做法的详细信息,请参阅托管身份最佳做法建议。
本教程将介绍如何使用 Azure CLI 来部署 Python Web 应用程序和创建 Azure 资源。 本教程中编写的命令将在 Bash shell 中运行。 可以在任何安装了 CLI 的 Bash 环境(如本地环境或 Azure Cloud Shell)中运行教程命令。 只要稍加修改(例如对设置和使用环境变量),即可在 Windows 命令 shell 等其他环境中运行这些命令。
获取示例应用
使用 Django 示例应用程序来继续完成本教程。 下载或克隆示例应用程序到开发环境中。
克隆示例。
git clone https://github.com/Azure-Samples/msdocs-django-web-app-managed-identity.git
导航到应用程序文件夹。
cd msdocs-django-web-app-managed-identity
检查身份验证代码
示例 Web 应用需要对两个不同的数据存储进行身份验证:
- Azure Blob 存储服务器,用于存储和检索评论者提交的照片。
- Azure Database for PostgreSQL - 灵活服务器数据库,用于存储餐厅和评论。
它使用 DefaultAzureCredential 来对两个数据存储进行身份验证。 通过 DefaultAzureCredential
,应用就可以根据运行环境配置为以不同服务主体身份运行,而无需修改代码。 例如,在本地开发环境中,应用能以已登录 Azure CLI 的开发人员身份运行,而在 Azure 中,如本教程所示,应用程序能以用户指定的托管身份运行。
在任何一种情况下,应用运行的安全主体必须在应用使用的每个 Azure 资源上都有一个角色,该资源允许其在资源上执行应用所需的操作。 在本教程中,将使用 Azure CLI 命令来创建用户分配的托管身份,并将其分配给 Azure 中的应用。 然后,在 Azure 存储帐户和 Azure Database for PostgreSQL 服务器上为该身份手动分配适当的角色。 最后,在 Azure 中为应用设置 AZURE_CLIENT_ID
环境变量,以配置 DefaultAzureCredential
使用托管身份。
在应用及其运行时环境中配置了用户分配的托管身份,并在数据存储上分配了适当的角色后,就可以使用 DefaultAzureCredential
对所需的 Azure 资源进行身份验证。
以下代码用于创建一个 Blob 存储客户端,以上传 ./restaurant_review/views.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")
conf.settings.DATABASES['default']['PASSWORD'] = token.token
要了解有关使用 Azure 服务验证应用的详细信息,请参阅使用适用于 Python 的 Azure SDK 向 Azure 服务验证 Python 应用。 要了解有关 DefaultAzureCredential
的详细信息,包括如何为环境自定义评估凭证链,请参阅 DefaultAzureCredential 概述。
创建 Azure PostgreSQL 灵活服务器
设置教程所需的环境变量。
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" UA_NAME="UAManagedIdentityPythonTest$RAND_ID"
重要
ADMIN_PW
必须包含 8 至 128 个字符,并应从以下三个类别中选择:英文大写字母、英文小写字母、数字和非字母字符。 创建用户名或密码时不要使用$
字符。 稍后,将使用这些值创建环境变量,其中$
字符在用于运行 Python 应用的 Linux 容器中具有特殊含义。使用“az group create”命令创建资源组。
az group create --location $LOCATION --name $RESOURCE_GROUP_NAME
使用 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-name 是定价层和计算配置的名称。 有关详细信息,请参阅 Azure Database for PostgreSQL 定价。 要列出可用的 SKU,请使用
az postgres flexible-server list-skus --location $LOCATION
。使用 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
使用 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 地址是什么?网站。使用 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 应用程序服务并部署代码
在示例应用的根文件夹中运行这些命令,以创建应用程序服务并将代码部署到其中。
使用 az webapp up 命令创建应用程序服务。
az webapp up \ --resource-group $RESOURCE_GROUP_NAME \ --location $LOCATION \ --name $APP_SERVICE_NAME \ --runtime PYTHON:3.9 \ --sku B1
sku 定义应用程序服务计划的大小(CPU、内存)和成本。 B1(基本)服务计划会在订购的 Azure 服务中产生少量费用。 有关应用服务计划的完整列表,请查看应用服务定价页。
使用 az webapp config set 命令配置应用程序服务,以使用示例存储库中的 start.sh。
az webapp config set \ --resource-group $RESOURCE_GROUP_NAME \ --name $APP_SERVICE_NAME \ --startup-file "start.sh"
创建存储帐户和容器
示例应用将评论者提交的照片作为 Blob 存储在 Azure 存储中。
当用户提交带有评论的照片时,示例应用会使用托管身份和
DefaultAzureCredential
来访问存储帐户,从而将图片写入容器。当用户查看一家餐厅的评论时,应用会为每条评论返回一个链接,链接到 Blob 存储中与之相关的照片。 要显示照片,浏览器必须要能够访问存储帐户中的照片。 Blob 数据必须可通过匿名(未经身份验证)访问来公开读取。
在本部分中,将创建允许对容器中 Blob 进行公共读取访问的存储帐户和容器。 在后面的部分中,将创建一个用户分配的托管身份,并配置它将 Blob 写入存储帐户。
使用 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
使用 az storage container create 命令在存储帐户中创建一个名为 photos 的容器。
az storage container create \ --account-name $STORAGE_ACCOUNT_NAME \ --name photos \ --public-access blob \ --auth-mode login
注意
如果命令失败,例如出现错误,提示请求可能被存储帐户的网络规则阻止,此时请输入以下命令,以确保为 Azure 用户帐户分配了具有创建容器权限的 Azure 角色。
az role assignment create --role "Storage Blob Data Contributor" --assignee $ACCOUNT_EMAIL --scope "/subscriptions/$SUBSCRIPTION_ID/resourceGroups/$RESOURCE_GROUP_NAME/providers/Microsoft.Storage/storageAccounts/$STORAGE_ACCOUNT_NAME"
有关详细信息,请参阅快速入门:使用 Azure CLI 创建、下载和列出 Blob。 请注意,有几种 Azure 角色允许在存储帐户中创建容器,包括“所有者”、“参与者”、“存储 Blob 数据所有者”和“存储 Blob 数据参与者”。
创建用户分配的托管标识
创建用户分配的托管身份并将其分配给应用程序服务。 托管身份用于访问数据库和存储帐户。
使用 az identity create 命令创建用户分配的托管身份,并将客户 ID 输出到变量中,以供今后使用。
UA_CLIENT_ID=$(az identity create --name $UA_NAME --resource-group $RESOURCE_GROUP_NAME --query clientId --output tsv) echo $UA_CLIENT_ID
使用 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/$UA_NAME" echo $RESOURCE_ID
使用 az webapp identity assign 命令将托管身份分配给应用程序服务。
export MSYS_NO_PATHCONV=1 az webapp identity assign \ --resource-group $RESOURCE_GROUP_NAME \ --name $APP_SERVICE_NAME \ --identities $RESOURCE_ID
使用 az webapp config appsettings set 命令创建包含通过身份的客户端 ID 和其他配置信息的应用程序服务应用设置。
az webapp config appsettings set \ --resource-group $RESOURCE_GROUP_NAME \ --name $APP_SERVICE_NAME \ --settings AZURE_CLIENT_ID=$UA_CLIENT_ID \ STORAGE_ACCOUNT_NAME=$STORAGE_ACCOUNT_NAME \ STORAGE_CONTAINER_NAME=photos \ DBHOST=$DB_SERVER_NAME \ DBNAME=restaurant \ DBUSER=$UA_NAME
示例应用使用环境变量(应用设置)来定义数据库和存储帐户的连接信息,但这些变量不包括密码。 取而代之的是使用 DefaultAzureCredential
进行无密码的身份验证。
示例应用程序代码使用了 DefaultAzureCredential
类构造函数,但没有将用户分配的托管身份客户 ID 传递给构造函数。 在这种情况下,退而求其次的办法是检查 AZURE_CLIENT_ID
环境变量,将其设置为应用设置。
如果 AZURE_CLIENT_ID
环境变量不存在,则使用系统指定的托管身份(如已配置)。 有关详细信息,请参阅介绍 DefaultAzureCredential类。
为托管身份创建角色
在本部分中,将为托管身份创建角色分配,以启用对存储帐户和数据库的访问。
使用 az role assignment create 命令为托管身份创建角色分配,以启用对存储帐户的访问。
export MSYS_NO_PATHCONV=1 az role assignment create \ --assignee $UA_CLIENT_ID \ --role "Storage Blob Data Contributor" \ --scope "/subscriptions/$SUBSCRIPTION_ID/resourcegroups/$RESOURCE_GROUP_NAME"
该命令指定资源组的角色分配范围。 有关详细信息,请参阅了解角色分配。
使用 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('"$UA_NAME"', false, false);select * from pgaadauth_list_principals(false);"
如果在运行命令时遇到困难,请确保已将用户帐号添加为 PosgreSQL 服务器的 Microsoft Entra 管理员,并已在防火墙规则中允许访问你的 IP 地址。 有关详细信息,请参阅创建 Azure PostgreSQL 灵活服务器。
在 Azure 中测试 Python Web 应用
示例 Python 应用使用了 azure.identity 包及其 DefaultAzureCredential
类。 当应用在 Azure 中运行时,DefaultAzureCredential
会自动检测应用程序服务是否存在托管身份,如果存在,则使用该身份访问其他 Azure 资源(本例中为存储和 PostgreSQL)。 访问这些资源无需向应用程序服务提供存储密钥、证书或凭证。
浏览 URL
http://$APP_SERVICE_NAME.azurewebsites.net
中已部署的应用程序。应用可能需要一两分钟才能启动。 如果看到的默认应用页面不是默认示例应用页面,请稍等片刻并刷新浏览器。
添加一家餐厅和一些带有餐厅照片的评论,测试示例应用的功能。
餐厅和评论信息存储在 Azure PostgreSQL 数据库中,而照片存储在 Azure 存储中。 以下是示例屏幕截图:
清理
在本教程中,所有 Azure 资源都是在同一个资源组中创建的。 使用 az group delete 命令删除资源组会删除资源组中的所有资源,这也是删除应用使用的所有 Azure 资源的最快方法。
az group delete --name $RESOURCE_GROUP_NAME
可以选择性地添加 --no-wait
参数,以允许命令在操作完成之前返回。