Cloud Run 上的 Django

1. 简介

894762ebb681671c.png

Cloud Run 是一个托管式计算平台,供您运行可通过 HTTP 请求调用的无状态容器。Cloud Run 采用无服务器设计:您不用管理基础架构,因此可全力专注于最重要的工作,即构建出色的应用。

它还可与 Google Cloud 生态系统的许多其他部分原生集成,包括用于代管式数据库的 Cloud SQL、用于统一对象存储的 Cloud Storage,以及用于管理 Secret 的 Secret Manager

Django 是一个高级 Python Web 框架。

在本教程中,您将使用这些组件部署一个小型 Django 项目。

注意:此 Codelab 上次通过 Django 5.0 验证。除非未来的更新有任何重大变更,否则此 Codelab 应该可以继续使用。请查看未来的 Django 版本说明。

学习内容

  • 如何使用 Cloud Shell
  • 如何创建 Cloud SQL 数据库
  • 如何创建 Cloud Storage 存储分区
  • 如何创建 Secret Manager Secret
  • 如何使用来自不同 Google Cloud 服务的 Secret
  • 如何将 Google Cloud 组件连接到 Cloud Run 服务
  • 如何使用 Container Registry 存储构建的容器
  • 如何部署到 Cloud Run
  • 如何在 Cloud Build 中运行数据库架构迁移

2. 设置和要求

自定进度的环境设置

  1. 登录 Google Cloud 控制台,然后创建一个新项目或重复使用现有项目。如果您还没有 Gmail 或 Google Workspace 账号,则必须创建一个

fbef9caa1602edd0.png

a99b7ace416376c4.png

5e3ff691252acf41.png

  • 项目名称是此项目参与者的显示名称。它是 Google API 尚未使用的字符串。您可以随时对其进行更新。
  • 项目 ID 在所有 Google Cloud 项目中是唯一的,并且是不可变的(一经设置便无法更改)。Cloud 控制台会自动生成一个唯一字符串;通常情况下,您无需关注该字符串。在大多数 Codelab 中,您都需要引用项目 ID(通常用 PROJECT_ID 标识)。如果您不喜欢生成的 ID,可以再随机生成一个 ID。或者,您也可以尝试自己的项目 ID,看看是否可用。完成此步骤后便无法更改该 ID,并且此 ID 在项目期间会一直保留。
  • 此外,还有第三个值,即部分 API 使用的项目编号,供您参考。如需详细了解所有这三个值,请参阅文档
  1. 接下来,您需要在 Cloud 控制台中启用结算功能,以便使用 Cloud 资源/API。运行此 Codelab 应该不会产生太多的费用(如果有的话)。若要关闭资源以避免产生超出本教程范围的结算费用,您可以删除自己创建的资源或删除项目。Google Cloud 新用户符合参与 300 美元免费试用计划的条件。

Google Cloud Shell

虽然您可以从笔记本电脑远程操作 Google Cloud,但在此 Codelab 中,我们将使用 Google Cloud Shell,这是一个在云端运行的命令行环境。

激活 Cloud Shell

  1. 在 Cloud Console 中,点击激活 Cloud Shell853e55310c205094.png

3c1dabeca90e44e5.png

如果这是您首次启动 Cloud Shell,系统会显示一个中间屏幕,介绍 Cloud Shell 是什么。如果系统显示中间屏幕,请点击继续

9c92662c6a846a5c.png

预配和连接到 Cloud Shell 只需花几分钟时间。

9f0e51b578fecce5.png

此虚拟机已加载所需的所有开发工具。它提供了一个持久的 5 GB 主目录,并且在 Google Cloud 中运行,大大增强了网络性能和身份验证。您在此 Codelab 中的大部分(甚至全部)工作都可以使用浏览器完成。

在连接到 Cloud Shell 后,您应该会看到自己已通过身份验证,并且相关项目已设置为您的项目 ID。

  1. 在 Cloud Shell 中运行以下命令以确认您已通过身份验证:
gcloud auth list

命令输出

 Credentialed Accounts
ACTIVE  ACCOUNT
*       <my_account>@<my_domain.com>

