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.pnga99b7ace416376c4.png5e3ff691252acf41.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,系统会显示一个介绍其功能的过渡页面。如果您看到了过渡页面,请点击继续。

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 及其依赖项:sqlparse 和 asgiref。

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

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. 创建支持性服务

现在,您将创建后端服务:专用服务账号、Artifact Registry、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 或文本字符串进行存储、管理和访问。它非常适合用于存储应用运行时所需的数据库密码、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:

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 网络编辑器创建一个新的 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 存储桶,您需要运行 migrate 和 collectstatic。

这些基本的 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

更新 Procfile

为了让 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

确保此命令输出显示执行“successfully completed”。

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

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

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

确保此命令输出显示执行“successfully completed”。

您无需再次运行此命令。

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_ORIGINS 和 ALLOWED_HOSTS 设置中使用该变量。虽然定义 ALLOWED_HOSTS 不是强制性的,但最好添加此属性,因为 CSRF_TRUSTED_ORIGINS 已经要求这样做。

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

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

以下实现利用了 gcloud 的格式设置和转义功能。

检索服务网址:

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.pngde755ef7a1779dc6.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,然后点击关停以删除项目。

了解详情