Pic-a-daily: Лабораторная работа 3. Создайте коллаж из самых последних фотографий.

1. Обзор

В этом практическом задании вы создадите новый сервис Cloud Run, сервис создания коллажей, который будет запускаться планировщиком Cloud Scheduler через регулярные интервалы времени. Сервис получает последние загруженные изображения и создает из них коллаж: он находит список последних изображений в Cloud Firestore, а затем загружает сами файлы изображений из Cloud Storage.

df20f5d0402b54b4.png

Что вы узнаете

  • Cloud Run
  • Планировщик облачных задач
  • Облачное хранилище
  • Облачный Firestore

2. Настройка и требования

Настройка среды для самостоятельного обучения

  1. Войдите в консоль Google Cloud и создайте новый проект или используйте существующий. Если у вас еще нет учетной записи Gmail или Google Workspace, вам необходимо ее создать .

96a9c957bc475304.png

b9a10ebdf5b5a448.png

a1e3c01a38fa61c2.png

  • Название проекта — это отображаемое имя участников данного проекта. Это строка символов, не используемая API Google, и вы можете изменить её в любое время.
  • Идентификатор проекта должен быть уникальным для всех проектов Google Cloud и неизменяемым (его нельзя изменить после установки). Консоль Cloud автоматически генерирует уникальную строку; обычно вам неважно, какая она. В большинстве практических заданий вам потребуется указать идентификатор проекта (обычно он обозначается как PROJECT_ID ), поэтому, если он вам не нравится, сгенерируйте другой случайный идентификатор или попробуйте свой собственный и посмотрите, доступен ли он. Затем он "замораживается" после создания проекта.
  • Существует третье значение — номер проекта , который используется некоторыми API. Подробнее обо всех трех значениях можно узнать в документации .
  1. Далее вам потребуется включить оплату в консоли Cloud, чтобы использовать ресурсы/API Cloud. Выполнение этого практического задания не должно стоить дорого, если вообще что-либо. Чтобы отключить ресурсы и избежать дополнительных расходов после завершения этого урока, следуйте инструкциям по «очистке», приведенным в конце практического задания. Новые пользователи Google Cloud имеют право на бесплатную пробную версию стоимостью 300 долларов США .

Запустить Cloud Shell

Хотя Google Cloud можно управлять удаленно с ноутбука, в этом практическом занятии вы будете использовать Google Cloud Shell — среду командной строки, работающую в облаке.

В консоли GCP щелкните значок Cloud Shell на панели инструментов в правом верхнем углу:

bce75f34b2c53987.png

Подготовка и подключение к среде займут всего несколько минут. После завершения вы должны увидеть что-то подобное:

f6ef2b5f13479f3a.png

Эта виртуальная машина оснащена всеми необходимыми инструментами разработки. Она предоставляет постоянный домашний каталог размером 5 ГБ и работает в облаке Google, что значительно повышает производительность сети и аутентификацию. Всю работу в этой лаборатории можно выполнять с помощью обычного браузера.

3. Включите API.

Для регулярного запуска службы Cloud Run вам потребуется планировщик задач Cloud Scheduler. Убедитесь, что он включен:

gcloud services enable cloudscheduler.googleapis.com

Вы должны увидеть сообщение об успешном завершении операции:

Operation "operations/acf.5c5ef4f6-f734-455d-b2f0-ee70b5a17322" finished successfully.

4. Клонируйте код

Если вы еще не клонировали код в предыдущем практическом занятии, сделайте это следующим образом:

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/Node. Bluebird используется для обработки промисов, а 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 и запускаем код Node с помощью команды 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 — это веб-фреймворк Node, который мы будем использовать, ImageMagick — библиотека для обработки изображений, Bluebird — библиотека для обработки промисов JavaScript, 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.js: наше приложение отвечает на 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 фотографии.

Мы получаем 4 последних изображения, загруженных нашими пользователями, из метаданных, хранящихся в CloudFirestore. Мы проверяем, пуста ли полученная коллекция, а затем продолжаем выполнение в ветке 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 , а затем ожидаем завершения выполнения промиса, создающего коллаж из изображений:

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");

Поскольку изображение коллажа сохранено локально на диске во временной папке, теперь нам необходимо загрузить его в облачное хранилище и получить успешный ответ (код состояния 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.

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 Build, вы также можете использовать Cloud Run для создания образа контейнера с помощью Google Cloud Buildpacks .

Выполните следующую команду для сборки образа контейнера:

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 Buildpacks для сборки образа контейнера, определенного в Dockerfile .

Обратите внимание, что при развертывании на основе исходного кода для хранения собранных контейнеров используется Artifact Registry . Artifact Registry — это современная версия Google Container Registry. CLI предложит включить API, если он еще не включен в проекте, и создаст репозиторий с именем cloud-run-source-deploy в регионе, куда вы развертываете приложение.

Флаг --no-allow-unauthenticated делает службу Cloud Run внутренней службой, которая будет запускаться только определенными учетными записями служб.

8. Настройка облачного планировщика.

Теперь, когда служба 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

Создайте задание в 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 Console вы можете перейти в раздел Cloud Scheduler, чтобы убедиться, что он настроен и указывает на URL-адрес службы Cloud Run:

35119e28c1da53f3.png

9. Проверьте работу сервиса.

Чтобы проверить работоспособность настройки, проверьте наличие изображения коллажа (называемого collage.png ) в папке thumbnails . Вы также можете проверить журналы службы:

93922335a384be2e.png

10. Уборка (необязательно)

Если вы не планируете продолжать выполнение остальных лабораторных работ из этой серии, вы можете освободить ресурсы, чтобы сэкономить средства и в целом ответственно относиться к облачным технологиям. Освободить ресурсы можно по отдельности следующим образом.

Удалить сервис:

gcloud run services delete $SERVICE_NAME -q

Удалите задание планировщика облачных задач:

gcloud scheduler jobs delete $SERVICE_NAME-job -q

В качестве альтернативы вы можете удалить весь проект:

gcloud projects delete $GOOGLE_CLOUD_PROJECT

11. Поздравляем!

Поздравляем! Вы создали запланированную службу: благодаря Cloud Scheduler, который каждую минуту отправляет сообщение в топик Pub/Sub, запускается ваша служба Cloud Run для создания коллажей, которая может объединять изображения для создания итогового изображения.

Что мы рассмотрели

  • Cloud Run
  • Планировщик облачных задач
  • Облачное хранилище
  • Облачный Firestore

Следующие шаги