To set the active account, run:
    $ gcloud config set account `ACCOUNT`
  1. 在 Cloud Shell 中运行以下命令,以确认 gcloud 命令了解您的项目:
gcloud config list project

命令输出

[core]
project = <PROJECT_ID>

如果不是上述结果,您可以使用以下命令进行设置:

gcloud config set project <PROJECT_ID>

命令输出

Updated property [core/project].

3. 启用 Cloud API

在 Cloud Shell 中,为要使用的组件启用 Cloud API:

gcloud services enable \
  run.googleapis.com \
  sql-component.googleapis.com \
  sqladmin.googleapis.com \
  compute.googleapis.com \
  cloudbuild.googleapis.com \
  secretmanager.googleapis.com \
  artifactregistry.googleapis.com

由于这是您首次通过 gcloud 调用 API,因此系统会要求您授权使用您的凭据发出此请求。这将在每次 Cloud Shell 会话中发生一次。

此操作可能需要几分钟才能完成。

完成后,系统应显示类似如下内容的成功消息:

Operation "operations/acf.cc11852d-40af-47ad-9d59-477a12847c9e" finished successfully.

4. 创建模板项目

您将使用默认的 Django 项目模板作为示例 Django 项目。

如需创建此模板项目,请使用 Cloud Shell 创建一个名为 django-cloudrun 的新目录并前往此目录:

mkdir ~/django-cloudrun
cd ~/django-cloudrun

然后,将 Django 安装到临时虚拟环境中:

virtualenv venv
source venv/bin/activate
pip install Django

将已安装软件包的列表保存到 requirements.txt

pip freeze > requirements.txt

此列表应包含 Django 及其依赖项:sqlparseasgiref

然后,创建一个新的模板项目:

django-admin startproject myproject .

您将获得一个名为 manage.py 的新文件,以及一个名为 myproject 的新文件夹,其中包含多个文件,包括一个 settings.py

确认顶级文件夹的内容符合预期:

ls -F
manage.py myproject/ requirements.txt venv/

确认 myproject 文件夹的内容符合预期:

ls -F myproject/
__init__.py asgi.py settings.py  urls.py  wsgi.py

现在,您可以退出并移除临时虚拟环境:

deactivate
rm -rf venv

从此,系统将在容器内调用 Django。

5. 创建支持性服务

现在,您将创建后备服务:一个专用服务账号、一个工件注册库、一个 Cloud SQL 数据库、一个 Cloud Storage 存储分区以及一些 Secret Manager 值。

确保部署中使用的密码值的安全性对任何项目的安全性都至关重要,并可确保没有人意外将密码放置在不该放置的位置(例如,直接放入设置文件中,或直接输入到可以从历史记录中检索到密码的终端)。

首先,设置两个基本环境变量,一个用于项目 ID:

PROJECT_ID=$(gcloud config get-value core/project)

以及一个用于地区的参数:

REGION=us-central1

创建服务账号

如需限制该服务对 Google Cloud 其他部分的访问权限,请创建一个专用服务账号:

gcloud iam service-accounts create cloudrun-serviceaccount

在本 Codelab 的后续部分中,您将通过电子邮件地址引用此账号。在环境变量中设置该值:

SERVICE_ACCOUNT=$(gcloud iam service-accounts list \
    --filter cloudrun-serviceaccount --format "value(email)")

创建 Artifact Registry

如需存储构建的容器映像,请在您选择的区域中创建一个容器注册库:

gcloud artifacts repositories create containers --repository-format docker --location $REGION

您将在此 Codelab 的后续部分中按名称引用此注册表:

ARTIFACT_REGISTRY=${REGION}-docker.pkg.dev/${PROJECT_ID}/containers

创建数据库

创建 Cloud SQL 实例:

gcloud sql instances create myinstance --project $PROJECT_ID \
  --database-version POSTGRES_14 --tier db-f1-micro --region $REGION

此操作可能需要几分钟才能完成。

在该实例中,创建一个数据库:

gcloud sql databases create mydatabase --instance myinstance

在同一实例中,创建用户:

DJPASS="$(cat /dev/urandom | LC_ALL=C tr -dc 'a-zA-Z0-9' | fold -w 30 | head -n 1)"
gcloud sql users create djuser --instance myinstance --password $DJPASS

向服务账号授予连接到实例的权限:

gcloud projects add-iam-policy-binding $PROJECT_ID \
    --member serviceAccount:${SERVICE_ACCOUNT} \
    --role roles/cloudsql.client

创建存储分区

创建一个 Cloud Storage 存储分区(请注意,名称必须是全局唯一的):

GS_BUCKET_NAME=${PROJECT_ID}-media
gcloud storage buckets create gs://${GS_BUCKET_NAME} --location ${REGION} 

向服务账号授予管理存储分区的权限:

gcloud storage buckets add-iam-policy-binding gs://${GS_BUCKET_NAME} \
    --member serviceAccount:${SERVICE_ACCOUNT} \
    --role roles/storage.admin

将配置存储为 Secret

设置好后备服务后,您现在需要将这些值存储在使用 Secret Manager 保护的文件中。

借助 Secret Manager,您可以将 Secret 作为二进制 blob 或文本字符串进行存储、管理和访问。Secret 非常适合用于存储应用运行时所需的数据库密码、API 密钥或 TLS 证书等配置信息。

首先,创建一个文件,其中包含数据库连接字符串、媒体存储分区、Django 的密钥(用于对会话和令牌进行加密签名),以及用于启用调试的值:

echo DATABASE_URL=\"postgres://djuser:${DJPASS}@//cloudsql/${PROJECT_ID}:${REGION}:myinstance/mydatabase\" > .env

echo GS_BUCKET_NAME=\"${GS_BUCKET_NAME}\" >> .env

echo SECRET_KEY=\"$(cat /dev/urandom | LC_ALL=C tr -dc 'a-zA-Z0-9' | fold -w 50 | head -n 1)\" >> .env

echo DEBUG=True >> .env

然后,创建一个名为 application_settings 的 Secret,并将该文件用作 Secret:

gcloud secrets create application_settings --data-file .env

允许服务账号访问此 Secret:

gcloud secrets add-iam-policy-binding application_settings \
  --member serviceAccount:${SERVICE_ACCOUNT} --role roles/secretmanager.secretAccessor

通过列出 Secret 来确认 Secret 已创建:

gcloud secrets versions list application_settings

确认已创建 Secret 后,请移除本地文件:

rm .env

6. 配置您的应用

鉴于您刚刚创建的后备服务,您需要对模板项目进行一些更改,以使其适用。

这将包括引入 django-environ 以使用环境变量作为配置设置,您将使用定义为 Secret 的值对这些设置进行初始化。为此,您需要扩展模板设置。您还需要添加其他 Python 依赖项。

配置设置

移动 settings.py 文件,并将其重命名为 basesettings.py:

mv myproject/settings.py myproject/basesettings.py

使用 Cloud Shell Web 编辑器,使用以下代码创建新的 settings.py 文件:

touch myproject/settings.py
cloudshell edit myproject/settings.py

myproject/settings.py

import io
import os
from urllib.parse import urlparse

import environ

# Import the original settings from each template
from .basesettings import *

# Load the settings from the environment variable
env = environ.Env()
env.read_env(io.StringIO(os.environ.get("APPLICATION_SETTINGS", None)))

# Setting this value from django-environ
SECRET_KEY = env("SECRET_KEY")

# Ensure myproject is added to the installed applications
if "myproject" not in INSTALLED_APPS:
    INSTALLED_APPS.append("myproject")

# If defined, add service URLs to Django security settings
CLOUDRUN_SERVICE_URLS = env("CLOUDRUN_SERVICE_URLS", default=None)
if CLOUDRUN_SERVICE_URLS:
    CSRF_TRUSTED_ORIGINS = env("CLOUDRUN_SERVICE_URLS").split(",")
    # Remove the scheme from URLs for ALLOWED_HOSTS
    ALLOWED_HOSTS = [urlparse(url).netloc for url in CSRF_TRUSTED_ORIGINS]
