1. Tổng quan
Trong lớp học lập trình này, bạn sẽ tạo một dịch vụ Cloud Run mới (dịch vụ tạo ảnh ghép) do Cloud Scheduler kích hoạt theo một khoảng thời gian đều đặn. Dịch vụ này tìm nạp những bức ảnh mới nhất được tải lên và tạo một bức ảnh ghép từ những bức ảnh đó: dịch vụ này tìm danh sách những bức ảnh gần đây trong Cloud Firestore, sau đó tải các tệp ảnh thực tế xuống từ Cloud Storage.

Kiến thức bạn sẽ học được
- Cloud Run
- Cloud Scheduler
- Cloud Storage
- Cloud Firestore
2. Thiết lập và yêu cầu
Thiết lập môi trường theo tốc độ của riêng bạn
- Đăng nhập vào Google Cloud Console rồi tạo một dự án mới hoặc sử dụng lại một dự án hiện có. Nếu chưa có tài khoản Gmail hoặc Google Workspace, bạn phải tạo một tài khoản.



- Tên dự án là tên hiển thị của những người tham gia dự án này. Đây là một chuỗi ký tự mà các API của Google không dùng và bạn có thể cập nhật chuỗi này bất cứ lúc nào.
- Mã dự án phải là duy nhất trên tất cả các dự án trên Google Cloud và không thể thay đổi (bạn không thể thay đổi sau khi đã đặt). Cloud Console sẽ tự động tạo một chuỗi duy nhất; thường thì bạn không cần quan tâm đến chuỗi này. Trong hầu hết các lớp học lập trình, bạn sẽ cần tham chiếu đến Mã dự án (thường được xác định là
PROJECT_ID). Vì vậy, nếu không thích mã này, bạn có thể tạo một mã ngẫu nhiên khác hoặc thử mã của riêng mình để xem mã đó có dùng được hay không. Sau đó, mã này sẽ "đóng băng" sau khi dự án được tạo. - Có một giá trị thứ ba là Số dự án mà một số API sử dụng. Tìm hiểu thêm về cả 3 giá trị này trong tài liệu.
- Tiếp theo, bạn cần bật tính năng thanh toán trong Cloud Console để sử dụng các tài nguyên/API trên Cloud. Việc thực hiện lớp học lập trình này sẽ không tốn nhiều chi phí, nếu có. Để tắt các tài nguyên nhằm tránh bị tính phí ngoài phạm vi hướng dẫn này, hãy làm theo mọi hướng dẫn "dọn dẹp" ở cuối lớp học lập trình. Người dùng mới của Google Cloud đủ điều kiện tham gia chương trình Dùng thử miễn phí trị giá 300 USD.
Khởi động Cloud Shell
Mặc dù có thể vận hành Google Cloud từ xa trên máy tính xách tay, nhưng trong lớp học lập trình này, bạn sẽ sử dụng Google Cloud Shell, một môi trường dòng lệnh chạy trên Cloud.
Trên Bảng điều khiển GCP, hãy nhấp vào biểu tượng Cloud Shell trên thanh công cụ ở trên cùng bên phải:

Quá trình này chỉ mất vài phút để cung cấp và kết nối với môi trường. Khi quá trình này kết thúc, bạn sẽ thấy như sau:

