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

1. Przegląd

W tym module szkoleniowym utworzysz nową usługę Cloud Run o nazwie collage service, która będzie wywoływana 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 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 użyj istniejącego. Jeśli nie masz jeszcze konta Gmail ani Google Workspace, musisz je utworzyć.

96a9c957bc475304.png

b9a10ebdf5b5a448.png

a1e3c01a38fa61c2.png

  • Nazwa projektu to wyświetlana nazwa uczestników tego projektu. Jest to ciąg znaków, który nie jest używany przez interfejsy API Google. Możesz go w dowolnym momencie zaktualizować.
  • Identyfikator projektu musi być unikalny we wszystkich projektach Google Cloud i jest niezmienny (nie można go zmienić po ustawieniu). Konsola Cloud automatycznie generuje unikalny ciąg znaków. Zwykle nie musisz się nim przejmować. W większości modułów z kodem musisz odwoływać się do identyfikatora projektu (zwykle oznaczanego jako PROJECT_ID). Jeśli Ci się nie podoba, wygeneruj inny losowy identyfikator lub spróbuj użyć własnego i sprawdź, czy jest dostępny. Po utworzeniu projektu jest on „zamrażany”.
  • Istnieje też trzecia wartość, czyli numer projektu, którego używają niektóre interfejsy API. Więcej informacji o tych 3 wartościach znajdziesz w dokumentacji.
  1. Następnie musisz włączyć płatności w konsoli Cloud, aby korzystać z zasobów i interfejsów API Google Cloud. Ukończenie tego laboratorium nie powinno wiązać się z dużymi kosztami, a nawet z żadnymi. Aby wyłączyć zasoby i uniknąć naliczenia opłat po zakończeniu tego samouczka, postępuj zgodnie z instrukcjami „czyszczenia” na końcu ćwiczenia. Nowi użytkownicy Google Cloud mogą skorzystać z bezpłatnego okresu próbnego, w którym mają do dyspozycji środki w wysokości 300 USD.

Uruchamianie Cloud Shell

Z Google Cloud można korzystać zdalnie na laptopie, ale w tym module praktycznym będziesz używać Google Cloud Shell, czyli środowiska wiersza poleceń działającego w chmurze.

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

bce75f34b2c53987.png

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

f6ef2b5f13479f3a.png

Ta maszyna wirtualna zawiera wszystkie potrzebne narzędzia dla programistów. Zawiera również stały katalog domowy o pojemności 5 GB i działa w Google Cloud, co znacznie zwiększa wydajność sieci i usprawnia proces uwierzytelniania. Wszystkie zadania w tym module możesz wykonać w przeglądarce.

3. Włącz interfejsy API

Aby regularnie wywoływać usługę Cloud Run, potrzebujesz usługi Cloud Scheduler. Sprawdź, czy jest włączona:

gcloud services enable cloudscheduler.googleapis.com

Operacja powinna zakończyć się powodzeniem:

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

4. Klonowanie kodu

Sklonuj kod, jeśli nie masz go jeszcze z poprzedniego modułu:

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

Usługa będzie miała ten układ pliku:

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

W folderze znajdują się 3 pliki:

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

5. Poznaj kod

Zależności

Plik package.json określa wymagane 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 obrazów w Cloud Storage używamy biblioteki Cloud Storage. Deklarujemy zależność od Cloud Firestore, aby pobrać metadane obrazu, które zostały wcześniej zapisane. Express to platforma internetowa JavaScript / Node. Bluebird służy do obsługi obietnic, a imagemagick to biblioteka do manipulowania obrazami.

Dockerfile

Dockerfile określa 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 lekkiego obrazu podstawowego Node 14. Instalujemy bibliotekę imagemagick. Następnie instalujemy moduły NPM potrzebne do działania naszego kodu i uruchamiamy kod węzła za pomocą polecenia npm start.

index.js

Przyjrzyjmy się bliżej naszemu index.js kodowi:

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

