Wprowadzenie do zadań Cloud Run

1. Wprowadzenie

1965fab24c502bd5.png

Przegląd

Usługi Cloud Run są odpowiednie dla kontenerów, które działają bezterminowo i nasłuchują żądań HTTP, natomiast zadania Cloud Run lepiej sprawdzają się w przypadku kontenerów, które działają do zakończenia (obecnie do 24 godzin) i nie obsługują żądań. Na przykład przetwarzanie rekordów z bazy danych, przetwarzanie listy plików z zasobnika Cloud Storage lub długotrwała operacja, taka jak obliczanie liczby Pi, sprawdzi się w przypadku implementacji jako zadanie Cloud Run.

Zadania nie mogą obsługiwać żądań ani nasłuchiwać na porcie. Oznacza to, że w przeciwieństwie do usług Cloud Run zadania nie powinny zawierać serwera WWW. Zamiast tego kontenery zadań powinny kończyć działanie po zakończeniu pracy.

W zadaniach Cloud Run możesz uruchamiać równolegle wiele kopii kontenera, określając liczbę zadań. Każde zadanie reprezentuje jedną działającą kopię kontenera. Używanie wielu zadań jest przydatne, jeśli każde z nich może niezależnie przetwarzać podzbiór danych. Na przykład przetworzenie 10 tys. rekordów z Cloud SQL lub 10 tys. plików z Cloud Storage może być szybsze, jeśli 10 zadań będzie przetwarzać po 1000 rekordów lub plików równolegle.

Korzystanie z zadań Cloud Run to proces dwuetapowy:

  1. Utwórz zadanie: obejmuje to całą konfigurację potrzebną do uruchomienia zadania, np. obraz kontenera, region i zmienne środowiskowe.
  2. Uruchom zadanie: spowoduje to utworzenie nowego wykonania zadania. Opcjonalnie możesz skonfigurować uruchamianie zadania zgodnie z harmonogramem za pomocą usługi Cloud Scheduler.

W tym laboratorium najpierw zapoznasz się z aplikacją Node.js, która robi zrzuty ekranu stron internetowych i zapisuje je w Cloud Storage. Następnie skompilujesz obraz kontenera aplikacji, uruchomisz go w zadaniach Cloud Run, zaktualizujesz zadanie, aby przetwarzać więcej stron internetowych, i uruchomisz je zgodnie z harmonogramem za pomocą Cloud Scheduler.

Czego się nauczysz

  • Jak używać aplikacji do robienia zrzutów ekranu ze stron internetowych.
  • Jak utworzyć obraz kontenera aplikacji.
  • Jak utworzyć zadanie Cloud Run dla aplikacji.
  • Jak uruchomić aplikację jako zadanie Cloud Run.
  • Jak zaktualizować zadanie.
  • Jak zaplanować zadanie za pomocą usługi Cloud Scheduler.

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 lub Google Workspace, musisz je utworzyć.

295004821bab6a87.png

37d264871000675d.png

96d86d3d5655cdbe.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. Zawsze możesz ją zaktualizować.
  • Identyfikator projektu jest unikalny we wszystkich projektach Google Cloud i 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 ćwiczeń z programowania musisz odwoływać się do identyfikatora projektu (zwykle oznaczanego jako PROJECT_ID). Jeśli wygenerowany identyfikator Ci się nie podoba, możesz wygenerować inny losowy identyfikator. Możesz też spróbować własnej nazwy i sprawdzić, czy jest dostępna. Po tym kroku nie można go zmienić i pozostaje on taki przez cały czas trwania projektu.
  • Warto wiedzieć, że istnieje trzecia wartość, numer projektu, której 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. Wykonanie tego laboratorium nie będzie kosztować dużo, a może nawet nic. Aby wyłączyć zasoby i uniknąć naliczania opłat po zakończeniu tego samouczka, możesz usunąć utworzone zasoby lub projekt. Nowi użytkownicy Google Cloud mogą skorzystać z programu bezpłatnego okresu próbnego o wartoś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 Google Cloud kliknij ikonę Cloud Shell na pasku narzędzi w prawym górnym rogu:

Aktywowanie Cloud Shell