Máy ảo này được trang bị tất cả các công cụ phát triển mà bạn cần. Nó cung cấp một thư mục chính có dung lượng 5 GB và chạy trên Google Cloud, giúp tăng cường đáng kể hiệu suất mạng và hoạt động xác thực. Bạn chỉ cần một trình duyệt là có thể thực hiện mọi thao tác trong phòng thí nghiệm này.
3. Bật API
Bạn sẽ cần Cloud Scheduler để kích hoạt dịch vụ Cloud Run theo định kỳ. Đảm bảo bạn đã bật tính năng này:
gcloud services enable cloudscheduler.googleapis.com
Bạn sẽ thấy thao tác hoàn tất thành công:
Operation "operations/acf.5c5ef4f6-f734-455d-b2f0-ee70b5a17322" finished successfully.
4. Sao chép mã
Sao chép mã nếu bạn chưa thực hiện trong lớp học lập trình trước:
git clone https://github.com/GoogleCloudPlatform/serverless-photosharing-workshop
Sau đó, bạn có thể chuyển đến thư mục chứa dịch vụ:
cd serverless-photosharing-workshop/services/collage/nodejs
Bạn sẽ có bố cục tệp sau cho dịch vụ:
services
|
├── collage
|
├── nodejs
|
├── Dockerfile
├── index.js
├── package.json
Trong thư mục này, bạn có 3 tệp:
index.jschứa mã Node.jspackage.jsonxác định các phần phụ thuộc của thư việnDockerfilexác định hình ảnh vùng chứa
5. Khám phá mã
Phần phụ thuộc
Tệp package.json xác định các phần phụ thuộc cần thiết của thư viện:
{
"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"
}
}
Chúng tôi phụ thuộc vào thư viện Cloud Storage để đọc và lưu các tệp hình ảnh trong Cloud Storage. Chúng ta khai báo một phần phụ thuộc vào Cloud Firestore để tìm nạp siêu dữ liệu hình ảnh mà chúng ta đã lưu trữ trước đó. Express là một khung web JavaScript / Node. Bluebird được dùng để xử lý các promise và imagemagick là một thư viện để thao tác với hình ảnh.
tệp Docker
Dockerfile xác định hình ảnh vùng chứa cho ứng dụng:
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" ]
Chúng tôi đang sử dụng hình ảnh cơ sở Node 14 nhẹ. Chúng ta sẽ cài đặt thư viện imagemagick. Sau đó, chúng ta sẽ cài đặt các mô-đun NPM mà mã của chúng ta cần và chạy mã nút bằng lệnh npm start.
index.js
Hãy xem xét kỹ hơn mã index.js của chúng ta:
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');
Chúng ta cần nhiều phần phụ thuộc để chương trình chạy: Express là khung web Node mà chúng ta sẽ sử dụng, ImageMagick là thư viện để thao tác với hình ảnh, Bluebird là thư viện để xử lý các lời hứa JavaScript, Path được dùng để xử lý các đường dẫn tệp và thư mục, sau đó Storage và Firestore lần lượt được dùng để làm việc với Google Cloud Storage (các nhóm hình ảnh của chúng ta) và kho dữ liệu 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);
}
});
Ở trên, chúng ta có cấu trúc của trình xử lý Node: ứng dụng của chúng ta phản hồi các yêu cầu HTTP GET. Chúng ta sẽ xử lý một chút lỗi trong trường hợp có sự cố xảy ra. Bây giờ, hãy xem bên trong cấu trúc này có gì.
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 {
/* ... */
}
Dịch vụ tạo ảnh ghép của chúng tôi cần ít nhất 4 bức ảnh (đã tạo hình thu nhỏ), vì vậy, hãy nhớ tải 4 bức ảnh lên trước.
Chúng tôi truy xuất 4 bức ảnh mới nhất do người dùng tải lên từ siêu dữ liệu được lưu trữ trong Cloud Firestore. Chúng ta kiểm tra xem tập hợp kết quả có trống hay không, sau đó tiếp tục trong nhánh else của mã.
Hãy thu thập danh sách tên tệp:
snapshot.forEach(doc => {
thumbnailFiles.push(doc.id);
});
console.log(`Picture file names: ${JSON.stringify(thumbnailFiles)}`);
Chúng ta sẽ tải từng tệp đó xuống từ nhóm hình thu nhỏ. Tên của nhóm này sẽ lấy từ một biến môi trường mà chúng ta đặt tại thời điểm triển khai:
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');
Sau khi tải các hình thu nhỏ mới nhất lên, chúng ta sẽ sử dụng thư viện ImageMagick để tạo lưới 4x4 gồm những hình thu nhỏ đó. Chúng ta sử dụng thư viện Bluebird và việc triển khai Promise của thư viện này để chuyển đổi mã dựa trên lệnh gọi lại thành mã thân thiện với async / await, sau đó chúng ta chờ promise tạo ảnh ghép:
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");
Vì ảnh ghép đã được lưu vào ổ đĩa cục bộ trong thư mục tạm thời, nên giờ đây, chúng ta cần tải ảnh đó lên Cloud Storage, rồi trả về một phản hồi thành công (mã trạng thái 2xx):
await thumbBucket.upload(collagePath);
console.log("Uploaded collage to Cloud Storage bucket ${process.env.BUCKET_THUMBNAILS}");
res.status(204).send("Collage created.");
Bây giờ là lúc để tập lệnh Node của chúng ta lắng nghe các yêu cầu đến:
const PORT = process.env.PORT || 8080;
app.listen(PORT, () => {
console.log(`Started collage service on port ${PORT}`);
});
Ở cuối tệp nguồn, chúng ta có hướng dẫn để Express thực sự khởi động ứng dụng web trên cổng mặc định 8080.
6. Kiểm thử cục bộ
Kiểm thử mã cục bộ để đảm bảo mã hoạt động trước khi triển khai lên đám mây.
Trong thư mục collage/nodejs, hãy cài đặt các phần phụ thuộc npm và khởi động máy chủ:
npm install; npm start
Nếu mọi thứ diễn ra suôn sẻ, thì máy chủ sẽ khởi động trên cổng 8080:
Started collage service on port 8080
Sử dụng CTRL-C để thoát.
7. Tạo bản dựng và triển khai lên Cloud Run
Trước khi triển khai lên Cloud Run, hãy đặt khu vực Cloud Run thành một trong các khu vực được hỗ trợ và nền tảng thành managed:
gcloud config set run/region europe-west1 gcloud config set run/platform managed
Bạn có thể kiểm tra để đảm bảo rằng cấu hình đã được thiết lập:
gcloud config list ... [run] platform = managed region = europe-west1
Thay vì tạo và xuất bản hình ảnh vùng chứa bằng Cloud Build theo cách thủ công, bạn cũng có thể dựa vào Cloud Run để tạo hình ảnh vùng chứa cho bạn bằng Google Cloud Buildpacks.
Chạy lệnh sau để tạo hình ảnh vùng chứa:
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
Lưu ý cờ –-source. Đây là quy trình triển khai dựa trên nguồn trong Cloud Run. Nếu có một Dockerfile trong thư mục mã nguồn, thì mã nguồn được tải lên sẽ được tạo bằng Dockerfile đó. Nếu không có Dockerfile trong thư mục mã nguồn, các gói bản dựng Google Cloud sẽ tự động phát hiện ngôn ngữ bạn đang sử dụng và tìm nạp các phần phụ thuộc của mã để tạo một hình ảnh vùng chứa sẵn sàng cho sản xuất, bằng cách sử dụng một hình ảnh cơ sở an toàn do Google quản lý. Lệnh này gắn cờ Cloud Run để sử dụng Google Cloud Buildpacks nhằm tạo hình ảnh vùng chứa được xác định trong Dockerfile.
Xin lưu ý rằng quá trình triển khai dựa trên nguồn sử dụng Artifact Registry để lưu trữ các vùng chứa đã tạo. Artifact Registry là phiên bản mới của Google Container Registry. CLI sẽ nhắc bạn bật API nếu API chưa được bật trong dự án và sẽ tạo một kho lưu trữ có tên cloud-run-source-deploy trong khu vực mà bạn đang triển khai.
Cờ --no-allow-unauthenticated biến dịch vụ Cloud Run thành một dịch vụ nội bộ và chỉ được kích hoạt bởi các tài khoản dịch vụ cụ thể.
8. Thiết lập Cloud Scheduler
Bây giờ, khi dịch vụ Cloud Run đã sẵn sàng và được triển khai, đã đến lúc tạo lịch biểu thường xuyên để gọi dịch vụ này mỗi phút.
Tạo tài khoản dịch vụ:
SERVICE_ACCOUNT=collage-scheduler-sa gcloud iam service-accounts create $SERVICE_ACCOUNT \ --display-name "Collage Scheduler Service Account"
Cấp quyền cho tài khoản dịch vụ để gọi dịch vụ 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
Tạo một công việc Cloud Scheduler để thực thi cứ 1 phút một lần:
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
Bạn có thể chuyển đến phần Cloud Scheduler trong Cloud Console để xem chế độ thiết lập và trỏ đến URL dịch vụ Cloud Run:

