教程:使用 Azure 容器应用和 PostgreSQL 生成和部署 Python Web 应用

本文是一个关于如何将 Python Web 应用程序容器化并部署到 Azure 容器应用的系列教程的一部分。 容器应用让你能够部署容器化应用,而无需管理复杂的基础结构。

在本教程中,你将:

  • 通过在云中生成容器映像来容器化 Python 示例 Web 应用(Django 或 Flask)。
  • 将容器映像部署到 Azure 容器应用。
  • 定义环境变量,使容器应用能够连接到 Azure Database for PostgreSQL 灵活服务器 实例,其中示例应用存储数据。

下图重点介绍了本教程中的任务:生成和部署容器映像。

在 Azure 容器应用上部署 Python 应用所涉及的服务关系图,其中突出显示了有关手动生成映像的部分。

先决条件

如果没有 Azure 订阅,请在开始之前创建 免费帐户

可以在 Azure Cloud Shell 或安装了 Azure CLI 的工作站上运行 Azure CLI 命令。

如果在本地运行,请按照以下步骤登录并安装本教程所需的模块:

  1. 如有必要,请登录到 Azure 并进行身份验证:

    az login
    
  2. 请确保运行最新版本的 Azure CLI:

    az upgrade
    
  3. 使用 az extension add 命令来安装或升级 containerapprdbms-connect Azure CLI 扩展:

    az extension add --name containerapp --upgrade
    az extension add --name rdbms-connect --upgrade
    

    注意

    若要列出系统上安装的扩展,可以使用 az extension list 命令。 例如:

    az extension list --query [].name --output tsv
    

获取示例应用

分叉并将示例代码克隆到开发人员环境:

  1. 转到示例应用程序(DjangoFlask)的 GitHub 存储库,然后选择“分叉”。

    请按照以下步骤将存储库分叉到 GitHub 帐户。 还可以直接将代码存储库下载到本地计算机,而无需分叉或 GitHub 帐户。 但是,如果使用下载方法,将无法在本系列的下一教程中设置持续集成和持续交付(CI/CD)。

  2. 使用 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
    
  3. 更改目录:

    cd python-container
    

从 Web 应用代码生成容器映像

执行这些步骤后,你将有一个 Azure 容器注册表实例,该实例包含从示例代码生成的 Docker 容器映像。

  1. 使用 az group create 命令创建资源组:

    az group create \
        --name pythoncontainer-rg \
        --location <location>
    

    <位置> 替换为命令 az account list-locations -o table输出中的一个 Azure 位置 Name 值。

  2. 使用 az acr create 命令创建容器注册表:

    az acr create \
        --resource-group pythoncontainer-rg \
        --name <registry-name> \
        --sku Basic \
        --admin-enabled
    

    用于 <registry-name> 的名称在 Azure 中必须唯一,并且必须包含 5 到 50 个字母数字字符。

  3. 使用 az acr login 命令登录到注册表:

    az acr login --name <registry-name>
    

    该命令会在名称中添加“azurecr.io”,以创建完全限定的注册表名称。 如果登录成功,将显示消息“登录成功”。 如果访问注册表的订阅不同于创建注册表的订阅,请使用 --suffix 开关。

    如果登录失败,请确保 Docker 守护程序正在系统上运行。

  4. 使用 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 相同)选项,则命令会将本地上下文生成排入队列,而不会将其推送到注册表。 在不推送的情况下生成映像可用于检查映像是否已生成。

  5. 使用 az acr repository list 命令确认已创建容器映像:

    az acr repository list --name <registry-name>
    

注意

本部分中的步骤在基本服务层中创建容器注册表。 此层经过成本优化,具有面向开发人员方案的功能集和吞吐量,适用于本教程的要求。 在生产环境中,您最有可能使用标准或高级服务层级。 这些层提供增强的存储和吞吐量级别。

若要了解详细信息,请参阅 Azure 容器注册表服务层。 有关定价的信息,请参阅 Azure 容器注册表定价

创建 PostgreSQL 灵活服务器实例

示例应用程序(DjangoFlask)将餐厅评论数据存储在 PostgreSQL 数据库中。 在这些步骤中,将创建包含数据库的服务器。

  1. 使用 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。 允许的字符 AZ09和连字符(-)。

    • <位置>:使用用于 Web 应用的相同位置。 <位置> 是命令 az account list-locations -o table输出中的 Azure 位置 Name 值之一。

    • <管理员用户名>:管理员帐户的用户名。 它不能 azure_superuseradminadministratorrootguestpublic。 在本教程中使用 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 地址添加防火墙规则。

  2. 使用 az ad signed-in-user show 命令来获取用户帐户的对象 ID。 在下一个命令中将会用到此 ID。

    az ad signed-in-user show --query id --output tsv
    
  3. 使用 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 灵活服务器中的 计算选项。 有关定价的信息,请参阅 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 管理员角色。

  1. 使用 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>
    
  2. 使用 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 命令添加规则。 对于下一步中的命令也存在相同的要求。

  3. 使用以下 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

  1. 使用 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 值之一。

  2. 使用 az acr credential show 命令获取 Azure 容器注册表实例的登录凭据:

    az acr credential show -n <registry-name>
    

    在步骤 5 中创建容器应用时,可以使用命令输出中返回的用户名和密码之一。

  3. 使用 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

  4. 运行以下命令以生成密钥值:

    python -c 'import secrets; print(secrets.token_hex())'
    

    在步骤 5 中创建容器应用时,可以使用密钥值设置环境变量。

    注意

    此步骤显示的命令适用于 Bash shell。 根据环境,可能需要使用 python3调用 Python。 在 Windows 上,需要将命令用双引号而不是单引号括在 -c 参数中。 可能还需要使用 pypy -3调用 Python,具体取决于环境。

  5. 使用 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 的值是在上一步中生成的密钥值。

  6. 仅对 Django 迁移并创建数据库架构。 (在 Flask 示例应用中,这一步将自动完成,因此你可以跳过。)

    使用 az containerapp exec 命令进行连接:

        az containerapp exec \
            --name python-container-app \
            --resource-group pythoncontainer-rg
    

    然后,在 shell 命令提示符处输入 python manage.py migrate

    不需要为容器的修订版本进行迁移。

  7. 测试网站。

    之前输入的 az containerapp create 命令会输出一个应用程序 URL,您可以使用此 URL 浏览到应用程序。 URL 以 azurecontainerapps.io结尾。 在浏览器中打开该 URL。 或者,也可以使用 az containerapp browse 命令。

下面是增加了一家餐厅和两条评论后的示例网站。

本教程中生成的示例网站的屏幕截图。

排查部署问题

你忘记了用于访问网站的应用程序 URL

在 Azure 门户中:

  • 转到容器应用的 概述 页,查找 应用程序 URL

在 VS Code 中:

  1. 转到 Azure 视图 (Ctrl+Shift+A),并展开正在处理的订阅。
  2. 展开容器应用节点,展开托管环境,右键单击 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_sLog_s 列的顺序。 按最新日志进行排序,并在 Stream_s 列中查找 Python stderrstdout 消息。 Python print 输出 stdout 消息。
    • 在 Azure CLI 中,使用 az containerapp logs show 命令。
  • 如果使用 Django 框架,请检查数据库中是否存在 restaurants_reviews 表。 如果没有,请使用控制台访问容器并运行 python manage.py migrate

下一步