Uzyskanie dostępu do środowiska i połączenie się z nim powinno zająć tylko kilka chwil. Po zakończeniu powinny pojawić się dane wyjściowe podobne do tych:

Zrzut ekranu terminala Google Cloud Shell pokazujący, że środowisko zostało połączone

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 laboratorium możesz wykonać w przeglądarce. Nie musisz niczego instalować.

Konfigurowanie gcloud

W Cloud Shell ustaw identyfikator projektu i region, w którym chcesz wdrożyć zadanie Cloud Run. Zapisz je jako zmienne PROJECT_IDREGION. W przyszłości będzie można wybrać region z lokalizacji Cloud Run.

PROJECT_ID=[YOUR-PROJECT-ID]
REGION=us-central1
gcloud config set core/project $PROJECT_ID

Włącz interfejsy API

Włącz wszystkie niezbędne usługi:

gcloud services enable \
  artifactregistry.googleapis.com \
  cloudbuild.googleapis.com \
  run.googleapis.com

3. Pobierz kod

Najpierw zapoznasz się z aplikacją Node.js, która robi zrzuty ekranu stron internetowych i zapisuje je w Cloud Storage. Później utworzysz obraz kontenera dla aplikacji i uruchomisz go jako zadanie w Cloud Run.

W Cloud Shell uruchom to polecenie, aby sklonować kod aplikacji z repozytorium tego:

git clone https://github.com/GoogleCloudPlatform/jobs-demos.git

Przejdź do katalogu zawierającego aplikację:

cd jobs-demos/screenshot

Powinien wyświetlić się taki układ pliku:

screenshot
 |
 ├── Dockerfile
 ├── README.md
 ├── screenshot.js
 ├── package.json

Oto krótki opis każdego pliku:

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

4. Poznaj kod

Aby przejrzeć kod, użyj wbudowanego edytora tekstu, klikając przycisk Open Editor u góry okna Cloud Shell.

15a2cdc9b7f6dfc6.png

Oto krótki opis każdego pliku.

screenshot.js

screenshot.js najpierw dodaje Puppeteer i Cloud Storage jako zależności. Puppeteer to biblioteka Node.js, której możesz używać do robienia zrzutów ekranu stron internetowych:

const puppeteer = require('puppeteer');
const {Storage} = require('@google-cloud/storage');

Jest funkcja initBrowser do inicjowania Puppeteer i funkcja takeScreenshot do robienia zrzutów ekranu z danego adresu URL:

async function initBrowser() {
  console.log('Initializing browser');
  return await puppeteer.launch();
}

async function takeScreenshot(browser, url) {
  const page = await browser.newPage();

  console.log(`Navigating to ${url}`);
  await page.goto(url);

  console.log(`Taking a screenshot of ${url}`);
  return await page.screenshot({
    fullPage: true
  });
}

Następnie znajduje się funkcja pobierania lub tworzenia zasobnika Cloud Storage oraz kolejna funkcja przesyłania zrzutu ekranu strony internetowej do zasobnika:

async function createStorageBucketIfMissing(storage, bucketName) {
  console.log(`Checking for Cloud Storage bucket '${bucketName}' and creating if not found`);
  const bucket = storage.bucket(bucketName);
  const [exists] = await bucket.exists();
  if (exists) {
    // Bucket exists, nothing to do here
    return bucket;
  }

  // Create bucket
  const [createdBucket] = await storage.createBucket(bucketName);
  console.log(`Created Cloud Storage bucket '${createdBucket.name}'`);
  return createdBucket;
}

async function uploadImage(bucket, taskIndex, imageBuffer) {
  // Create filename using the current time and task index
  const date = new Date();
  date.setMinutes(date.getMinutes() - date.getTimezoneOffset());
  const filename = `${date.toISOString()}-task${taskIndex}.png`;

  console.log(`Uploading screenshot as '${filename}'`)
  await bucket.file(filename).save(imageBuffer);
}

Funkcja main jest punktem wejścia:

async function main(urls) {
  console.log(`Passed in urls: ${urls}`);

  const taskIndex = process.env.CLOUD_RUN_TASK_INDEX || 0;
  const url = urls[taskIndex];
  if (!url) {
    throw new Error(`No url found for task ${taskIndex}. Ensure at least ${parseInt(taskIndex, 10) + 1} url(s) have been specified as command args.`);
  }
  const bucketName = process.env.BUCKET_NAME;
  if (!bucketName) {
    throw new Error('No bucket name specified. Set the BUCKET_NAME env var to specify which Cloud Storage bucket the screenshot will be uploaded to.');
  }

  const browser = await initBrowser();
  const imageBuffer = await takeScreenshot(browser, url).catch(async err => {
    // Make sure to close the browser if we hit an error.
    await browser.close();
    throw err;
  });
  await browser.close();

  console.log('Initializing Cloud Storage client')
  const storage = new Storage();
  const bucket = await createStorageBucketIfMissing(storage, bucketName);
  await uploadImage(bucket, taskIndex, imageBuffer);

  console.log('Upload complete!');
}

main(process.argv.slice(2)).catch(err => {
  console.error(JSON.stringify({severity: 'ERROR', message: err.message}));
  process.exit(1);
});

Kilka uwag o metodzie main:

  • Adresy URL są przekazywane jako argumenty.
  • Nazwa zasobnika jest przekazywana jako zdefiniowana przez użytkownika zmienna środowiskowa BUCKET_NAME. Nazwa zasobnika musi być globalnie unikalna w całej usłudze Google Cloud.
  • Zmienna środowiskowa CLOUD_RUN_TASK_INDEX jest przekazywana przez zadania Cloud Run. Zadania Cloud Run mogą uruchamiać wiele kopii aplikacji jako unikalne działania. CLOUD_RUN_TASK_INDEX to indeks uruchomionego zadania. Domyślnie ma wartość zero, gdy kod jest uruchamiany poza zadaniami Cloud Run. Gdy aplikacja jest uruchamiana jako wiele zadań, każde zadanie/kontener pobiera adres URL, za który jest odpowiedzialny, robi zrzut ekranu i zapisuje obraz w zasobniku.

package.json

Plik package.json definiuje aplikację i określa zależności dla Cloud Storage i Puppeteer:

{
  "name": "screenshot",
  "version": "1.0.0",
  "description": "Create a job to capture screenshots",
  "main": "screenshot.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "Google LLC",
  "license": "Apache-2.0",
  "dependencies": {
    "@google-cloud/storage": "^5.18.2",
    "puppeteer": "^13.5.1"
  }
}

Dockerfile

Dockerfile definiuje obraz kontenera dla aplikacji ze wszystkimi wymaganymi bibliotekami i zależnościami:

FROM ghcr.io/puppeteer/puppeteer:16.1.0
COPY package*.json ./
RUN npm ci --omit=dev
COPY . .
ENTRYPOINT ["node", "screenshot.js"]

5. Wdrażanie zadania

Zanim utworzysz zadanie, musisz utworzyć konto usługi, którego będziesz używać do jego uruchamiania.

gcloud iam service-accounts create screenshot-sa --display-name="Screenshot app service account"

Przyznaj kontu usługi rolę storage.admin, aby można było go używać do tworzenia zasobników i obiektów.

gcloud projects add-iam-policy-binding $PROJECT_ID \
  --role roles/storage.admin \
  --member serviceAccount:screenshot-sa@$PROJECT_ID.iam.gserviceaccount.com

Możesz teraz wdrożyć zadanie Cloud Run, które zawiera konfigurację potrzebną do jego uruchomienia.

gcloud beta run jobs deploy screenshot \
  --source=. \
  --args="https://example.com" \
  --args="https://cloud.google.com" \
  --tasks=2 \
  --task-timeout=5m \
  --region=$REGION \
  --set-env-vars=BUCKET_NAME=screenshot-$PROJECT_ID-$RANDOM \
  --service-account=screenshot-sa@$PROJECT_ID.iam.gserviceaccount.com

Wykorzystuje to wdrażanie na podstawie źródła i tworzy zadanie Cloud Run bez jego wykonywania.

Zwróć uwagę, że strony internetowe są przekazywane jako argumenty. Nazwa zasobnika, w którym mają być zapisywane zrzuty ekranu, jest przekazywana jako zmienna środowiskowa.

