本教程介绍如何构建事件驱动型流水线来帮助您自动评估文件是否包含恶意代码。
对上传到 Cloud Storage 的大量文件进行手动评估,对于大多数应用来说都非常耗时。
此流水线是使用 Google Cloud 产品连同名为 ClamAV 的开源防病毒引擎进行构建的。在本教程中,ClamAV 在托管在 Cloud Run 中的 Docker 容器中运行。流水线还将日志条目写入 Cloud Logging 并将指标记录到 Cloud Monitoring。
您可以使用这些 Logging 日志条目针对受感染的文件触发基于日志的提醒,但这些提醒的设置流程不在本教程的涵盖范围内。
在本教程中,“恶意软件”一词是一个通用术语,泛指特洛伊木马、病毒和其他恶意代码。
本教程假定您熟悉 Cloud Storage、Cloud Run、Cloud Scheduler、Eventarc、Docker 和 Node.js 的基本功能。
架构
下图概述了架构。
该架构管理两个流水线:
- 文件扫描流水线,用于检查上传的文件是否包含恶意软件。
- ClamAV 恶意软件数据库镜像更新流水线,用于维护 ClamAV 使用的恶意软件数据库的最新镜像。
文件扫描流水线
文件扫描流水线的工作方式如下:
- 最终用户将其文件上传到未经扫描的 Cloud Storage 存储桶。
- Eventarc 服务会捕获此上传事件,并告知 Cloud Run 服务系统已上传此新文件。
- Cloud Run 服务会从未经扫描的 Cloud Storage 存储桶下载该新文件,并将其传递给 ClamAV 恶意软件扫描工具。
- 根据恶意软件扫描的结果,该服务会执行以下操作之一:
- 如果 ClamAV 声明该文件干净,则系统会将其从未经扫描的 Cloud Storage 存储桶移到干净的 Cloud Storage 存储桶。
- 如果 ClamAV 声明该文件包含恶意软件,则系统会将该文件从未经扫描的 Cloud Storage 存储桶移到隔离的 Cloud Storage 存储桶。
- 该服务会将这些操作的结果报告给 Logging 和 Monitoring,以便管理员可以相应地采取措施。
ClamAV 恶意软件数据库镜像更新流水线
为了能够有效扫描,ClamAV 恶意软件扫描工具需要维护最新的恶意软件签名数据库。
系统会使用 Cloud Run(这是一项无状态服务)来运行 ClamAV 服务。在服务实例启动时,ClamAV 必须始终下载最新的完整恶意软件数据库,其大小为数百 MB。
ClamAV 的公共恶意软件数据库托管在内容分发网络 (CDN) 上,因此系统可能会对这些下载施加速率限制。如果多个实例启动并尝试下载完整数据库,则可能会触发速率限制。这会导致 Cloud Run 使用的外部 IP 地址被屏蔽 24 小时;从而导致 ClamAV 服务无法启动,也无法下载恶意软件数据库更新。
此外,Cloud Run 是使用外部 IP 地址的共享池。因此,CDN 会将来自不同项目的恶意软件扫描实例的下载操作视为来自单个地址,从而也会触发屏蔽。
此流水线会保留 Cloud Storage 中数据库的最新私有本地镜像。这可确保系统只在每次更新 ClamAV 公共数据库时访问该数据库一次以下载较小的增量更新文件,而不是下载完整数据库,从而防止发生速率限制。
此流水线的工作方式如下:
- 将一个 Cloud Scheduler 作业配置为每两个小时触发一次,这与 ClamAV freshclam 服务所使用的默认更新检查间隔相同。此作业会向 Cloud Run 服务发出 HTTP POST 请求,以指示其更新恶意软件数据库镜像。
- Cloud Run 实例会将恶意软件数据库镜像从 Cloud Storage 存储桶复制到本地文件系统。
- 然后,该实例会运行 ClamAV CVDUpdate 工具,以下载所有可用的增量更新并将其应用于数据库镜像。
- 之后,它会将更新后的恶意软件数据库镜像复制回 Cloud Storage 存储桶。
在 Cloud Run 实例中运行的 ClamAV freshclam 服务会在启动时从 Cloud Storage 下载恶意软件数据库;并且会在运行时定期检查 Cloud Storage 存储桶中是否有可用的数据库更新,如果有,则会予以下载。
目标
在 Cloud Storage 存储桶中构建 ClamAV 恶意软件定义数据库的镜像。
构建具有以下功能的 Cloud Run 服务:
- 使用 ClamAV 扫描 Cloud Storage 存储桶中的文件是否包含恶意软件,并根据扫描结果将已扫描的文件移到干净的存储桶或隔离的存储桶。
- 在 Cloud Storage 中维护 ClamAV 恶意软件定义数据库的镜像。
创建 Eventarc 触发器,以在有文件上传到 Cloud Storage 时触发恶意软件扫描服务。
创建 Cloud Scheduler 作业,以触发恶意软件扫描服务来刷新 Cloud Storage 中的恶意软件定义数据库的镜像。
费用
在本文档中,您将使用 Google Cloud 的以下收费组件:
您可使用价格计算器根据您的预计使用情况来估算费用。
完成本文档中描述的任务后,您可以通过删除所创建的资源来避免继续计费。如需了解详情,请参阅清理。
准备工作
- Sign in to your Google Cloud account. If you're new to Google Cloud, create an account to evaluate how our products perform in real-world scenarios. New customers also get $300 in free credits to run, test, and deploy workloads.
-
In the Google Cloud console, on the project selector page, select or create a Google Cloud project.
-
Make sure that billing is enabled for your Google Cloud project.
-
Enable the Artifact Registry, Cloud Run, Eventarc, Logging, Cloud Scheduler, Pub/Sub, and Cloud Build APIs.
-
In the Google Cloud console, on the project selector page, select or create a Google Cloud project.
-
Make sure that billing is enabled for your Google Cloud project.
-
Enable the Artifact Registry, Cloud Run, Eventarc, Logging, Cloud Scheduler, Pub/Sub, and Cloud Build APIs.
-
In the Google Cloud console, activate Cloud Shell.
At the bottom of the Google Cloud console, a Cloud Shell session starts and displays a command-line prompt. Cloud Shell is a shell environment with the Google Cloud CLI already installed and with values already set for your current project. It can take a few seconds for the session to initialize.
在本教程中,您将在 Cloud Shell 中运行所有命令。
设置环境
在本部分中,您需要为整个教程通篇使用的值(例如区域和地区)分配设置。在本教程中,您将使用 us-central1
作为 Cloud Run 服务的区域,使用 us
作为 Eventarc 触发器和 Cloud Storage 存储桶的位置。
在 Cloud Shell 中,设置常见的 shell 变量,包括区域和位置:
REGION=us-central1 LOCATION=us PROJECT_ID=PROJECT_ID SERVICE_NAME="malware-scanner" SERVICE_ACCOUNT="${SERVICE_NAME}@${PROJECT_ID}.iam.gserviceaccount.com"
将 PROJECT_ID 设置为您的项目 ID。
使用您的项目 ID 初始化
gcloud
环境:gcloud config set project "${PROJECT_ID}"
创建三个具有唯一名称的 Cloud Storage 存储分区:
gsutil mb -l "${LOCATION}" "gs://unscanned-${PROJECT_ID}" gsutil mb -l "${LOCATION}" "gs://quarantined-${PROJECT_ID}" gsutil mb -l "${LOCATION}" "gs://clean-${PROJECT_ID}"
${PROJECT_ID}
用于确保存储桶名称是唯一的。这三个存储桶用于在文件扫描流水线的各个阶段存放上传的文件:
unscanned-PROJECT_ID
:存放尚未经过扫描的文件。您的用户会将其文件上传到此存储桶。quarantined-PROJECT_ID
:存放恶意软件扫描工具服务已扫描并认为其包含恶意软件的文件。clean-PROJECT_ID
:存放恶意软件扫描工具服务已扫描并发现其未受感染的文件。
创建第四个 Cloud Storage 存储桶:
gsutil mb -l "${LOCATION}" "gs://cvd-mirror-${PROJECT_ID}"
${PROJECT_ID}
用于确保存储桶名称是唯一的。此存储桶
cvd-mirror-PROJECT_ID
用于维护恶意软件定义数据库的本地镜像,以防止 ClamAV CDN 触发速率限制。
为恶意软件扫描工具服务创建服务帐号并授予权限
在本部分中,您将创建一个服务帐号,供恶意软件扫描工具服务使用,并向该服务帐号授予适当的角色,使其拥有读取和写入 Cloud Storage 存储桶的权限。这是为了确保该帐号具有最小权限,并且仅访问其所需资源。
创建
malware-scanner
服务帐号:gcloud iam service-accounts create ${SERVICE_NAME}
授予存储桶的 Object Admin 角色,以允许服务从未扫描的存储桶中读取和删除文件,以及将文件写入隔离的存储桶和干净的存储桶。
gsutil iam ch \ "serviceAccount:${SERVICE_ACCOUNT}:objectAdmin" \ "gs://unscanned-${PROJECT_ID}" gsutil iam ch \ "serviceAccount:${SERVICE_ACCOUNT}:objectAdmin" \ "gs://clean-${PROJECT_ID}" gsutil iam ch \ "serviceAccount:${SERVICE_ACCOUNT}:objectAdmin" \ "gs://quarantined-${PROJECT_ID}" gsutil iam ch \ "serviceAccount:${SERVICE_ACCOUNT}:objectAdmin" \ "gs://cvd-mirror-${PROJECT_ID}"
授予 Metric Writer 角色,以允许服务将指标写入 Monitoring:
gcloud projects add-iam-policy-binding \ "${PROJECT_ID}" \ --member="serviceAccount:${SERVICE_ACCOUNT}" \ --role=roles/monitoring.metricWriter
在 Cloud Run 中创建恶意软件扫描工具服务
在本部分中,您需要将恶意软件扫描工具服务部署到 Cloud Run。该服务在 Docker 容器中运行,并包含以下内容:
Dockerfile
,用于通过服务、Node.js 运行时、GCloud SDK 和 ClamAV 二进制文件构建容器映像。- 恶意软件扫描工具 Cloud Run 服务的 Node.js 文件。
config.json
配置文件,用于指定 Cloud Storage 存储桶名称。updateCvdMirror.sh
shell 脚本,用于刷新 Cloud Storage 中的 ClamAV 恶意软件定义数据库镜像。cloud-run-proxy
服务向freshclam
代理发出的 HTTP 请求,用于提供对 Cloud Storage API 的经过身份验证的访问权限。bootstrap.sh
shell 脚本,用于在实例启动时运行必要的服务。
在 Cloud Shell 中,克隆包含代码文件的 GitHub 代码库:
git clone https://github.com/GoogleCloudPlatform/docker-clamav-malware-scanner.git
切换到
cloudrun-malware-scanner
目录:cd docker-clamav-malware-scanner/cloudrun-malware-scanner
修改
config.json
配置文件,以指定您刚刚创建的 Cloud Storage 存储桶。由于存储桶名称基于项目 ID,因此可以进行简单的搜索和替换:sed "s/-bucket-name/-${PROJECT_ID}/" config.json.tmpl > config.json
您可以查看更新后的配置文件:
cat config.json
在 Cloud Storage 中执行 ClamAV 恶意软件数据库镜像的初始填充:
python3 -m venv pyenv . pyenv/bin/activate pip3 install crcmod cvdupdate ./updateCvdMirror.sh "cvd-mirror-${PROJECT_ID}" deactivate
这将执行
CVDUpdate
工具的本地安装,使用该工具下载恶意软件数据库,然后将其上传到您先前创建的 Cloud Storage 存储桶“cvd-mirror”。您可以查看镜像的内容:
gsutil ls "gs://cvd-mirror-${PROJECT_ID}/cvds"
该存储桶应包含多个 CVD 文件(包含完整的恶意软件数据库)、多个
.cdiff
文件(包含每日增量更新)以及两个.json
文件(包含配置和状态信息)。使用先前创建的服务帐号创建和部署 Cloud Run 服务:
gcloud beta run deploy "${SERVICE_NAME}" \ --source . \ --region "${REGION}" \ --no-allow-unauthenticated \ --memory 4Gi \ --cpu 1 \ --concurrency 20 \ --min-instances 1 \ --max-instances 5 \ --no-cpu-throttling \ --cpu-boost \ --service-account="${SERVICE_ACCOUNT}"
这将创建一个具有 1 个 vCPU 并使用 4GiB RAM 的 Cloud Run 实例。虽然对于本教程该实例大小足够,但在生产环境中,您可能需要为实例选择更大的 CPU 和内存大小,并且根据该服务需要处理的流量大小,您可能还需要设置更大的
--max-instances
参数。--concurrency
参数指定每个实例可以处理的并发请求数。--no-cpu-throttling
参数允许实例在后台执行操作,例如更新恶意软件定义。--cpu-boost
参数会在实例启动时将 vCPU 数量加倍,以缩短启动延迟时间。--min-instances 1
参数至少保持一个实例处于活跃状态,因为每个实例都需要较长的时间来启动,这样可以节省启动时间。--max-instances 5
参数可防止服务过度纵向扩容。出现提示时,输入
Y
。
构建和部署过程大约需要 10 分钟时间来完成。完成后,您将看到一条消息:
Service [malware-scanner] revision [malware-scanner-NNNNN-XXX] has been deployed and is serving 100 percent of traffic. Service URL: https://malware-scanner-XXXXXXXX-XX.a.run.app
将部署命令的输出中的 Service URL
值存储在一个 shell 变量中。稍后将在创建 Cloud Scheduler 作业时使用该信息:
SERVICE_URL="SERVICE_URL"
Cloud Run 服务要求所有调用都通过身份验证,并且进行身份验证的身份需要在服务上具有 run.routes.invoke
权限。
您可以通过执行以下命令来检查正在运行的服务和 ClamAV 版本:
curl -D - -H "Authorization: Bearer $(gcloud auth print-identity-token)" \ ${SERVICE_URL}
创建 Eventarc Cloud Storage 触发器
在本部分中,您将添加权限以允许 Eventarc 捕获 Cloud Storage 事件和一个触发器,以将这些事件发送到 Cloud Run malware-scanner
服务。
如果您使用的是在 2021 年 4 月 8 日之前创建的现有项目,请为推送通知配置 Pub/Sub。
在 Cloud Shell 中,向 Cloud Storage 服务帐号授予
roles/pubsub.publisher
角色:STORAGE_SERVICE_ACCOUNT=$(gsutil kms serviceaccount -p "${PROJECT_ID}") gcloud projects add-iam-policy-binding "${PROJECT_ID}" \ --member "serviceAccount:${STORAGE_SERVICE_ACCOUNT}" \ --role "roles/pubsub.publisher"
允许
malware-scanner
服务帐号调用 Cloud Run 服务,并充当 Eventarc 事件接收器:gcloud run services add-iam-policy-binding "${SERVICE_NAME}" \ --region="${REGION}" \ --member "serviceAccount:${SERVICE_ACCOUNT}" \ --role roles/run.invoker gcloud projects add-iam-policy-binding "${PROJECT_ID}" \ --member "serviceAccount:${SERVICE_ACCOUNT}" \ --role "roles/eventarc.eventReceiver"
创建一个 Eventarc 触发器以捕获未扫描的 Cloud Storage 存储桶中的最终对象事件,并将其发送到您的 Cloud Run 服务。触发器将使用
malware-scanner
服务帐号进行身份验证:BUCKET_NAME="unscanned-${PROJECT_ID}" gcloud eventarc triggers create "trigger-${BUCKET_NAME}-${SERVICE_NAME}" \ --destination-run-service="${SERVICE_NAME}" \ --destination-run-region="${REGION}" \ --location="${LOCATION}" \ --event-filters="type=google.cloud.storage.object.v1.finalized" \ --event-filters="bucket=${BUCKET_NAME}" \ --service-account="${SERVICE_ACCOUNT}"
如果您收到以下两个错误之一,请等待一分钟,然后重新执行命令:
ERROR: (gcloud.eventarc.triggers.create) INVALID_ARGUMENT: The request was invalid: Bucket "unscanned-PROJECT_ID" was not found. Please verify that the bucket exists.
ERROR: (gcloud.eventarc.triggers.create) FAILED_PRECONDITION: Invalid resource state for "": Permission denied while using the Eventarc Service Agent. If you recently started to use Eventarc, it may take a few minutes before all necessary permissions are propagated to the Service Agent. Otherwise, verify that it has Eventarc Service Agent role.
在 Eventarc 触发器使用的底层 Pub/Sub 订阅中将消息确认时限更改为两分钟:
SUBSCRIPTION_NAME=$(gcloud eventarc triggers describe \ "trigger-${BUCKET_NAME}-${SERVICE_NAME}" \ --location="${LOCATION}" \ --format="get(transport.pubsub.subscription)") gcloud pubsub subscriptions update "${SUBSCRIPTION_NAME}" --ack-deadline=120
对于大型文件或高负载,默认值 10 秒太短。
创建 Cloud Scheduler 作业以触发 ClamAV 数据库镜像更新
创建一个 Cloud Scheduler 作业,以在 Cloud Run 服务上执行 HTTP POST 请求,并使用一个命令来更新恶意软件定义数据库的镜像。为避免过多客户端使用相同时段,ClamAV 会要求您将作业安排在 3 到 57 之间的随机分钟数,且避免 10 的倍数。
while : ; do
# set MINUTE to a random number between 3 and 57
MINUTE="$((RANDOM%55 + 3))"
# exit loop if MINUTE is not a multiple of 10
[[ $((MINUTE % 10)) != 0 ]] && break
done
gcloud scheduler jobs create http \
"${SERVICE_NAME}-mirror-update" \
--location="${REGION}" \
--schedule="${MINUTE} */2 * * *" \
--oidc-service-account-email="${SERVICE_ACCOUNT}" \
--uri="${SERVICE_URL}" \
--http-method=post \
--message-body='{"kind":"schedule#cvd_update"}' \
--headers="Content-Type=application/json"
--schedule
命令行参数使用 unix-cron 字符串格式定义作业何时运行。给定值表示作业应该每 2 小时在随机生成的特定分钟数运行一次。
此作业将仅更新 Cloud Storage 中的 ClamAV 镜像。每个 Cloud Run 实例中的 ClamAV freshclam 守护程序每 30 分钟会检查镜像一次,以查看是否有可用的新定义,如果有,则会相应更新 ClamAV 守护程序。
通过上传文件测试流水线
您可以上传一个干净(不含恶意软件)的文件和一个已受感染的文件来测试流水线。
创建示例文本文件或使用现有的干净文件来测试流水线流程。
将示例数据文件复制到未扫描的存储桶:
gsutil cp filename "gs://unscanned-${PROJECT_ID}"
将
filename
替换为干净文本文件的名称。恶意软件扫描工具服务会检查每个文件并将其移到相应的存储桶。此文件已移到干净的存储桶。给流水线几秒钟的时间来处理文件,然后检查您的干净存储桶,确认该存储桶中是否存在已处理的文件:
gsutil ls -r "gs://clean-${PROJECT_ID}"
该文件已从未扫描的存储桶中移除:
gsutil ls -r "gs://unscanned-${PROJECT_ID}"
在 Cloud Shell 中,将一个名为
eicar-infected.txt
的文件(其中包含 EICAR 标准反恶意软件测试签名)上传到未扫描的存储桶:echo -e 'X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*' \ | gsutil cp - "gs://unscanned-${PROJECT_ID}/eicar-infected.txt"
等待几秒钟,然后检查隔离的存储桶,以查看文件是否已成功通过了流水线。如果检测到被恶意软件感染的文件,则该服务还会记录 Logging 日志条目。
gsutil ls -r "gs://quarantined-${PROJECT_ID}"
该文件已从未扫描的存储桶中移除:
gsutil ls -r "gs://unscanned-${PROJECT_ID}"
测试恶意软件定义数据库更新机制
在 Cloud Shell 中,您可以通过强制运行 Cloud Scheduler 作业来触发更新检查:
gcloud scheduler jobs run "${SERVICE_NAME}-mirror-update" --location="${REGION}"
此命令的结果将仅在详细日志中显示。
监控服务
您可以使用 Cloud Logging 和 Cloud Monitoring 监控该服务。
查看详细日志
在 Google Cloud 控制台中,转到 Cloud Logging 日志浏览器页面。
如果未显示日志字段过滤条件,请点击日志字段按钮。
在日志字段过滤条件中,点击 Cloud Run 修订版本
在日志字段过滤条件的服务名称部分中,点击恶意软件扫描工具
日志查询结果将显示服务的日志,其中几行将显示您上传的 2 个文件的扫描请求和状态,例如:
Scan request for gs://unscanned-PROJECT_ID/filename, (##### bytes) scanning with clam ClamAV CLAMAV_VERSION_STRING Scan status for gs://unscanned-PROJECT_ID/filename: CLEAN (##### bytes in #### ms) ... Scan request for gs://unscanned-PROJECT_ID/eicar-infected.txt, (69 bytes) scanning with clam ClamAV CLAMAV_VERSION_STRING Scan status for gs://unscanned-PROJECT_ID/eicar-infected.txt: INFECTED stream: Eicar-Signature FOUND (69 bytes in ### ms)
您可以看到系统报告了 ClamAV 版本和恶意软件数据库签名修订版本,并且显示了受感染测试文件的恶意软件名称。
您可以使用这些日志消息来设置发现恶意软件或扫描失败时的提醒。
系统还会显示恶意软件定义镜像更新日志,如下所示:
Starting CVD Mirror update CVD Mirror update check complete. output: ...
如果镜像已更新,则会显示额外的一行:
CVD Mirror updated: DATE_TIME - INFO: Downloaded daily.cvd. Version: VERSION_INFO
freshclam 更新日志每 30 分钟显示一次,如下所示:
DATE_TIME -> Received signal: wake up DATE_TIME -> ClamAV update process started at DATE_TIME DATE_TIME -> daily.cvd database is up-to-date (version: VERSION_INFO) DATE_TIME -> main.cvd database is up-to-date (version: VERSION_INFO) DATE_TIME -> bytecode.cvd database is up-to-date (version: VERSION_INFO)
如果数据库已更新,则 freshclam 日志行将如下所示:
DATE_TIME -> daily.cld updated (version: VERSION_INFO)
查看指标
该服务会生成以下用于监控和提醒目的的指标:
- 已处理的清洁文件数:
custom.googleapis.com/opencensus/malware-scanning/clean_files
- 已处理的受感染文件数:
custom.googleapis.com/opencensus/malware-scanning/infected_files
- 扫描文件所花费的时间:
custom.googleapis.com/opencensus/malware-scanning/scan_duration
- 扫描的字节总数:
custom.googleapis.com/opencensus/malware-scanning/bytes_scanned
- 失败的恶意软件扫描次数:
custom.googleapis.com/opencensus/malware-scanning/scans_failed
- CVD 镜像更新检查次数:
custom.googleapis.com/opencensus/malware-scanning/cvd-mirror-updates
您可以在 Cloud Monitoring Metrics Explorer 中查看这些指标。
在 Google Cloud 控制台,转到 Cloud Monitoring Metrics Explorer 页面。
点击选择指标字段,然后输入过滤条件字符串
malware
。选择 OpenCensus/malware-scanning/clean_files 指标。该图表会显示一个数据点,指明何时扫描清洁文件。
指标可用于监控流水线,以及在检测到恶意软件或文件处理失败时创建提醒。
如需了解详情,您可以按各种指标标签细分指标:
- source_bucket
- destination_bucket
- clam_version
- cloud_run_revision
处理多个存储桶
恶意软件扫描工具服务能够扫描多个来源存储桶中的文件,并将这些文件发送到单独的、隔离的清洁存储桶。
虽然此高级配置不在本教程的探讨范围内,但所涉及的步骤总结如下:
创建具有唯一名称的各种未扫描、干净和隔离的 Cloud Storage 存储桶。
向各个存储桶的
malware-scanner
服务帐号授予适当的角色。修改配置文件
config.json
以指定每个配置的存储桶名称:{ "buckets": [ { "unscanned": "unscanned-bucket-1-name", "clean": "clean-bucket-1-name", "quarantined": "quarantined-bucket-1-name" }, { "unscanned": "unscanned-bucket-2-name", "clean": "clean-bucket-2-name", "quarantined": "quarantined-bucket-2-name" } ] "ClamCvdMirrorBucket": "cvd-mirror-bucket-name" }
为每个未扫描的存储桶创建一个 Eventarc 触发器,并且确保每个存储桶的触发器名称都是唯一的。
清理
为避免因本教程中使用的资源导致您的 Google Cloud 帐号产生费用,请删除包含这些资源的项目,或者保留项目但删除各个资源。
删除项目
- In the Google Cloud console, go to the Manage resources page.
- In the project list, select the project that you want to delete, and then click Delete.
- In the dialog, type the project ID, and then click Shut down to delete the project.
后续步骤
- 探索 Cloud Storage 文档。
- 探索有关 Google Cloud 的参考架构、图表和最佳做法。查看我们的 Cloud Architecture Center。