Zdjęcia codziennie: moduł 3 – tworzenie kolażu najnowszych zdjęć

1. Omówienie

W tym module z kodem utworzysz nową usługę Cloud Run o nazwie kolażu, która będzie aktywowana przez Cloud Scheduler w regularnych odstępach czasu. Usługa pobiera najnowsze przesłane zdjęcia i tworzy z nich kolaż: znajduje listę ostatnich zdjęć w Cloud Firestore, a następnie pobiera rzeczywiste pliki zdjęć z Cloud Storage.

df20f5d0402b54b4.png

Czego się nauczysz

  • Cloud Run
  • Cloud Scheduler
  • Cloud Storage
  • Cloud Firestore

2. Konfiguracja i wymagania

Samodzielne konfigurowanie środowiska

  1. Zaloguj się w konsoli Google Cloud i utwórz nowy projekt lub wykorzystaj już istniejący. Jeśli nie masz jeszcze konta Gmail ani Google Workspace, musisz je utworzyć.

96a9c957bc475304.png

b9a10ebdf5b5a448.png

a1e3c01a38fa61c2.png

  • Nazwa projektu jest wyświetlaną nazwą uczestników tego projektu. Jest to ciąg znaków, który nie jest używany przez interfejsy API Google i w każdej chwili możesz go zaktualizować.
  • Identyfikator projektu musi być unikalny we wszystkich projektach Google Cloud i nie można go zmienić (nie można go zmienić po ustawieniu). Cloud Console automatycznie wygeneruje unikalny ciąg znaków. zwykle nieważne, co ona jest. W większości ćwiczeń w Codelabs musisz odwoływać się do identyfikatora projektu (który zwykle nazywa się PROJECT_ID), więc jeśli Ci się nie podoba, wygeneruj kolejny losowy kod lub wypróbuj swój własny i sprawdź, czy jest dostępny. Potem urządzenie jest „zawieszone”. po utworzeniu projektu.
  • Występuje trzecia wartość – numer projektu – używany przez niektóre interfejsy API. Więcej informacji o wszystkich 3 wartościach znajdziesz w dokumentacji.
  1. Następnie musisz włączyć płatności w konsoli Cloud, aby móc korzystać z zasobów i interfejsów API Cloud. Ukończenie tego ćwiczenia z programowania nie powinno kosztować zbyt wiele. Aby wyłączyć zasoby, aby nie naliczać opłat po zakończeniu tego samouczka, wykonaj czynności „wyczyść” znajdziesz na końcu tego ćwiczenia. Nowi użytkownicy Google Cloud mogą skorzystać z programu bezpłatnego okresu próbnego o wartości 300 USD.

Uruchamianie Cloud Shell

Google Cloud można obsługiwać zdalnie z laptopa, ale w ramach tego ćwiczenia z programowania wykorzystasz Google Cloud Shell – środowisko wiersza poleceń działające w Cloud.

W konsoli GCP kliknij ikonę Cloud Shell na górnym pasku narzędzi:

bce75f34b2c53987.png

Uzyskanie dostępu do środowiska i połączenie się z nim powinno zająć tylko kilka chwil. Po zakończeniu powinno pojawić się coś takiego:

f6ef2b5f13479f3a.png

Ta maszyna wirtualna ma wszystkie potrzebne narzędzia dla programistów. Zawiera stały katalog domowy o pojemności 5 GB i działa w Google Cloud, znacząco zwiększając wydajność sieci i uwierzytelnianie. Wszystkie zadania w tym module możesz wykonać w przeglądarce.

3. Włącz interfejsy API

Aby regularnie aktywować usługę Cloud Run, potrzebujesz usługi Cloud Scheduler. Upewnij się, że jest włączona:

gcloud services enable cloudscheduler.googleapis.com

Powinna pojawić się operacja dokończenia:

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

4. Klonowanie kodu

Skopiuj kod, jeśli jeszcze nie udało Ci się tego zrobić w poprzednim module:

git clone https://github.com/GoogleCloudPlatform/serverless-photosharing-workshop

Następnie możesz przejść do katalogu zawierającego usługę:

cd serverless-photosharing-workshop/services/collage/nodejs

Masz do dyspozycji taki układ plików dla usługi:

services
 |
 ├── collage
      |
      ├── nodejs
           |
           ├── Dockerfile
           ├── index.js
           ├── package.json

