Cloud Run 上的 Django CMS

Cloud Run 上的 Django CMS

关于此 Codelab

subject上次更新时间:1月 31, 2024
account_circleKatie McLaughlin 编写

1. 简介

894762ebb681671c.png

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

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

Django CMS 是基于 Django 构建的企业内容管理系统 (CMS)。Django 是一个高级 Python Web 框架。

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

注意:此 Codelab 上次验证是使用 Django CMS 3.11.4django-cms/cms-template v3.11

学习内容

  • 如何使用 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,系统会显示一个中间屏幕,说明它是什么。如果您看到中间屏幕,请点击继续

9c92662c6a846a5c

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

9f0e51b578fecce5

这个虚拟机装有所需的所有开发工具。它提供了一个持久的 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 CMS cms-template 作为示例 Django CMS 项目。

如需创建此模板项目,请使用 Cloud Shell 创建一个名为 djangocms-cloudrun 的新目录并导航到该目录:

mkdir ~/djangocms-cloudrun
cd ~/djangocms-cloudrun

将 django-cms 软件包安装到临时虚拟环境中:

virtualenv venv
source venv/bin/activate
pip install djangocms-frontend\[cms-3]

创建 cms-template 项目的副本:

django-admin startproject --template https://github.com/django-cms/cms-template/archive/3.11.zip myproject .

现在,名为 myproject 的文件夹中有一个模板 Django CMS 项目:

ls -F
manage.py*  media/  myproject/  project.db  requirements.in requirements.txt  static/ venv/

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

deactivate
rm -rf venv

从此处,将在容器内调用 Django CMS。

您也可以移除自动复制的 requirements.in 文件(供 pip-tools 生成 requirements.txt 文件)。此 Codelab 不会用到它:

rm requirements.in

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

由于存储在此存储分区中的对象将具有不同的来源(存储分区网址而非 Cloud Run 网址),因此您需要配置跨域资源共享 (CORS) 设置。

新建一个名为 cors.json 的文件,其中包含以下内容:

touch cors.json
cloudshell edit cors.json

cors.json

[
   
{
     
"origin": ["*"],
     
"responseHeader": ["Content-Type"],
     
"method": ["GET"],
     
"maxAgeSeconds": 3600
   
}
]

将此 CORS 配置应用于新创建的存储分区:

gsutil cors set cors.json gs://$GS_BUCKET_NAME

将配置存储为 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

列出密钥,确认已创建密钥:

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 URL to Django security settings
CLOUDRUN_SERVICE_URL
= env("CLOUDRUN_SERVICE_URL", default=None)
if CLOUDRUN_SERVICE_URL:
    ALLOWED_HOSTS
= [urlparse(CLOUDRUN_SERVICE_URL).netloc]
    CSRF_TRUSTED_ORIGINS
= [CLOUDRUN_SERVICE_URL]
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
= []
DEFAULT_FILE_STORAGE
= "storages.backends.gcloud.GoogleCloudStorage"
STATICFILES_STORAGE
= "storages.backends.gcloud.GoogleCloudStorage"
GS_DEFAULT_ACL
= "publicRead"

请花些时间阅读针对每种配置添加的注释。

请注意,您可能会在此文件中看到执行 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 Buildpack。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

更新 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

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

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

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

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

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

您无需再次运行此命令。

8. 部署到 Cloud Run

创建并填充后备服务后,您现在可以创建 Cloud Run 服务来访问它们。

使用以下命令创建容器化应用到 Cloud Run 的初始部署:

gcloud run deploy djangocms-cloudrun \
  --platform managed \
  --region $REGION \
  --image gcr.io/${PROJECT_ID}/myimage \
  --set-cloudsql-instances ${PROJECT_ID}:${REGION}:myinstance \
  --set-secrets APPLICATION_SETTINGS=application_settings:latest \
  --service-account $SERVICE_ACCOUNT \
  --allow-unauthenticated

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

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

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

e54d1a1486427431.png

由于是首次安装,系统会自动将您重定向至登录页面。

9. 访问 Django 管理

Django CMS 的主要功能之一是交互式管理。

更新 CSRF 设置

Django 包含针对跨站请求伪造 (CSRF) 的保护措施。每当您的 Django 站点提交表单(包括登录到 Django 管理员)时,就会选中“受信任的源”设置。如果它与请求的来源不匹配,Django 将返回错误。

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

由于您需要服务网址,因此在首次部署之前无法添加此配置。

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

检索您的服务网址:

CLOUDRUN_SERVICE_URL=$(gcloud run services describe djangocms-cloudrun \
  --platform managed \
  --region $REGION  \
  --format "value(status.url)")
echo $CLOUDRUN_SERVICE_URL

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

gcloud run services update djangocms-cloudrun \
  --region $REGION \
  --update-env-vars CLOUDRUN_SERVICE_URL=$CLOUDRUN_SERVICE_URL

登录 Django 管理员

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

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

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

13d77046e2e7427

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 djangocms-cloudrun \
  --platform managed \
  --region $REGION \
  --image gcr.io/${PROJECT_ID}/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,然后点击关停以删除项目。

了解详情