else:
    ALLOWED_HOSTS = ["*"]

# Default false. True allows default landing pages to be visible
DEBUG = env("DEBUG", default=False)

# Set this value from django-environ
DATABASES = {"default": env.db()}

# Change database settings if using the Cloud SQL Auth Proxy
if os.getenv("USE_CLOUD_SQL_AUTH_PROXY", None):
    DATABASES["default"]["HOST"] = "127.0.0.1"
    DATABASES["default"]["PORT"] = 5432

# Define static storage via django-storages[google]
GS_BUCKET_NAME = env("GS_BUCKET_NAME")
STATICFILES_DIRS = []
GS_DEFAULT_ACL = "publicRead"
STORAGES = {
    "default": {
        "BACKEND": "storages.backends.gcloud.GoogleCloudStorage",
    },
    "staticfiles": {
        "BACKEND": "storages.backends.gcloud.GoogleCloudStorage",
    },
}

请花些时间阅读有关每个配置的评论。

请注意,您可能会在此文件中看到 lint 错误。这是正常现象。Cloud Shell 不了解此项目的要求背景,因此可能会报告无效的导入和未使用的导入。

Python 依赖项

找到 requirements.txt 文件,然后附加以下软件包:

cloudshell edit requirements.txt

requirements.txt(附加)

gunicorn
psycopg2-binary
django-storages[google]
django-environ

定义应用图片

Cloud Run 会运行任何符合 Cloud Run 容器合同的容器。本教程选择省略 Dockerfile,改用 Cloud Native Buildpacks。Buildpack 可帮助为常用语言(包括 Python)构建容器。

本教程选择自定义用于启动 Web 应用的 Procfile

如需将模板项目容器化,请先在项目的顶层(与 manage.py 位于同一目录中)创建一个名为 Procfile 的新文件,然后复制以下内容:

touch Procfile
cloudshell edit Procfile

Procfile

web: gunicorn --bind 0.0.0.0:$PORT --workers 1 --threads 8 --timeout 0 myproject.wsgi:application

7. 配置、构建和运行迁移步骤

如需在 Cloud SQL 数据库中创建数据库架构并使用静态资源填充 Cloud Storage 存储分区,您需要运行 migratecollectstatic

这些基本 Django 迁移命令需要在具有对数据库访问权限的构建容器映像上下文中运行。

您还需要运行 createsuperuser 来创建用于登录 Django 管理控制台的管理员账号。

为此,您将使用 Cloud Run 作业来执行这些任务。借助 Cloud Run 作业,您可以运行具有明确结束时间的进程,非常适合执行管理任务。

定义 Django 超级用户密码

如需创建超级用户,您将使用非交互式版本的 createsuperuser 命令。此命令需要使用一个特殊命名的环境变量来代替输入密码的提示。

使用随机生成的密码创建新的 Secret:

echo -n $(cat /dev/urandom | LC_ALL=C tr -dc 'a-zA-Z0-9' | fold -w 30 | head -n 1) | gcloud secrets create django_superuser_password --data-file=-

允许您的服务账号访问此 Secret:

gcloud secrets add-iam-policy-binding django_superuser_password \
  --member serviceAccount:${SERVICE_ACCOUNT} \
  --role roles/secretmanager.secretAccessor

更新您的个人资料

为了让 Cloud Run 作业更清晰,请在 Procfile 中创建快捷方式,将以下入口点附加到 Procfile

migrate: python manage.py migrate && python manage.py collectstatic --noinput --clear
createuser: python manage.py createsuperuser --username admin --email noop@example.com --noinput

现在,您应该有三个条目:默认的 web 入口点、用于应用数据库迁移的 migrate 入口点,以及用于运行 createsuperuser 命令的 createuser 入口点。

构建应用映像

更新 Procfile 后,构建映像:

gcloud builds submit --pack image=${ARTIFACT_REGISTRY}/myimage

创建 Cloud Run 作业

现在,该映像已存在,您可以使用它创建 Cloud Run 作业。

这些作业使用之前构建的映像,但使用不同的 command 值。这些值会映射到 Procfile 中的值。

