教程:使用 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/<github-username>/msdocs-python-django-azure-container-apps.git python-container
    
    # Flask
    # git clone https://github.com/<github-username>/msdocs-python-flask-azure-container-apps.git python-container
    
  3. 更改目录:

    cd python-container
    

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

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

  1. 如果在 Windows 计算机上的 Git Bash shell 中运行命令,请在继续操作之前输入以下命令:

    #!/bin/bash
    export MSYS_NO_PATHCONV=1
    
  2. 使用 az group create 命令创建资源组:

    #!/bin/bash
    RESOURCE_GROUP_NAME=<resource-group-name>
    LOCATION=<location>
    az group create \
        --name $RESOURCE_GROUP_NAME \
        --location $LOCATION
    
  3. 使用 az acr create 命令创建容器注册表:

    #!/bin/bash
    REGISTRY_NAME=<registry-name> #The name that you use for *\<registry-name>* must be unique within Azure, and it must contain 5 to 50 alphanumeric characters.
    az acr create \
        --resource-group $RESOURCE_GROUP_NAME \
        --name $REGISTRY_NAME \
        --sku Basic \
        --admin-enabled true
    
  4. 使用 az acr login 命令登录到注册表:

    az acr login --name $REGISTRY_NAME
    

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

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

  5. 使用 az acr build 命令生成映像:

    #!/bin/bash
    az acr build \
        --registry $REGISTRY_NAME \
        --resource-group $RESOURCE_GROUP_NAME \
        --image pythoncontainer:latest .
    

    以下注意事项适用:

    • 命令末尾的点(.)指示要生成的源代码的位置。 如果未在示例应用的根目录中运行此命令,请指定代码的路径。

    • 如果在 Azure Cloud Shell 中运行命令,请使用 git clone 先将存储库拉取到 Cloud Shell 环境中。 然后将目录更改为项目的根目录,以便正确解释点 (.)。

    • 如果不使用 -t(与 --image 相同)选项,则命令会将本地上下文生成排入队列,而不会将其推送到注册表。 在不推送的情况下生成映像可用于检查映像是否已生成。

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

    az acr repository list --name $REGISTRY_NAME
    

注意

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

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