Możesz uruchomić równolegle wiele kopii kontenera, określając liczbę zadań do wykonania za pomocą flagi --tasks. Każde zadanie reprezentuje jedną działającą kopię kontenera. Używanie wielu zadań jest przydatne, jeśli każde z nich może niezależnie przetwarzać podzbiór danych. Aby to ułatwić, każde zadanie zna swój indeks, który jest przechowywany w zmiennej środowiskowej CLOUD_RUN_TASK_INDEX. Twój kod odpowiada za określenie, które zadanie obsługuje który podzbiór danych. Zwróć uwagę na symbol --tasks=2 w tym przykładzie. Dzięki temu uruchomimy 2 kontenery dla 2 adresów URL, które chcemy przetworzyć.

Każde zadanie może trwać maksymalnie 24 godziny. Możesz skrócić ten czas oczekiwania za pomocą flagi --task-timeout, tak jak w tym przykładzie. Aby zadanie zakończyło się sukcesem, musisz wykonać wszystkie działania. Domyślnie zadania zakończone niepowodzeniem nie są ponawiane. Możesz skonfigurować ponawianie zadań w przypadku ich niepowodzenia. Jeśli którekolwiek zadanie przekroczy liczbę ponownych prób, całe zadanie się nie powiedzie.

Domyślnie zadanie będzie uruchamiać jak najwięcej działań równolegle. Będzie to liczba zadań w Twoim projekcie, maksymalnie 100. W przypadku zadań, które uzyskują dostęp do backendu o ograniczonej skalowalności, możesz ustawić niższą równoległość. Na przykład baza danych, która obsługuje ograniczoną liczbę aktywnych połączeń. Możesz zmniejszyć wczytywanie równoległe za pomocą flagi --parallelism.

6. Uruchom zadanie

Przed uruchomieniem zadania wyświetl listę zadań, aby sprawdzić, czy zostało ono utworzone:

gcloud run jobs list

✔
JOB: screenshot
REGION: us-central
LAST RUN AT:
CREATED: 2022-02-22 12:20:50 UTC

Uruchom zadanie za pomocą tego polecenia:

gcloud run jobs execute screenshot --region=$REGION

Spowoduje to wykonanie zadania. Możesz wyświetlić listę bieżących i wcześniejszych wykonań:

gcloud run jobs executions list --job screenshot --region=$REGION

...
JOB: screenshot
EXECUTION: screenshot-znkmm
REGION: $REGION
RUNNING: 1
COMPLETE: 1 / 2
CREATED: 2022-02-22 12:40:42 UTC

Opisz wykonanie. Powinien wyświetlić się zielony znacznik wyboru i komunikat tasks completed successfully:

gcloud run jobs executions describe screenshot-znkmm --region=$REGION

✔ Execution screenshot-znkmm in region $REGION
2 tasks completed successfully


Image:           $REGION-docker.pkg.dev/$PROJECT_ID/containers/screenshot at 311b20d9...
Tasks:           2
Args:            https://example.com https://cloud.google.com
Memory:          1Gi
CPU:             1000m
Task Timeout:    3600s
Parallelism:     2
Service account: 11111111-compute@developer.gserviceaccount.com
Env vars:
  BUCKET_NAME    screenshot-$PROJECT_ID-$RANDOM

Stan możesz też sprawdzić na stronie zadań Cloud Run w konsoli Cloud:

1afde14d65f0d9ce.png

Jeśli sprawdzisz zasobnik Cloud Storage, zobaczysz 2 utworzone pliki z zrzutami ekranu:

7c4d355f6f65106.png

Czasami może być konieczne zatrzymanie wykonania przed jego zakończeniem, np. gdy zorientujesz się, że musisz uruchomić zadanie z innymi parametrami, lub gdy w kodzie wystąpi błąd i nie chcesz niepotrzebnie wykorzystywać czasu obliczeniowego.

Aby zatrzymać wykonywanie zadania, musisz je usunąć:

gcloud run jobs executions delete screenshot-znkmm --region=$REGION

7. Aktualizowanie zadania

Nowe wersje kontenera nie są automatycznie wybierane przez zadania Cloud Run podczas następnego wykonania. Jeśli zmienisz kod zadania, musisz ponownie utworzyć kontener i zaktualizować zadanie. Używanie otagowanych obrazów pomoże Ci określić, która wersja obrazu jest obecnie używana.

