教程:使用 Azure 容器应用和 PostgreSQL 生成和部署 Python Web 应用
本文是一个关于如何将 Python Web 应用程序容器化并部署到 Azure 容器应用的系列教程的一部分。 容器应用让你能够部署容器化应用,而无需管理复杂的基础结构。
在本教程中,你将:
- 通过在云中生成容器映像来容器化 Python 示例 Web 应用(Django 或 Flask)。
- 将容器映像部署到 Azure 容器应用。
- 定义环境变量,使容器应用能够连接到 Azure Database for PostgreSQL 灵活服务器 实例,其中示例应用存储数据。
下图重点介绍了本教程中的任务:生成和部署容器映像。
先决条件
如果没有 Azure 订阅,请在开始之前创建 免费帐户。
可以在 Azure Cloud Shell 或安装了 Azure CLI 的工作站上运行 Azure CLI 命令。
如果在本地运行,请按照以下步骤登录并安装本教程所需的模块:
如有必要,请登录到 Azure 并进行身份验证:
az login
请确保运行最新版本的 Azure CLI:
az upgrade
使用 az extension add 命令来安装或升级 containerapp 和 rdbms-connect Azure CLI 扩展:
az extension add --name containerapp --upgrade az extension add --name rdbms-connect --upgrade
获取示例应用
分叉并将示例代码克隆到开发人员环境:
转到示例应用程序(Django 或 Flask)的 GitHub 存储库,然后选择“分叉”。
请按照以下步骤将存储库分叉到 GitHub 帐户。 还可以直接将代码存储库下载到本地计算机,而无需分叉或 GitHub 帐户。 但是,如果使用下载方法,将无法在本系列的下一教程中设置持续集成和持续交付(CI/CD)。
使用 git clone 命令将分叉的存储库克隆到 python-container 文件夹中:
# Django git clone https://github.com/$USERNAME/msdocs-python-django-azure-container-apps.git python-container # Flask # git clone https://github.com/$USERNAME/msdocs-python-flask-azure-container-apps.git python-container
更改目录:
cd python-container
从 Web 应用代码生成容器映像
执行这些步骤后,你将有一个 Azure 容器注册表实例,该实例包含从示例代码生成的 Docker 容器映像。
使用 az group create 命令创建资源组:
az group create \ --name pythoncontainer-rg \ --location <location>
将 <位置> 替换为命令
az account list-locations -o table
输出中的一个 Azure 位置Name
值。使用 az acr create 命令创建容器注册表:
az acr create \ --resource-group pythoncontainer-rg \ --name <registry-name> \ --sku Basic \ --admin-enabled
用于 <registry-name> 的名称在 Azure 中必须唯一,并且必须包含 5 到 50 个字母数字字符。
使用 az acr login 命令登录到注册表:
az acr login --name <registry-name>
该命令会在名称中添加“azurecr.io”,以创建完全限定的注册表名称。 如果登录成功,将显示消息“登录成功”。 如果访问注册表的订阅不同于创建注册表的订阅,请使用
--suffix
开关。如果登录失败,请确保 Docker 守护程序正在系统上运行。
使用 az acr build 命令生成映像:
az acr build \ --registry <registry-name> \ --resource-group pythoncontainer-rg \ --image pythoncontainer:latest .
以下注意事项适用:
命令末尾的点(
.
)指示要生成的源代码的位置。 如果未在示例应用的根目录中运行此命令,请指定代码的路径。如果在 Azure Cloud Shell 中运行命令,请使用
git clone
先将存储库拉取到 Cloud Shell 环境中。 然后将目录更改为项目的根目录,以便正确解释点 (.
)。如果不使用
-t
(与--image
相同)选项,则命令会将本地上下文生成排入队列,而不会将其推送到注册表。 在不推送的情况下生成映像可用于检查映像是否已生成。
使用 az acr repository list 命令确认已创建容器映像:
az acr repository list --name <registry-name>
注意
本部分中的步骤在基本服务层中创建容器注册表。 此层经过成本优化,具有面向开发人员方案的功能集和吞吐量,适用于本教程的要求。 在生产环境中,您最有可能使用标准或高级服务层级。 这些层提供增强的存储和吞吐量级别。
若要了解详细信息,请参阅 Azure 容器注册表服务层。 有关定价的信息,请参阅 Azure 容器注册表定价。
创建 PostgreSQL 灵活服务器实例
示例应用程序(Django 或 Flask)将餐厅评论数据存储在 PostgreSQL 数据库中。 在这些步骤中,将创建包含数据库的服务器。
使用 az postgres flexible-server create 命令在 Azure 中创建 PostgreSQL 服务器。 此命令通常会运行几分钟才能完成。
az postgres flexible-server create \ --resource-group pythoncontainer-rg \ --name <postgres-server-name> \ --location <location> \ --admin-user demoadmin \ --admin-password <admin-password> \ --active-directory-auth Enabled \ --tier burstable \ --sku-name standard_b1ms \ --public-access 0.0.0.0
使用以下值:
pythoncontainer-rg
:本教程使用的资源组名称。 如果使用的是其他名称,请更改此值。<postgres-server-name>:PostgreSQL 数据库服务器名称。 此名称在所有 Azure 中必须是唯一的。 服务器终结点为
https://<postgres-server-name>.postgres.database.azure.com
。 允许的字符A
Z
、0
9
和连字符(-
)。<位置>:使用用于 Web 应用的相同位置。 <位置> 是命令
az account list-locations -o table
输出中的 Azure 位置Name
值之一。<管理员用户名>:管理员帐户的用户名。 它不能
azure_superuser
、admin
、administrator
、root
、guest
或public
。 在本教程中使用demoadmin
。<管理员密码>:管理员用户的密码。 密码必须包含以下三个类别的 8 到 128 个字符:英文大写字母、英文小写字母、数字和非字母数字字符。
重要
创建用户名或密码时,不要使用美元符号 ($) 字符。 稍后,使用这些值创建环境变量时,该字符在用于运行 Python 应用的 Linux 容器中具有特殊含义。
--active-directory-auth
:此值指定是否在 PostgreSQL 服务器上启用 Microsoft Entra 身份验证。 将其设置为Enabled
。--sku-name
:定价层和计算配置的名称;例如,Standard_B1ms
。 有关详细信息,请参阅 Azure Database for PostgreSQL 定价。 要列出可用层级,请使用az postgres flexible-server list-skus --location <location>
。--public-access
:使用0.0.0.0
。 它允许从任何 Azure 服务(例如容器应用)公开访问服务器。
注意
如果你计划从本地工作站通过工具访问 PostgreSQL 服务器,则需要使用 az postgres flexible-server firewall-rule create 命令为工作站的 IP 地址添加防火墙规则。
使用 az ad signed-in-user show 命令来获取用户帐户的对象 ID。 在下一个命令中将会用到此 ID。
az ad signed-in-user show --query id --output tsv
使用 az postgres flexible-server ad-admin create 命令将用户帐户添加为 PostgreSQL 服务器上的 Microsoft Entra 管理员:
az postgres flexible-server ad-admin create \ --resource-group pythoncontainer-rg \ --server-name <postgres-server-name> \ --display-name <your-email-address> \ --object-id <your-account-object-id>
对于帐户对象 ID,请使用在上一步中获取的值。
注意
本部分中的步骤在可突发定价层中创建具有单个 vCore 和有限内存的 PostgreSQL 服务器。 可突发层是一种较低成本的选项,适用于不需要持续使用完整 CPU 的工作负荷,并且适合本教程的要求。 对于生产工作负荷,可以升级到“常规用途”或“内存优化”定价层。 这些层提供更高的性能,但会增加成本。
若要了解详细信息,请参阅 Azure Database for PostgreSQL 灵活服务器中的
在服务器上创建数据库
至此,你就拥有了一个 PostgreSQL 服务器。 在本部分中,你将在服务器上创建一个数据库。
使用 az postgres flexible-server db create 命令来创建名为 restaurants_reviews 的数据库:
az postgres flexible-server db create \
--resource-group pythoncontainer-rg \
--server-name <postgres-server-name> \
--database-name restaurants_reviews
使用以下值:
pythoncontainer-rg
:本教程使用的资源组名称。 如果使用的是其他名称,请更改此值。<postgres-server-name>
:PostgreSQL 服务器的名称。
还可以使用 az postgres flexible-server connect 命令来连接到数据库,然后再使用 psql 命令。 在使用 psql 时,通常更容易使用 Azure Cloud Shell,因为该 shell 为你包含了所有所需的依赖项。
还可以连接到 Azure Database for PostgreSQL 灵活服务器,并使用 psql 或支持 PostgreSQL 的 IDE 创建数据库,例如 Azure Data Studio。 有关使用 psql 的步骤,请参阅本文后面的 在 PostgreSQL 数据库上配置托管标识。
创建用户分配的托管标识
创建用户分配的托管标识,以便在 Azure 中运行时用作容器应用的标识。
注意
若要创建用户分配的托管标识,你的帐户需要托管标识参与者角色分配。
使用 az identity create 命令创建用户分配的托管标识:
az identity create --name my-ua-managed-id --resource-group pythoncontainer-rg
在 PostgreSQL 数据库上配置托管标识
将托管标识配置为 PostgreSQL 服务器上的角色,然后向其授予对 restaurants_reviews 数据库所需的权限。 无论是使用 Azure CLI 还是 psql,都必须使用服务器实例中配置为 Microsoft Entra 管理员的用户连接到 Azure PostgreSQL 服务器。 只有被配置为 PostgreSQL 管理员的 Microsoft Entra 帐户才能在您的服务器上配置托管标识和其他 Microsoft 管理员角色。
使用 az account get-access-token 命令获取 Azure 帐户的访问令牌。 后续步骤中使用访问令牌。
az account get-access-token --resource-type oss-rdbms --output tsv --query accessToken
返回的令牌很长。 在环境变量中设置其值,以在下一步中的命令中使用:
MY_ACCESS_TOKEN=<your-access-token>
使用 az postgres flexible-server execute 命令,将用户分配的托管标识添加为 PostgreSQL 服务器上的数据库角色。
az postgres flexible-server execute \ --name <postgres-server-name> \ --database-name postgres \ --querytext "select * from pgaadauth_create_principal('"my-ua-managed-id"', false, false);select * from pgaadauth_list_principals(false);" \ --admin-user <your-Azure-account-email> \ --admin-password $MY_ACCESS_TOKEN
使用以下值:
如果为托管标识使用了其他名称,请将
pgaadauth_create_principal
命令中的my-ua-managed-id
替换为托管标识的名称。对于
--admin-user
值,请使用 Azure 帐户的电子邮件地址。对于
--admin-password
值,请使用上一命令输出中的访问令牌,不带引号。确保数据库名称是
postgres
。
注意
如果在本地工作站上运行
az postgres flexible-server execute
命令,请确保为工作站的 IP 地址添加了防火墙规则。 可以使用 az postgres flexible-server firewall-rule create 命令添加规则。 对于下一步中的命令也存在相同的要求。使用以下 az postgres flexible-server execute 命令授予用户分配的托管标识对 restaurants_reviews 数据库的必要权限:
az postgres flexible-server execute \ --name <postgres-server-name> \ --database-name restaurants_reviews \ --querytext "GRANT CONNECT ON DATABASE restaurants_reviews TO \"my-ua-managed-id\";GRANT USAGE ON SCHEMA public TO \"my-ua-managed-id\";GRANT CREATE ON SCHEMA public TO \"my-ua-managed-id\";GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO \"my-ua-managed-id\";ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO \"my-ua-managed-id\";" \ --admin-user <your-Azure-account-email> \ --admin-password $MY_ACCESS_TOKEN
使用以下值:
如果为托管标识使用了其他名称,请将命令中
my-ua-managed-id
的所有实例替换为托管标识的名称。 查询字符串中有五个实例。对于
--admin-user
值,请使用 Azure 帐户的电子邮件地址。对于
--admin-password
值,请使用上一个输出中的访问令牌,不带引号。确保数据库名称为
restaurants_reviews
。
此 Azure CLI 命令连接到服务器上的 restaurants_reviews 数据库,并发出以下 SQL 命令:
GRANT CONNECT ON DATABASE restaurants_reviews TO "my-ua-managed-id"; GRANT USAGE ON SCHEMA public TO "my-ua-managed-id"; GRANT CREATE ON SCHEMA public TO "my-ua-managed-id"; GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO "my-ua-managed-id"; ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO "my-ua-managed-id";
将 Web 应用部署到容器应用中
容器应用部署到 Azure 容器应用 环境,该环境充当安全边界。 在以下步骤中,将创建环境和环境内的容器。 然后配置容器,以便网站在外部可见。
这些步骤需要 Azure 容器应用扩展,containerapp。
使用 az containerapp env create 命令创建容器应用环境:
az containerapp env create \ --name python-container-env \ --resource-group pythoncontainer-rg \ --location <location>
<位置> 是命令
az account list-locations -o table
输出中的 Azure 位置Name
值之一。使用 az acr credential show 命令获取 Azure 容器注册表实例的登录凭据:
az acr credential show -n <registry-name>
在步骤 5 中创建容器应用时,可以使用命令输出中返回的用户名和密码之一。
使用 az identity show 命令,以获取用户分配的托管标识的客户端 ID 和资源 ID:
az identity show --name my-ua-managed-id --resource-group pythoncontainer-rg --query "[clientId, id]" --output tsv
在步骤 5 中创建容器应用时,可以使用命令输出中的客户端 ID(GUID)值和资源 ID。 资源 ID 具有以下形式:
/subscriptions/<subscription-id>/resourcegroups/pythoncontainer-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/my-ua-managed-id
。运行以下命令以生成密钥值:
python -c 'import secrets; print(secrets.token_hex())'
在步骤 5 中创建容器应用时,可以使用密钥值设置环境变量。
注意
此步骤显示的命令适用于 Bash shell。 根据环境,可能需要使用
python3
调用 Python。 在 Windows 上,需要将命令用双引号而不是单引号括在-c
参数中。 可能还需要使用py
或py -3
调用 Python,具体取决于环境。使用 az containerapp create 命令在环境中创建容器应用:
az containerapp create \ --name python-container-app \ --resource-group pythoncontainer-rg \ --image <registry-name>.azurecr.io/pythoncontainer:latest \ --environment python-container-env \ --ingress external \ --target-port <5000 for Flask or 8000 for Django> \ --registry-server <registry-name>.azurecr.io \ --registry-username <registry-username> \ --registry-password <registry-password> \ --user-assigned <managed-identity-resource-id> \ --query properties.configuration.ingress.fqdn \ --env-vars DBHOST="<postgres-server-name>" \ DBNAME="restaurants_reviews" \ DBUSER="my-ua-managed-id" \ RUNNING_IN_PRODUCTION="1" \ AZURE_CLIENT_ID="<managed-identity-client-id>" \ AZURE_SECRET_KEY="<your-secret-key>"
请务必将尖括号中的所有值替换为本教程中正在使用的值。 请注意,容器应用的名称在 Azure 中必须是唯一的。
--env-vars
参数的值是一个字符串,由 key=“value” 格式中的空格分隔值组成,具有以下值:DBHOST="\<postgres-server-name>"
DBNAME="restaurants_reviews"
DBUSER="my-ua-managed-id"
RUNNING_IN_PRODUCTION="1"
AZURE_CLIENT_ID="\<managed-identity-client-id>"
AZURE_SECRET_KEY="\<your-secret-key>"
DBUSER
的值是用户分配的托管标识的名称。AZURE_CLIENT_ID
的值是用户分配的托管标识的客户端 ID。 上一步中您获得了此值。AZURE_SECRET_KEY
的值是在上一步中生成的密钥值。仅对 Django 迁移并创建数据库架构。 (在 Flask 示例应用中,这一步将自动完成,因此你可以跳过。)
使用 az containerapp exec 命令进行连接:
az containerapp exec \ --name python-container-app \ --resource-group pythoncontainer-rg
然后,在 shell 命令提示符处输入
python manage.py migrate
。不需要为容器的修订版本进行迁移。
测试网站。
之前输入的
az containerapp create
命令会输出一个应用程序 URL,您可以使用此 URL 浏览到应用程序。 URL 以azurecontainerapps.io
结尾。 在浏览器中打开该 URL。 或者,也可以使用 az containerapp browse 命令。
下面是增加了一家餐厅和两条评论后的示例网站。
排查部署问题
你忘记了用于访问网站的应用程序 URL
在 Azure 门户中:
- 转到容器应用的 概述 页,查找 应用程序 URL。
在 VS Code 中:
- 转到 Azure 视图 (Ctrl+Shift+A),并展开正在处理的订阅。
- 展开容器应用节点,展开托管环境,右键单击 python-container-app,并选择浏览。 VS Code 使用应用程序 URL 打开浏览器。
在 Azure CLI 中:
- 使用命令
az containerapp show -g pythoncontainer-rg -n python-container-app --query properties.configuration.ingress.fqdn
。
在 VS Code 中,Azure 任务中的生成映像返回错误
如果“错误:未能下载上下文。 请检查 VS Code 输出 窗口中的 URL 是否不正确,请在 Docker 扩展中刷新注册表。 若要刷新,请选择 Docker 扩展,转到 注册表 部分,找到注册表,然后选择它。
如果再次运行在 Azure 中生成映像任务,请检查上次运行的注册表是否存在。 如果存在,请使用它。
在 Azure 门户中,在创建容器应用期间出现访问错误
禁用 Azure 容器注册表实例上的管理员凭据时,会出现包含“无法访问 ACR'<名称>.azurecr.io'”的访问错误。
若要在门户中检查管理员状态,请转到 Azure 容器注册表实例,选择 访问密钥 资源,并确保启用 管理员用户。
容器映像不会显示在 Azure 容器注册表实例中
- 检查 Azure CLI 命令或 VS Code 输出的输出,并查找消息以确认成功。
- 检查使用 Azure CLI 的构建命令或 VS Code 任务提示中是否正确指定了注册表的名称。
- 确保证书没有过期。 例如,在 VS Code 中,在 Docker 扩展中找到目标注册表并刷新。 在 Azure CLI 中,运行
az login
。
网站返回“错误请求(400)”
如果收到“错误请求(400)”错误,请检查传递到容器的 PostgreSQL 环境变量。 400 错误通常表示 Python 代码无法连接 PostgreSQL 实例。
本教程中使用的示例代码检查容器环境变量是否存在 RUNNING_IN_PRODUCTION
,该变量可以设置为任何值(如 1
)。
网站返回“未找到(404)”
- 检查概述页面上容器的应用程序 Url 值。 如果应用程序 URL 包含“内部”一词,则未正确设置入口。
- 检查容器的入口。 例如,在 Azure 门户中,转到容器的入口资源。 确保启用了 HTTP 入口,并选择了接受来自任何位置的流量。
网站无法启动,你会看到“流超时”,或者未返回任何结果
- 检查日志:
- 在 Azure 门户中,转到容器应用的修订管理功能资源,并查看容器的 预配状态:
- 如果状态为正在预配,请等待预配完成。
- 如果状态是失败,请选择修订并查看控制台日志。 选择显示生成时间、Stream_s 和 Log_s 列的顺序。 按最新日志进行排序,并在 Stream_s 列中查找 Python
stderr
和stdout
消息。 Pythonprint
输出stdout
消息。
- 在 Azure CLI 中,使用 az containerapp logs show 命令。
- 在 Azure 门户中,转到容器应用的修订管理功能资源,并查看容器的 预配状态:
- 如果使用 Django 框架,请检查数据库中是否存在 restaurants_reviews 表。 如果没有,请使用控制台访问容器并运行
python manage.py migrate
。