Wymagamy różnych zależności potrzebnych do działania naszego programu: Express to platforma internetowa Node, której będziemy używać, ImageMagick to biblioteka do manipulowania obrazami, Bluebird to biblioteka do obsługi obietnic JavaScript, Path służy do obsługi ścieżek plików i katalogów, a Storage i Firestore służą odpowiednio do pracy z Google Cloud Storage (naszymi zasobnikami obrazów) i 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 widzimy strukturę naszego modułu obsługi Node.js: nasza aplikacja odpowiada na żądania HTTP GET. Wprowadzamy też obsługę błędów na wypadek, gdyby coś poszło nie tak. Przyjrzyjmy się teraz, co znajduje się w środku 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żu wymaga co najmniej 4 zdjęć (z wygenerowanymi miniaturami), więc najpierw prześlij 4 zdjęcia.

Pobieramy 4 najnowsze zdjęcia przesłane przez użytkowników z metadanych przechowywanych w Cloud Firestore. Sprawdzamy, czy wynikowa kolekcja jest pusta, a następnie przechodzimy do gałęzi else w naszym kodzie.

Zbierzmy 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 segmentu miniatur, którego nazwa pochodzi ze zmiennej środowiskowej ustawionej w momencie 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ę 4x4 z tych miniatur. Używamy biblioteki Bluebird i jej implementacji Promise, aby przekształcić kod oparty na wywołaniach zwrotnych w kod przyjazny dla async / await, a następnie czekamy na obietnicę tworzenia kolażu 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 na dysku lokalnym w folderze tymczasowym, więc musimy go teraz przesłać do Cloud Storage, a następnie zwrócić odpowiedź z informacją o powodzeniu (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 skrypt Node nasłuchiwał przychodzących żądań:

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

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

Na końcu pliku źródłowego znajdują się instrukcje, które powodują, że Express uruchamia aplikację internetową na domyślnym porcie 8080.

6. Testowanie lokalne

Przed wdrożeniem w chmurze przetestuj kod lokalnie, aby sprawdzić, czy działa.

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

npm install; npm start

Jeśli wszystko przebiegło pomyślnie, serwer powinien zostać uruchomiony na porcie 8080:

Started collage service on port 8080

Aby wyjść, użyj CTRL-C.

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, a platformę 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 tworzyć i publikować obraz kontenera za pomocą Cloud Build, możesz też polegać na Cloud Run, który utworzy obraz kontenera za pomocą pakietów kompilacji Google Cloud.

Aby utworzyć obraz kontenera, uruchom to polecenie:

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 kodu źródłowego znajduje się plik Dockerfile, przesłany kod źródłowy jest kompilowany przy użyciu tego pliku.Dockerfile Jeśli w katalogu kodu źródłowego nie ma pliku 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, używając bezpiecznego obrazu bazowego zarządzanego przez Google. Oznacza to, że Cloud Run ma używać pakietów kompilacji Google Cloud do tworzenia 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. CLI wyświetli prośbę o włączenie interfejsu API, jeśli nie jest on jeszcze włączony w projekcie, i 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 staje się usługą wewnętrzną, która będzie wywoływana tylko przez określone konta usługi.

8. Konfigurowanie Cloud Scheduler

Usługa Cloud Run jest już gotowa i wdrożona, więc możemy utworzyć regularny harmonogram, aby wywoływać usługę 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, które będzie wykonywane co 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

W sekcji Cloud Scheduler w konsoli Cloud możesz 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, poszukaj w zasobniku thumbnails obrazu kolażu (o nazwie collage.png). Możesz też sprawdzić logi usługi:

93922335a384be2e.png

10. Zwalnianie miejsca (opcjonalnie)

Jeśli nie zamierzasz kontynuować pracy z innymi ćwiczeniami z tej serii, możesz usunąć zasoby, aby zaoszczędzić pieniądze i być dobrym użytkownikiem chmury. Możesz zwolnić miejsce, czyszcząc poszczególne zasoby 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! Utworzono usługę zaplanowaną: dzięki Cloud Scheduler, który co minutę wysyła wiadomość do tematu Pub/Sub, usługa kolażu Cloud Run jest wywoływana i może łączyć zdjęcia, aby utworzyć wynikowy obraz.

Omówione zagadnienia

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

Następne kroki