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

3c1dabeca90e44e5

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

9c92662c6a846a5c

预配和连接到 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. 创建支持性服务

现在,您将创建支持性服务:专用服务账号、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

如需存储构建的容器映像,请在所选区域中创建一个 Container 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,并将该文件用作 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 格式设置转义功能。

检索您的服务网址:

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

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 --instances=${PROJECT_ID}:${REGION}:myinstance=tcp:5432

# 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,您可以存储 Secret,并让 Google Cloud 的某些部分(而非其他部分)可以访问 Secret。

清理

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

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

了解详情