1. 概览
在此 Codelab 中,您将创建一个新的 Cloud Run 服务,即拼贴服务,该服务将由 Cloud Scheduler 定期触发。该服务会提取最新上传的图片并为这些照片创建拼贴:它会在 Cloud Firestore 中找到近期图片的列表,然后从 Cloud Storage 下载实际的图片文件。

学习内容
- Cloud Run
- Cloud Scheduler
- Cloud Storage
- Cloud Firestore
2. 设置和要求
自定进度的环境设置
- 登录 Google Cloud 控制台,然后创建一个新项目或重复使用现有项目。如果您还没有 Gmail 或 Google Workspace 账号,则必须创建一个。



- 项目名称是此项目参与者的显示名称。它是 Google API 尚未使用的字符串,您可以随时对其进行更新。
- 项目 ID 在所有 Google Cloud 项目中必须是唯一的,并且不可变(一经设置便无法更改)。Cloud Console 会自动生成一个唯一字符串;通常情况下,您无需关注该字符串。在大多数 Codelab 中,您都需要引用项目 ID(它通常标识为
PROJECT_ID),因此如果您不喜欢某个 ID,请再生成一个随机 ID,还可以尝试自己创建一个,并确认是否可用。然后,项目创建后,ID 会处于“冻结”状态。 - 第三个值是一些 API 使用的项目编号。如需详细了解所有这三个值,请参阅文档。
- 接下来,您需要在 Cloud Console 中启用结算功能,才能使用 Cloud 资源/API。运行此 Codelab 应该不会产生太多的费用(如果有费用的话)。要关闭资源以避免产生超出本教程范围的费用,请按照此 Codelab 末尾提供的任何“清理”说明操作。Google Cloud 的新用户符合参与 $300 USD 免费试用计划的条件。
启动 Cloud Shell
虽然可以通过笔记本电脑对 Google Cloud 进行远程操作,但在此 Codelab 中,您将使用 Google Cloud Shell,这是一个在云端运行的命令行环境。
在 GCP 控制台中,点击右上角工具栏上的 Cloud Shell 图标:

预配和连接到环境应该只需要片刻时间。完成后,您应该会看到如下内容:

这个虚拟机已加载了您需要的所有开发工具。它提供了一个持久的 5GB 主目录,并且在 Google Cloud 中运行,大大增强了网络性能和身份验证功能。只需一个浏览器,即可完成本实验中的所有工作。
3. 启用 API
您需要使用 Cloud Scheduler 定期触发 Cloud Run 服务。确保其已启用:
gcloud services enable cloudscheduler.googleapis.com
您应该会看到操作成功完成:
Operation "operations/acf.5c5ef4f6-f734-455d-b2f0-ee70b5a17322" finished successfully.
4. 克隆代码
如果之前的 Codelab 中尚未克隆该代码,请执行以下操作:
git clone https://github.com/GoogleCloudPlatform/serverless-photosharing-workshop
然后,您可以转到包含该服务的目录:
cd serverless-photosharing-workshop/services/collage/nodejs
该服务的文件布局如下:
services
|
├── collage
|
├── nodejs
|
├── Dockerfile
├── index.js
├── package.json
该文件夹中包含 3 个文件:
index.js包含 Node.js 代码package.json定义库依赖项Dockerfile定义容器映像
5. 探索代码
依赖项
package.json 文件定义了所需的库依赖项:
{
"name": "collage_service",
"version": "0.0.1",
"main": "index.js",
"scripts": {
"start": "node index.js"
},
"dependencies": {
"bluebird": "^3.7.2",
"express": "^4.17.1",
"imagemagick": "^0.1.3",
"@google-cloud/firestore": "^4.9.9",
"@google-cloud/storage": "^5.8.3"
}
}
我们依赖 Cloud Storage 库来读取和保存 Cloud Storage 中的图片文件。我们声明依赖于 Cloud Firestore 来提取之前存储的照片元数据。Express 是一种 JavaScript / 节点 Web 框架。Bluebird 用于处理 promise,而 imagemagick 是一个用于处理图片的库。
Dockerfile
Dockerfile 定义应用的容器映像:
FROM node:14-slim
# installing Imagemagick
RUN set -ex; \
apt-get -y update; \
apt-get -y install imagemagick; \
rm -rf /var/lib/apt/lists/*
WORKDIR /picadaily/services/collage
COPY package*.json ./
RUN npm install --production
COPY . .
CMD [ "npm", "start" ]
我们使用的是轻量级 Node 14 基础映像。我们正在安装 imagemagick 库。然后,我们安装代码所需的 NPM 模块,并使用 npm start 运行节点代码。
index.js
我们来详细了解一下 index.js 代码:
const express = require('express');
const imageMagick = require('imagemagick');
const Promise = require("bluebird");
const path = require('path');
const {Storage} = require('@google-cloud/storage');
const Firestore = require('@google-cloud/firestore');
我们需要程序运行所需的各种依赖项:Express 是我们要使用的节点 Web 框架,ImageMagick 是用于处理图片操作的库,Bluebird 是一个用于处理 JavaScript promise 的库,Path 用于处理文件和目录路径,然后 Storage 和 Firestore 分别用于处理 Google Cloud Storage(我们的图片存储分区),以及 Cloud Firestore 数据存储区。
const app = express();
app.get('/', async (req, res) => {
try {
console.log('Collage request');
/* ... */
} catch (err) {
console.log(`Error: creating the collage: ${err}`);
console.error(err);
res.status(500).send(err);
}
});
上面我们有 Node 处理程序的结构:我们的应用响应 HTTP GET 请求。我们会进行一些错误处理,以防出现问题。现在,我们来看看这个结构内部是什么。
const thumbnailFiles = [];
const pictureStore = new Firestore().collection('pictures');
const snapshot = await pictureStore
.where('thumbnail', '==', true)
.orderBy('created', 'desc')
.limit(4).get();
if (snapshot.empty) {
console.log('Empty collection, no collage to make');
res.status(204).send("No collage created.");
} else {
/* ... */
}
我们的拼图服务至少需要四张图片(已生成缩略图),因此请务必先上传 4 张图片。
我们从 Cloud Firerstore 中存储的元数据中检索用户上传的 4 张最新照片。我们检查生成的集合是否为空,然后在代码的 else 分支中继续操作。
让我们来收集文件名列表:
snapshot.forEach(doc => {
thumbnailFiles.push(doc.id);
});
console.log(`Picture file names: ${JSON.stringify(thumbnailFiles)}`);
我们将从缩略图存储分区下载其中的每个文件,其名称来自我们在部署时设置的环境变量:
const thumbBucket = storage.bucket(process.env.BUCKET_THUMBNAILS);
await Promise.all(thumbnailFiles.map(async fileName => {
const filePath = path.resolve('/tmp', fileName);
await thumbBucket.file(fileName).download({
destination: filePath
});
}));
console.log('Downloaded all thumbnails');
上传最新缩略图后,我们将使用 ImageMagick 库创建这些缩略图的 4x4 网格。我们使用 Bluebird 库及其 Promise 实现将回调驱动的代码转换为适用于 async / await 的代码,然后等待生成图片拼贴的 promise:
const collagePath = path.resolve('/tmp', 'collage.png');
const thumbnailPaths = thumbnailFiles.map(f => path.resolve('/tmp', f));
const convert = Promise.promisify(im.convert);
await convert([
'(', ...thumbnailPaths.slice(0, 2), '+append', ')',
'(', ...thumbnailPaths.slice(2), '+append', ')',
'-size', '400x400', 'xc:none', '-background', 'none', '-append',
collagePath]);
console.log("Created local collage picture");
由于拼贴图片已保存到本地磁盘的临时文件夹中,我们现在需要将其上传到 Cloud Storage,然后返回成功响应(状态代码 2xx):
await thumbBucket.upload(collagePath);
console.log("Uploaded collage to Cloud Storage bucket ${process.env.BUCKET_THUMBNAILS}");
res.status(204).send("Collage created.");
现在,让我们的 Node 脚本监听传入请求:
const PORT = process.env.PORT || 8080;
app.listen(PORT, () => {
console.log(`Started collage service on port ${PORT}`);
});
在源文件末尾,我们有指示让 Express 在 8080 默认端口上实际启动我们的 Web 应用。
6. 在本地测试
在将代码部署到云端之前,请在本地测试代码以确保它可以正常运行。
在 collage/nodejs 文件夹内,安装 npm 依赖项并启动服务器:
npm install; npm start
如果一切顺利,它应该会在端口 8080 上启动服务器:
Started collage service on port 8080
使用 CTRL-C 退出。
7. 构建并部署到 Cloud Run
在部署到 Cloud Run 之前,请将 Cloud Run 区域设置为其中一个受支持的区域和平台:managed:
gcloud config set run/region europe-west1 gcloud config set run/platform managed
您可以检查是否已设置配置:
gcloud config list ... [run] platform = managed region = europe-west1
您还可以依靠 Cloud Run 使用 Google Cloud Buildpack 为您构建容器映像,而无需使用 Cloud Build 手动构建和发布容器映像。
运行以下命令以构建容器映像:
BUCKET_THUMBNAILS=thumbnails-$GOOGLE_CLOUD_PROJECT
SERVICE_NAME=collage-service
gcloud run deploy $SERVICE_NAME \
--source . \
--no-allow-unauthenticated \
--update-env-vars BUCKET_THUMBNAILS=$BUCKET_THUMBNAILS
请注意 –-source 标志。这是 Cloud Run 中基于源代码的部署 。如果源代码目录中存在 Dockerfile,则上传的源代码将使用该 Dockerfile 构建。如果源代码目录中没有 Dockerfile,Google Cloud Buildpacks 会使用由 Google 管理的安全基础映像,自动检测您使用的语言并提取代码的依赖项,以创建可用于生产环境的容器映像。这会标记 Cloud Run 以使用 Google Cloud Buildpack 构建 Dockerfile 中定义的容器映像。
另请注意,基于源代码的部署使用 Artifact Registry 来存储构建的容器。Artifact Registry 是 Google Container Registry 的现代版本。CLI 会提示您启用该 API(如果项目中尚未启用该 API),并在要部署到的区域中创建名为 cloud-run-source-deploy 的代码库。
--no-allow-unauthenticated 标志将 Cloud Run 服务设置为只能由特定服务账号触发的内部服务。
8. 设置 Cloud Scheduler
现在,Cloud Run 服务已准备就绪并已部署,接下来可以创建定期时间表,以每分钟调用一次服务。
创建服务账号:
SERVICE_ACCOUNT=collage-scheduler-sa gcloud iam service-accounts create $SERVICE_ACCOUNT \ --display-name "Collage Scheduler Service Account"
向服务账号授予调用 Cloud Run 服务的权限:
gcloud run services add-iam-policy-binding $SERVICE_NAME \ --member=serviceAccount:$SERVICE_ACCOUNT@$GOOGLE_CLOUD_PROJECT.iam.gserviceaccount.com \ --role=roles/run.invoker
创建一个每 1 分钟执行的 Cloud Scheduler 作业:
SERVICE_URL=$(gcloud run services describe $SERVICE_NAME --format 'value(status.url)') gcloud scheduler jobs create http $SERVICE_NAME-job --schedule "* * * * *" \ --http-method=GET \ --location=europe-west1 \ --uri=$SERVICE_URL \ --oidc-service-account-email=$SERVICE_ACCOUNT@$GOOGLE_CLOUD_PROJECT.iam.gserviceaccount.com \ --oidc-token-audience=$SERVICE_URL
您可以前往 Cloud 控制台中的 Cloud Scheduler 部分,确认它已设置完毕并指向 Cloud Run 服务网址:

9. 测试服务
如需测试设置是否有效,请在 thumbnails 存储分区中检查拼图图片(称为 collage.png)。您还可以检查服务的日志:

10. 清理(可选)
如果您不打算继续完成本系列中的其他实验,可以清理资源以节省成本,并成为一个整体优秀的云公民。您可以按以下步骤逐个清理资源。
删除服务:
gcloud run services delete $SERVICE_NAME -q
删除 Cloud Scheduler 作业:
gcloud scheduler jobs delete $SERVICE_NAME-job -q
或者,您也可以删除整个项目:
gcloud projects delete $GOOGLE_CLOUD_PROJECT
11. 恭喜!
恭喜!您创建了一项预定服务:借助 Cloud Scheduler,该调度程序每分钟都会针对一个 Pub/Sub 主题推送一条消息,您的 Cloud Run 拼图服务被调用并能够将图片附加到一起,从而生成图片。
所学内容
- Cloud Run
- Cloud Scheduler
- Cloud Storage
- Cloud Firestore