W folderze znajdują się 3 pliki:

  • index.js zawiera kod Node.js
  • package.json definiuje zależności biblioteki
  • Dockerfile definiuje obraz kontenera

5. Zapoznaj się z kodem

Zależności

Plik package.json określa potrzebne zależności biblioteki:

{
  "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"
  }
}

Do odczytywania i zapisywania plików graficznych w Cloud Storage używamy biblioteki Cloud Storage. Deklarujemy zależność od Cloud Firestore, aby pobierać zapisane wcześniej metadane obrazu. Express to platforma internetowa JavaScript / węzłów. Bluebird służy do obsługi obietnic, a imagemagick to biblioteka do manipulowania obrazami.

Dockerfile

Dockerfile definiuje obraz kontenera aplikacji:

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" ]

Używamy jasnego obrazu podstawowego węzła Node 14. Instalujemy bibliotekę Imagemagick. Następnie instalujemy moduły NPM wymagane przez nasz kod i uruchamiamy kod węzła z uruchomieniem npm.

index.js

Przyjrzyjmy się bliżej naszemu kodowi w usłudze 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');

Program wymaga różnych zależności potrzebnych do uruchomienia programu: Express to platforma sieciowa węzła, której będziemy używać, ImageMagick do obsługi obrazów, Bluebird – biblioteka do obsługi obietnic JavaScriptu, ścieżka Ścieżka służy do obsługi plików i ścieżek katalogów, a Cloud Storage i Firestore służą do pracy odpowiednio z Google Cloud Storage (nasze zasobniki obrazów) oraz z magazynem danych 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);
    }
});

Powyżej mamy strukturę naszego modułu obsługi węzłów: nasza aplikacja odpowiada na żądania HTTP GET. Staramy się też naprawić błędy, gdy coś pójdzie nie tak. Przyjrzyjmy się teraz wnętrzu tej konstrukcji.

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 {

    /* ... */

}

Nasza usługa kolaży będzie potrzebować co najmniej czterech zdjęć (których miniatury zostały wygenerowane), więc pamiętaj, aby najpierw przesłać 4 zdjęcia.

Z metadanych przechowywanych w Cloud Firerstore pobieramy 4 najnowsze zdjęcia przesłane przez naszych użytkowników. Sprawdzamy, czy wynikowa kolekcja jest pusta, a następnie przechodzimy dalej w innej gałęzi kodu.

Utwórzmy listę nazw plików:

snapshot.forEach(doc => {
    thumbnailFiles.push(doc.id);
});
console.log(`Picture file names: ${JSON.stringify(thumbnailFiles)}`);

Pobierzemy każdy z tych plików z zasobnika na miniatury, którego nazwa pochodzi ze zmiennej środowiskowej ustawionej podczas wdrażania:

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

Po przesłaniu najnowszych miniatur użyjemy biblioteki ImageMagick, aby utworzyć siatkę 4 x 4 miniatur. Korzystając z biblioteki Bluebird i jej implementacji Promise, przekształcamy kod oparty na wywołaniach zwrotnych w przyjazny kod async / await, a następnie czekamy na obietnicę złożenia kolażu z obrazów:

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

Obraz kolażu został zapisany lokalnie na dysku w folderze tymczasowym, dlatego teraz musimy przesłać go do Cloud Storage, a następnie zwrócić pomyślną odpowiedź (kod stanu 2xx):

await thumbBucket.upload(collagePath);
console.log("Uploaded collage to Cloud Storage bucket ${process.env.BUCKET_THUMBNAILS}");

res.status(204).send("Collage created.");

Teraz czas, aby nasz skrypt węzła nasłuchiwał żądań przychodzących:

const PORT = process.env.PORT || 8080;

app.listen(PORT, () => {
    console.log(`Started collage service on port ${PORT}`);
});

Na końcu pliku źródłowego znajdziesz instrukcje, aby usługa Express uruchamiała aplikację internetową na domyślnym porcie 8080.

6. Przetestuj lokalnie

Przed wdrożeniem kodu w chmurze przetestuj go lokalnie, aby upewnić się, że działa.

W folderze collage/nodejs zainstaluj zależności npm i uruchom serwer:

npm install; npm start

Jeśli wszystko poszło dobrze, serwer powinien uruchamiać się na porcie 8080:

Started collage service on port 8080