为迁移创建作业:

gcloud run jobs create migrate \
  --region $REGION \
  --image ${ARTIFACT_REGISTRY}/myimage \
  --set-cloudsql-instances ${PROJECT_ID}:${REGION}:myinstance \
  --set-secrets APPLICATION_SETTINGS=application_settings:latest \
  --service-account $SERVICE_ACCOUNT \
  --command migrate

为用户创建创建作业:

gcloud run jobs create createuser \
  --region $REGION \
  --image ${ARTIFACT_REGISTRY}/myimage \
  --set-cloudsql-instances ${PROJECT_ID}:${REGION}:myinstance \
  --set-secrets APPLICATION_SETTINGS=application_settings:latest \
  --set-secrets DJANGO_SUPERUSER_PASSWORD=django_superuser_password:latest \
  --service-account $SERVICE_ACCOUNT \
  --command createuser

执行 Cloud Run 作业

完成作业配置后,运行迁移:

gcloud run jobs execute migrate --region $REGION --wait

确保此命令输出显示执行“已成功完成”。

您稍后在更新应用时将运行此命令。

完成数据库设置后,使用作业创建用户:

gcloud run jobs execute createuser --region $REGION --wait

确保此命令输出显示执行“已成功完成”。

您无需再次运行此命令。

8. 部署到 Cloud Run

创建并填充支持性服务后,您现在可以创建 Cloud Run 服务来访问这些服务。

使用以下命令,使用您之前构建的映像将服务部署到 Cloud Run:

gcloud run deploy django-cloudrun \
  --region $REGION \
  --image ${ARTIFACT_REGISTRY}/myimage \
  --set-cloudsql-instances ${PROJECT_ID}:${REGION}:myinstance \
  --set-secrets APPLICATION_SETTINGS=application_settings:latest \
  --service-account $SERVICE_ACCOUNT \
  --allow-unauthenticated

成功部署后,命令行中便会显示该服务的网址:

Service [django-cloudrun] revision [django-cloudrun-00001-...] has been deployed and is serving 100 percent of traffic.
Service URL: https://django-cloudrun-...run.app

现在,您可以在网络浏览器中打开以下网址,访问您部署的容器:

d2dfaf668baabfcc.png

9. 访问 Django 管理界面

Django 的主要功能之一就是其交互式管理界面。

更新 CSRF 设置

Django 包含防范跨站请求伪造 (CSRF) 的保护措施。每当您在 Django 网站上提交表单(包括登录 Django 管理界面)时,系统都会检查“受信任的来源”设置。如果不匹配请求的来源,Django 会返回错误。

mysite/settings.py 文件中,如果定义了 CLOUDRUN_SERVICE_URL 环境变量,则会在 CSRF_TRUSTED_ORIGINSALLOWED_HOSTS 设置中使用该变量。虽然定义 ALLOWED_HOSTS 并非强制性要求,但最好添加此项,因为 CSRF_TRUSTED_ORIGINS 已要求这样做。

由于您需要服务网址,因此只有在首次部署后才能添加此配置。

您必须更新服务才能添加此环境变量。您可以将其添加到 application_settings Secret,也可以直接将其添加为环境变量。

以下实现利用了 gcloud 格式设置escaping功能。

检索您的服务网址:

CLOUDRUN_SERVICE_URLS=$(gcloud run services describe django-cloudrun \
  --region $REGION  \
  --format "value(metadata.annotations[\"run.googleapis.com/urls\"])" | tr -d '"[]')
echo $CLOUDRUN_SERVICE_URLS

将此值作为环境变量设置在 Cloud Run 服务上:

gcloud run services update django-cloudrun \
  --region $REGION \
  --update-env-vars "^##^CLOUDRUN_SERVICE_URLS=$CLOUDRUN_SERVICE_URLS"

登录 Django 管理控制台

如需访问 Django 管理界面,请将 /admin 附加到服务网址。

现在,使用用户名“admin”登录,然后使用以下命令检索您的密码:

gcloud secrets versions access latest --secret django_superuser_password && echo ""

678cd382b7039769.png