创建 PostgreSQL 灵活服务器实例

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

  1. 使用 az postgres flexible-server create 命令在 Azure 中创建 PostgreSQL 服务器。 此命令通常会运行几分钟才能完成。

    #!/bin/bash
    ADMIN_USERNAME=demoadmin
    ADMIN_PASSWORD=<admin-password> # Use a strong password that meets the requirements for PostgreSQL.
    POSTGRES_SERVER_NAME=<postgres-server-name> 
    az postgres flexible-server create \
      --resource-group $RESOURCE_GROUP_NAME \
      --name $POSTGRES_SERVER_NAME \
      --location $LOCATION \
      --admin-user $ADMIN_USERNAME \
      --admin-password $ADMIN_PASSWORD \
      --version 16 \
      --tier Burstable \
      --sku-name Standard_B1ms \
      --public-access 0.0.0.0 \
      --microsoft-entra-auth Enabled \
      --storage-size 32 \
      --backup-retention 7 \
      --high-availability Disabled \
      --yes
    
    

    使用以下值:

    • <postgres-server-name>:PostgreSQL 数据库服务器名称。 此名称在所有 Azure 中必须是唯一的。 服务器终结点为 https://<postgres-server-name>.postgres.database.azure.com。 允许的字符 AZ09和连字符(-)。

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

    • <管理员用户名>:管理员帐户的用户名。 它不能 azure_superuseradminadministratorrootguestpublic。 在本教程中使用 demoadmin

    • <管理员密码>:管理员用户的密码。 密码必须包含以下三个类别的 8 到 128 个字符:英文大写字母、英文小写字母、数字和非字母数字字符。

      重要

      创建用户名或密码时,不要使用美元符号 ($) 字符。 稍后,使用这些值创建环境变量时,该字符在用于运行 Python 应用的 Linux 容器中具有特殊含义。

    • --version:使用 16。 它指定要用于服务器的 PostgreSQL 版本。

    • --tier:使用 Burstable。 它指定服务器的定价层。 可突发层是一种较低成本的选项,适用于不需要持续使用完整 CPU 的工作负荷,并且适合本教程的要求。

    • --sku-name:定价层和计算配置的名称;例如,Standard_B1ms。 有关详细信息,请参阅 Azure Database for PostgreSQL 定价。 要列出可用层级,请使用az postgres flexible-server list-skus --location <location>

    • --public-access:使用 0.0.0.0。 它允许从任何 Azure 服务(例如容器应用)公开访问服务器。

    • --microsoft-entra-auth:使用 Enabled。 它在服务器上启用Microsoft Entra 身份验证。

    • --storage-size:使用 32。 它指定服务器的存储大小(GB)。 最小值为 32 GB。

    • --backup-retention:使用 7。 它指定保留服务器的备份的天数。 最小值为 7 天。

    • --high-availability:使用 Disabled。 它禁用服务器的高可用性。 本教程不需要高可用性。

    • --yes:它接受 PostgreSQL 服务器的使用条款。

    注意

    如果你计划从本地工作站通过工具访问 PostgreSQL 服务器,则需要使用 az postgres flexible-server firewall-rule create 命令为工作站的 IP 地址添加防火墙规则。

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

    #!/bin/bash
    CALLER_OBJECT_ID=$(az ad signed-in-user show --query id -o tsv)
    CALLER_DISPLAY_NAME=$(az ad signed-in-user show --query userPrincipalName -o tsv)
    
  3. 使用 az postgres flexible-server ad-admin create 命令将用户帐户添加为 PostgreSQL 服务器上的 Microsoft Entra 管理员:

    #!/bin/bash
    az postgres flexible-server microsoft-entra-admin create \
      --server-name "$POSTGRES_SERVER_NAME" \
      --resource-group "$RESOURCE_GROUP_NAME" \
      --display-name "$CALLER_DISPLAY_NAME" \
      --object-id "$CALLER_OBJECT_ID" \
      --type User
    
    
  4. 使用 az postgres flexible-server firewall-rule create 命令添加一条允许 Web 应用访问 PostgreSQL 灵活服务器的规则。 在以下命令中,将服务器的防火墙配置为使用公共 IP 地址接受来自开发工作站的连接:

    MY_IP=$(curl -s ifconfig.me)
    az postgres flexible-server firewall-rule create \
      --name "$POSTGRES_SERVER_NAME" \
      --resource-group "$RESOURCE_GROUP_NAME" \
      --rule-name allow-my-ip \
      --start-ip-address "$MY_IP" \
      --end-ip-address "$MY_IP"
        ```
    
    

注意

本部分中的步骤在可突发定价层中创建具有单个 vCore 和有限内存的 PostgreSQL 服务器。 可突发层是一种较低成本的选项,适用于不需要持续使用完整 CPU 的工作负荷,并且适合本教程的要求。 对于生产工作负荷,可以升级到“常规用途”或“内存优化”定价层。 这些层提供更高的性能,但会增加成本。

若要了解详细信息,请参阅 Azure Database for PostgreSQL 灵活服务器中的 计算选项。 有关定价的信息,请参阅 Azure Database for PostgreSQL 定价

在服务器上创建数据库

至此,你就拥有了一个 PostgreSQL 服务器。 在本部分中,你将在服务器上创建一个数据库。

使用 az postgres flexible-server db create 命令来创建名为 restaurants_reviews 的数据库:

#!/bin/bash
DATABASE_NAME=restaurants_reviews
az postgres flexible-server db create \
    --resource-group $RESOURCE_GROUP_NAME \
    --server-name $POSTGRES_SERVER_NAME \
    --database-name $DATABASE_NAME

还可以使用 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 命令创建用户分配的托管标识:

UA_MANAGED_IDENTITY_NAME=<managed-identity-name> # Use a unique name for the managed identity, such as-"my-ua-managed-id".
az identity create \
    --name $UA_MANAGED_IDENTITY_NAME 
    --resource-group $RESOURCE_GROUP_NAME

在 PostgreSQL 数据库上配置托管标识

将托管标识配置为 PostgreSQL 服务器上的角色,然后向其授予对 restaurants_reviews 数据库所需的权限。 无论是使用 Azure CLI 还是 psql,都必须使用服务器实例中配置为 Microsoft Entra 管理员的用户连接到 Azure PostgreSQL 服务器。 只有被配置为 PostgreSQL 管理员的 Microsoft Entra 帐户才能在您的服务器上配置托管标识和其他 Microsoft 管理员角色。

  1. 使用 az account get-access-token 命令获取 Azure 帐户的访问令牌。 后续步骤中使用访问令牌。

    #!/bin/bash
    MY_ACCESS_TOKEN=$(az account get-access-token --resource-type oss-rdbms --output tsv --query accessToken)
    echo $MY_ACCESS_TOKEN
    
  2. 使用 az postgres flexible-server execute 命令,将用户分配的托管标识添加为 PostgreSQL 服务器上的数据库角色。

    #!/bin/bash
    az postgres flexible-server execute \
      --name "$POSTGRES_SERVER_NAME" \
      --admin-user "$CALLER_DISPLAY_NAME" \
      --admin-password "$ACCESS_TOKEN" \
      --database-name postgres \
      --querytext "SELECT * FROM pgaadauth_create_principal('$UA_MANAGED_IDENTITY_NAME', false, false);"
    

    注意

    如果在本地工作站上运行 az postgres flexible-server execute 命令,请确保为工作站的 IP 地址添加了防火墙规则。 可以使用 az postgres flexible-server firewall-rule create 命令添加规则。 对于下一步中的命令也存在相同的要求。

  3. 使用以下 az postgres flexible-server execute 命令授予用户分配的托管标识对 restaurants_reviews 数据库的必要权限:

    #!/bin/bash
    SQL_GRANTS=$(cat <<EOF
    GRANT CONNECT ON DATABASE $DATABASE_NAME TO "$UA_MANAGED_IDENTITY_NAME";
    GRANT USAGE, CREATE ON SCHEMA public TO "$UA_MANAGED_IDENTITY_NAME";
    GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO "$UA_MANAGED_IDENTITY_NAME";
    ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO "$UA_MANAGED_IDENTITY_NAME";
    EOF
    )
    
    az postgres flexible-server execute \
      --name "$POSTGRES_SERVER_NAME" \
      --admin-user "$CALLER_DISPLAY_NAME" \
      --admin-password "$MY_ACCESS_TOKEN" \
      --database-name "$DATABASE_NAME" \
      --querytext "$SQL_GRANTS"
    
    

    此 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 命令创建容器应用环境:

    #!/bin/bash
    APP_ENV_NAME=<app-env-name> # Use a unique name for the environment, such as "python-container-env".
    az containerapp env create \
    --name python-container-env \
    --resource-group $RESOURCE_GROUP_NAME \
    --location $LOCATION
    
  2. 使用 az acr credential show 命令获取 Azure 容器注册表实例的登录凭据:

    #!/bin/bash
    REGISTRY_CREDS=$(az acr credential show -n "$REGISTRY_NAME" --query "[username,passwords[0].value]" -o tsv)
    REGISTRY_USERNAME=$(echo "$REGISTRY_CREDS" | head -n1)
    REGISTRY_PASSWORD=$(echo "$REGISTRY_CREDS" | tail -n1)
    

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

  3. 使用 az identity show 命令,以获取用户分配的托管标识的客户端 ID 和资源 ID:

    UA_CLIENT_ID=$(az identity show \
        --name "$UA_MANAGED_IDENTITY_NAME" \
        --resource-group "$RESOURCE_GROUP" \
        --query clientId -o tsv)
    UA_RESOURCE_ID=$(az identity show \
        --name "$UA_MANAGED_IDENTITY_NAME" \
        --resource-group "$RESOURCE_GROUP" \
        --query id -o tsv)
    

    在步骤 5 中创建容器应用时,可以使用命令输出中的客户端 ID(GUID)值和资源 ID。 资源 ID 具有以下形式:/subscriptions/<subscription-id>/resourcegroups/pythoncontainer-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/my-ua-managed-id

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

    AZURE_SECRET_KEY=$(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 "$CONTAINER_APP_NAME" \
      --resource-group "$RESOURCE_GROUP" \
      --environment "$APP_ENV" \
      --image "$REGISTRY_NAME.azurecr.io/$IMAGE_NAME" \
      --target-port "$TARGET_PORT" \
      --ingress external \
      --registry-server "$REGISTRY_NAME.azurecr.io" \
      --registry-username "$REGISTRY_USERNAME" \
      --registry-password "$REGISTRY_PASSWORD" \
      --user-assigned "$UA_RESOURCE_ID" \
      --env-vars \
          DBHOST="$POSTGRES_SERVER_NAME" \
          DBNAME="$DATABASE_NAME" \
          DBUSER="$UA_MANAGED_IDENTITY_NAME" \
          RUNNING_IN_PRODUCTION=1 \
          AZURE_CLIENT_ID="$UA_CLIENT_ID" \
          AZURE_SECRET_KEY="$AZURE_SECRET_KEY"
        ```
    
    
  6. 仅对 Django 迁移并创建数据库架构。 (在 Flask 示例应用中,这一步将自动完成,因此你可以跳过。)

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

        az containerapp exec \
            --name $CONTAINER_APP_NAME \
            --resource-group $RESOURCE_GROUP_NAME
    

    然后,在 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 列的顺序。 按最新日志进行排序,并在 stderr 列中查找 Python stdout 消息。 Python print 输出 stdout 消息。
    • 在 Azure CLI 中,使用 az containerapp logs show 命令。
  • 如果使用 Django 框架,请检查数据库中是否存在 restaurants_reviews 表。 如果没有,请使用控制台访问容器并运行 python manage.py migrate

下一步