1. 简介
Cloud Run 是一个代管式计算平台,供您运行可通过 HTTP 请求调用的无状态容器。Cloud Run 采用无服务器设计:您不用管理基础架构,因此可全力专注于最重要的工作,即构建出色的应用。
它还可与 Google Cloud 生态系统的许多其他部分原生集成,包括用于代管式数据库的 Cloud SQL、用于统一对象存储的 Cloud Storage,以及用于管理 Secret 的 Secret Manager。
Wagtail 是基于 Django 构建的开源内容管理系统 (CMS)。Django 是一个高级 Python Web 框架。
在本教程中,您将使用这些组件部署一个小型 Wagtail 项目。
注意:此 Codelab 上次验证是使用 Wagtail 5.2.2 验证的,该版本支持 Django 5。
学习内容
- 如何使用 Cloud Shell
- 如何创建 Cloud SQL 数据库
- 如何创建 Cloud Storage 存储桶
- 如何创建 Secret Manager Secret
- 如何使用不同 Google Cloud 服务中的 Secret
- 如何将 Google Cloud 组件连接到 Cloud Run 服务
- 如何使用 Container Registry 存储构建的容器
- 如何部署到 Cloud Run
- 如何在 Cloud Build 中运行数据库架构迁移
2. 设置和要求
自定进度的环境设置
- 登录 Google Cloud 控制台,然后创建一个新项目或重复使用现有项目。如果您还没有 Gmail 或 Google Workspace 账号,则必须创建一个。
- 项目名称是此项目参与者的显示名称。它是 Google API 尚未使用的字符串。您可以随时对其进行更新。
- 项目 ID 在所有 Google Cloud 项目中是唯一的,并且是不可变的(一经设置便无法更改)。Cloud 控制台会自动生成一个唯一字符串;通常情况下,您无需关注该字符串。在大多数 Codelab 中,您都需要引用项目 ID(通常用
PROJECT_ID
标识)。如果您不喜欢生成的 ID,可以再随机生成一个 ID。或者,您也可以尝试自己的项目 ID,看看是否可用。完成此步骤后便无法更改该 ID,并且此 ID 在项目期间会一直保留。 - 此外,还有第三个值,即部分 API 使用的项目编号,供您参考。如需详细了解所有这三个值,请参阅文档。
- 接下来,您需要在 Cloud 控制台中启用结算功能,以便使用 Cloud 资源/API。运行此 Codelab 应该不会产生太多的费用(如果有的话)。若要关闭资源以避免产生超出本教程范围的结算费用,您可以删除自己创建的资源或删除项目。Google Cloud 新用户符合参与 300 美元免费试用计划的条件。
Google Cloud Shell
虽然您可以从笔记本电脑远程操作 Google Cloud,但在此 Codelab 中,我们将使用 Google Cloud Shell,这是一个在云端运行的命令行环境。
激活 Cloud Shell
- 在 Cloud Console 中,点击激活 Cloud Shell。
如果这是您首次启动 Cloud Shell,系统会显示一个中间屏幕,介绍 Cloud Shell 是什么。如果系统显示中间屏幕,请点击继续。
预配和连接到 Cloud Shell 只需花几分钟时间。
此虚拟机已加载所需的所有开发工具。它提供了一个持久的 5 GB 主目录,并且在 Google Cloud 中运行,大大增强了网络性能和身份验证。您在此 Codelab 中的大部分(即使不是全部)工作都可以通过浏览器完成。
在连接到 Cloud Shell 后,您应该会看到自己已通过身份验证,并且相关项目已设为您的项目 ID。
- 在 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`
- 在 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. 创建模板项目
您将使用默认的 Wagtail 项目模板作为示例 Wagtail 项目。为此,您需要暂时安装 Wagtail 来生成模板。
如需创建此模板项目,请使用 Cloud Shell 创建一个名为 wagtail-cloudrun
的新目录并前往此目录:
mkdir ~/wagtail-cloudrun cd ~/wagtail-cloudrun
然后,将 Wagtail 安装到临时虚拟环境中:
virtualenv venv source venv/bin/activate pip install wagtail
然后,在当前文件夹中创建一个新的模板项目:
wagtail start myproject .
现在,您的当前文件夹中将有一个模板 Wagtail 项目:
ls -F
Dockerfile home/ manage.py* myproject/ requirements.txt search/ venv/
您现在可以退出并移除临时虚拟环境:
deactivate rm -rf venv
从这里,系统会在容器内调用 Wagtail。
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
由于存储在此存储分区中的对象将具有不同的来源(存储分区网址而非 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 或文本字符串进行存储、管理和访问。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. 配置应用
现在,您需要对之前创建的模板项目进行一些更改。这些更改将降低 Wagtail 附带的模板设置配置的复杂性,并将 Wagtail 与您之前创建的支持性服务集成。
配置设置
在主 myproject
文件夹中找到生成的 base.py
设置文件,并将其重命名为 basesettings.py
:
mv myproject/settings/base.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 不了解此项目的要求上下文,因此可能会报告无效导入和未使用的导入。
然后,移除旧的设置文件夹。
rm -rf myproject/settings/
然后,您将拥有两个设置文件:一个来自 Wagtail,另一个是您刚刚创建的,基于这些设置构建而成:
ls myproject/*settings*
myproject/basesettings.py myproject/settings.py
最后,打开 manage.py
设置文件,并更新配置,以指示 Wagtail 指向主 settings.py
文件。
cloudshell edit manage.py
manage.py 行(之前)
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "myproject.settings.dev")
manage.py 行(之后)
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "myproject.settings")
对 myproject/wsgi.py
文件进行相同的配置更改:
cloudshell edit myproject/wsgi.py
myproject/wsgi.py 行(之前)
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "myproject.settings.dev")
myproject/wsgi.py 行(之后)
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "myproject.settings")
移除自动创建的 Dockerfile:
rm Dockerfile
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 存储桶,您需要运行 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
更新您的个人资料
为了让 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 wagtail-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 [wagtail-cloudrun] revision [wagtail-cloudrun-00001-...] has been deployed and is serving 100 percent of traffic. Service URL: https://wagtail-cloudrun-...run.app
现在,您可以在网络浏览器中打开以下网址,访问您部署的容器:
9. 访问 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 中,也可以作为环境变量直接添加。
检索您的服务网址:
CLOUDRUN_SERVICE_URLS=$(gcloud run services describe wagtail-cloudrun \ --region $REGION \ --format "value(metadata.annotations[\"run.googleapis.com/urls\"])" | tr -d '"[]') echo $CLOUDRUN_SERVICE_URLS
在 Cloud Run 服务中将此值设置为环境变量:
gcloud run services update wagtail-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 ""
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 wagtail-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,然后点击关停以删除项目。
了解详情
- Cloud Run 上的 Django:https://cloud.google.com/python/django/run
- 使用 Python 实现 Cloud Run Hello 示例:https://codelabs.developers.google.com/codelabs/cloud-run-hello-python3
- Google Cloud 上的 Python:https://cloud.google.com/python
- Google Cloud Python 客户端:https://github.com/googleapis/google-cloud-python
/