de755ef7a1779dc6.png

10. 开发应用

在开发应用时,您需要在本地对其进行测试。为此,您需要连接到 Cloud SQL(“生产”)数据库或本地(“测试”)数据库。

连接到生产数据库

您可以使用 Cloud SQL Auth 代理连接到 Cloud SQL 实例。此应用会在您的本地机器与数据库之间建立连接。

安装 Cloud SQL Auth 代理后,请按以下步骤操作:

# Create a virtualenv
virtualenv venv
source venv/bin/activate
pip install -r requirements.txt

# Copy the application settings to your local machine
gcloud secrets versions access latest --secret application_settings > temp_settings

# Run the Cloud SQL Auth Proxy
./cloud-sql-proxy ${PROJECT_ID}:${REGION}:myinstance

# In a new tab, start the local web server using these new settings
USE_CLOUD_SQL_AUTH_PROXY=true APPLICATION_SETTINGS=$(cat temp_settings) python manage.py runserver

请务必在完成工作后移除 temp_settings 文件。

连接到本地 SQLite 数据库

或者,您也可以在开发应用时使用本地数据库。Django 同时支持 PostgreSQL 和 SQLite 数据库,PostgreSQL 具有 SQLite 不具备的一些功能,但在许多情况下,两者的功能是相同的。

如需设置 SQLite,您必须更新应用设置以指向本地数据库,然后应用架构迁移。

如需设置此方法,请执行以下操作:

# Create a virtualenv
virtualenv venv
source venv/bin/activate
pip install -r requirements.txt

# Copy the application settings to your local machine
gcloud secrets versions access latest --secret application_settings > temp_settings

# Edit the DATABASE_URL setting to use a local sqlite file. For example:
DATABASE_URL=sqlite:////tmp/my-tmp-sqlite.db

# Set the updated settings as an environment variable
APPLICATION_SETTINGS=$(cat temp_settings) 

# Apply migrations to the local database
python manage.py migrate

# Start the local web server
python manage.py runserver

请务必在完成工作后移除 temp_settings 文件。

创建迁移

更改数据库模型时,您可能需要通过运行 python manage.py makemigrations 来生成 Django 的迁移文件。

设置生产或测试数据库连接后,您可以运行此命令。或者,您也可以通过提供空设置来生成不含数据库的迁移文件:

SECRET_KEY="" DATABASE_URL="" GS_BUCKET_NAME="" python manage.py makemigrations

应用更新

如需对应用应用更改,您需要执行以下操作:

  • 将更改构建为新映像,
  • 应用任何数据库或静态迁移,然后
  • 更新 Cloud Run 服务以使用新映像。

如需构建映像,请执行以下操作:

gcloud builds submit --pack image=${ARTIFACT_REGISTRY}/myimage

如果您有任何要应用的迁移,请运行 Cloud Run 作业:

gcloud run jobs execute migrate --region $REGION --wait

如需使用新映像更新服务,请执行以下操作:

gcloud run services update django-cloudrun \
  --region $REGION \
  --image ${ARTIFACT_REGISTRY}/myimage

11. 恭喜!

您刚刚将一个复杂的项目部署到了 Cloud Run!

  • Cloud Run 可以对您的容器映像进行自动横向扩容以处理收到的请求,并在需求减少时缩容。您只需为在处理请求期间消耗的 CPU、内存和网络付费。
  • 借助 Cloud SQL,您可以预配一个由系统自动维护的托管式 PostgreSQL 实例,并将其原生集成到许多 Google Cloud 系统中。
  • 借助 Cloud Storage,您可以以可在 Django 中无缝访问的方式使用云存储空间。
  • 借助 Secret Manager,您可以存储密文,并允许 Google Cloud 的某些部分访问这些密文,而其他部分则无法访问。

清理

为避免因本教程中使用的资源导致您的 Google Cloud Platform 账号产生费用,请执行以下操作:

  • 在 Cloud Console 中,转到管理资源页面。
  • 在项目列表中,选择您的项目,然后点击删除
  • 在对话框中输入项目 ID,然后点击关停以删除项目。

了解详情