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