9. Kiểm thử dịch vụ
Để kiểm tra xem chế độ thiết lập có hoạt động hay không, hãy kiểm tra trong vùng chứa thumbnails để tìm hình ảnh ghép (có tên là collage.png). Bạn cũng có thể kiểm tra nhật ký của dịch vụ:

10. Dọn dẹp (Không bắt buộc)
Nếu không có ý định tiếp tục với các phòng thí nghiệm khác trong loạt bài này, bạn có thể dọn dẹp tài nguyên để tiết kiệm chi phí và trở thành một công dân đám mây tốt. Bạn có thể dọn dẹp từng tài nguyên riêng lẻ như sau.
Xoá dịch vụ:
gcloud run services delete $SERVICE_NAME -q
Xoá công việc Cloud Scheduler:
gcloud scheduler jobs delete $SERVICE_NAME-job -q
Ngoài ra, bạn có thể xoá toàn bộ dự án:
gcloud projects delete $GOOGLE_CLOUD_PROJECT
11. Xin chúc mừng!
Xin chúc mừng! Bạn đã tạo một dịch vụ theo lịch: nhờ Cloud Scheduler, dịch vụ này sẽ đẩy một thông báo mỗi phút trên một chủ đề Pub/Sub, dịch vụ ảnh ghép Cloud Run của bạn sẽ được gọi và có thể nối các bức ảnh lại với nhau để tạo ra bức ảnh kết quả.
Nội dung đã đề cập
- Cloud Run
- Cloud Scheduler
- Cloud Storage
- Cloud Firestore