Użyj CTRL-C, aby wyjść.

7. Kompilowanie i wdrażanie w Cloud Run

Przed wdrożeniem w Cloud Run ustaw region Cloud Run na jeden z obsługiwanych regionów i platformy na managed:

gcloud config set run/region europe-west1
gcloud config set run/platform managed

Możesz sprawdzić, czy konfiguracja jest ustawiona:

gcloud config list

...
[run]
platform = managed
region = europe-west1

Zamiast ręcznie kompilować i publikować obraz kontenera za pomocą Cloud Build, możesz też skorzystać z Cloud Run, aby skompilować obraz kontenera za Ciebie za pomocą Google Cloud Buildpacks.

Uruchom to polecenie, aby utworzyć obraz kontenera:

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

Zwróć uwagę na flagę –-source. Jest to wdrożenie oparte na źródle w Cloud Run. Jeśli w katalogu z kodem źródłowym znajduje się tag Dockerfile, przesłany kod źródłowy jest generowany za pomocą tagu Dockerfile. Jeśli w katalogu z kodem źródłowym nie ma elementu Dockerfile, pakiety kompilacji Google Cloud automatycznie wykrywają używany język i pobierają zależności kodu, aby utworzyć obraz kontenera gotowy do wykorzystania w środowisku produkcyjnym, wykorzystując bezpieczny obraz podstawowy zarządzany przez Google. Ta flaga oznacza, że Cloud Run będzie używać Google Cloud Buildpacks do utworzenia obrazu kontenera zdefiniowanego w Dockerfile.

Pamiętaj też, że wdrożenie oparte na źródle korzysta z Artifact Registry do przechowywania utworzonych kontenerów. Artifact Registry to nowoczesna wersja Google Container Registry. Interfejs wiersza poleceń wyświetli prośbę o włączenie interfejsu API, jeśli nie jest jeszcze włączony w projekcie, oraz utworzy repozytorium o nazwie cloud-run-source-deploy w regionie, w którym wdrażasz.

Flaga --no-allow-unauthenticated sprawia, że usługa Cloud Run jest usługą wewnętrzną, która będzie aktywowana tylko przez określone konta usługi.

8. Konfigurowanie usługi Cloud Scheduler

Po przygotowaniu i wdrożeniu usługi Cloud Run możesz utworzyć zwykły harmonogram, który będzie ją wywoływać co minutę.

Utwórz konto usługi:

SERVICE_ACCOUNT=collage-scheduler-sa
gcloud iam service-accounts create $SERVICE_ACCOUNT \
   --display-name "Collage Scheduler Service Account"

Przyznaj kontu usługi uprawnienia do wywoływania usługi 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

Utwórz zadanie Cloud Scheduler do wykonywania co 1 minutę:

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

Możesz otworzyć sekcję Cloud Scheduler w konsoli Cloud, aby sprawdzić, czy usługa jest skonfigurowana i wskazuje adres URL usługi Cloud Run:

35119e28c1da53f3.png

9. Testowanie usługi

Aby sprawdzić, czy konfiguracja działa, sprawdź, czy w zasobniku thumbnails znajduje się obraz kolażu (o nazwie collage.png). Możesz też sprawdzić logi usługi:

93922335a384be2e.png

10. Czyszczenie (opcjonalnie)

Jeśli nie chcesz przechodzić do innych modułów z tej serii, możesz zwolnić zasoby, aby zmniejszyć koszty i zachować dobre praktyki związane z chmurą. Zasoby możesz wyczyścić pojedynczo w ten sposób.

Usuń usługę:

gcloud run services delete $SERVICE_NAME -q

Usuń zadanie Cloud Scheduler:

gcloud scheduler jobs delete $SERVICE_NAME-job -q

Możesz też usunąć cały projekt:

gcloud projects delete $GOOGLE_CLOUD_PROJECT

11. Gratulacje!

Gratulacje! Udało Ci się utworzyć zaplanowaną usługę: dzięki usłudze Cloud Scheduler, która przekazuje wiadomość w temacie Pub/Sub co minutę, wywoływana jest usługa kolaży Cloud Run, która może dołączać obrazy do siebie, aby utworzyć obraz końcowy.

Omówione zagadnienia

  • Cloud Run
  • Cloud Scheduler
  • Cloud Storage
  • Cloud Firestore

Następne kroki