Podobnie, jeśli chcesz zaktualizować niektóre zmienne konfiguracji, musisz też zaktualizować zadanie. Kolejne wykonania zadania będą korzystać z nowego kontenera i ustawień konfiguracji.

Zaktualizuj zadanie i zmień strony, z których aplikacja robi zrzuty ekranu, za pomocą flagi --args. Zaktualizuj też flagę --tasks, aby odzwierciedlała liczbę stron.

gcloud run jobs update screenshot \
  --args="https://www.pinterest.com" \
  --args="https://www.apartmenttherapy.com" \
  --args="https://www.google.com" \
  --region=$REGION \
  --tasks=3

Ponownie uruchom zadanie. Przekaż ten czas w parametrze --wait, aby poczekać na zakończenie działań:

gcloud run jobs execute screenshot --region=$REGION --wait

Po kilku sekundach w zasobniku powinny pojawić się 3 kolejne zrzuty ekranu:

ed0cbe0b5a5f9144.png

8. Planowanie zadania

Dotychczas zadania były uruchamiane ręcznie. W rzeczywistości prawdopodobnie chcesz uruchamiać zadania w odpowiedzi na zdarzenie lub zgodnie z harmonogramem. Zobaczmy, jak uruchomić zadanie zrzutu ekranu zgodnie z harmonogramem za pomocą usługi Cloud Scheduler.

Najpierw upewnij się, że interfejs Cloud Scheduler API jest włączony:

gcloud services enable cloudscheduler.googleapis.com

Otwórz stronę szczegółów zadań Cloud Run i kliknij sekcję Triggers:

3ae456368905472f.png

Kliknij przycisk Add Scheduler Trigger:

48cbba777f75e1eb.png

Po prawej stronie otworzy się panel. Utwórz zadanie usługi Scheduler, które będzie uruchamiane codziennie o godzinie 9:00 z tą konfiguracją, i kliknij Continue:

81fd098be0db216.png

Na następnej stronie wybierz domyślne konto usługi obliczeniowej i kliknij Create:

fe479501dfb91f9f.png

Powinna się teraz wyświetlać nowo utworzona reguła Cloud Scheduler:

5a7bc6d96b970b92.png

Kliknij View Details, aby otworzyć stronę Cloud Scheduler.

Możesz poczekać do godziny 9:00, aż harmonogram zostanie uruchomiony, lub ręcznie uruchomić Cloud Scheduler, klikając Force Run:

959525f2c8041a6a.png

Po kilku sekundach powinno pojawić się powiadomienie o pomyślnym wykonaniu zadania Cloud Scheduler:

d64e03fc84d61145.png

Powinny się też pojawić 3 dodatkowe zrzuty ekranu dodane przez wywołanie z Cloud Scheduler:

56398a0e827de8b0.png

9. Gratulacje

Gratulacje! Codelab został ukończony.

Czyszczenie (opcjonalnie)

Aby uniknąć obciążenia konta opłatami, warto usunąć zasoby.

Jeśli nie potrzebujesz projektu, możesz go po prostu usunąć:

gcloud projects delete $PROJECT_ID

Jeśli potrzebujesz projektu, możesz usunąć poszczególne zasoby.

Usuń kod źródłowy:

rm -rf ~/jobs-demos/

Usuń repozytorium Artifact Registry:

gcloud artifacts repositories delete containers --location=$REGION

Usuń konto usługi:

gcloud iam service-accounts delete screenshot-sa@$PROJECT_ID.iam.gserviceaccount.com

Usuń zadanie Cloud Run:

gcloud run jobs delete screenshot --region=$REGION

Usuń zadanie Cloud Scheduler:

gcloud scheduler jobs delete screenshot-scheduler-trigger --location=$REGION

Usuń zasobnik Cloud Storage:

gcloud storage rm --recursive gs://screenshot-$PROJECT_ID

Omówione zagadnienia

  • Jak używać aplikacji do robienia zrzutów ekranu ze stron internetowych.
  • Jak utworzyć obraz kontenera aplikacji.
  • Jak utworzyć zadanie Cloud Run dla aplikacji.
  • Jak uruchomić aplikację jako zadanie Cloud Run.
  • Jak zaktualizować zadanie.
  • Jak zaplanować zadanie za pomocą usługi Cloud Scheduler.