在 Cloud Run 上使用 Wagtail

1. 简介

894762ebb681671c.png

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. 设置和要求

自定进度的环境设置

  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. 创建模板项目

您将使用默认的 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 存储桶,您需要运行 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 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

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

c2f23d1f5b97a79a.png

9. 访问 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 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 ""

2b9139acc7208827

8ad565366c53ba3c

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

了解详情

/