Wprowadzenie do zadań Cloud Run

1. Wprowadzenie

1965fab24c502bd5.png

Omówienie

Usługi Cloud Run dobrze sprawdzają się w przypadku kontenerów, które działają bezterminowo i nasłuchują żądań HTTP. Z kolei zadania w Cloud Run lepiej sprawdzają się w przypadku kontenerów, które działają do końca (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 (np. obliczanie liczby π) to dobre rozwiązanie, gdy zostanie wdrożone jako zadanie Cloud Run.

Zadania nie mają możliwości obsługi żądań ani nasłuchiwania na porcie. Oznacza to, że w przeciwieństwie do usług Cloud Run zadania nie powinny być połączone z serwerem WWW. Zamiast tego kontenery zadań powinny być zamykane po zakończeniu.

Określ liczbę działań, aby w zadaniach Cloud Run równolegle uruchamiać wiele kopii kontenera. Każde zadanie odpowiada jednej bieżącej kopii kontenera. Używanie wielu zadań jest przydatne, jeśli każde z nich może niezależnie przetwarzać podzbiór danych. Na przykład przetwarzanie 10 tys. rekordów z Cloud SQL lub 10 000 plików z Cloud Storage można było wykonać szybciej, gdyby 10 zadań przetwarzało 1000 rekordów lub plików jednocześnie.

Korzystanie z zadań Cloud Run to proces dwuetapowy:

  1. Utwórz zadanie:zawiera całą konfigurację potrzebną do uruchomienia zadania, w tym obraz kontenera, region i zmienne środowiskowe.
  2. Uruchomienie zadania: powoduje utworzenie nowego wykonania zadania. Opcjonalnie możesz skonfigurować zadanie tak, aby było uruchamiane zgodnie z harmonogramem za pomocą usługi Cloud Scheduler.

W ramach tego ćwiczenia w Codelabs dowiesz się, jak używać aplikacji Node.js i robisz zrzuty ekranu stron internetowych, a następnie zapisujesz je w Cloud Storage. Następnie utworzysz obraz kontenera aplikacji, uruchomisz go w zadaniach Cloud Run, zaktualizujesz zadanie, aby przetwarzało więcej stron internetowych, i uruchamiaj je zgodnie z harmonogramem za pomocą usługi Cloud Scheduler.

Czego się nauczysz

  • Jak używać aplikacji do robienia zrzutów ekranu ze stronami internetowymi.
  • 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 wykorzystaj już istniejący. Jeśli nie masz jeszcze konta Gmail ani Google Workspace, musisz je utworzyć.

295004821bab6a87.png

37d264871000675d.png

96d86d3d5655cdbe.png

  • Nazwa projektu jest wyświetlaną nazwą uczestników tego projektu. To ciąg znaków, który nie jest używany przez interfejsy API Google. W każdej chwili możesz ją zaktualizować.
  • Identyfikator projektu jest unikalny we wszystkich projektach Google Cloud i nie można go zmienić (po jego ustawieniu nie można go zmienić). Cloud Console automatycznie wygeneruje unikalny ciąg znaków. zwykle nieważne, co ona jest. W większości ćwiczeń w Codelabs musisz podać swój identyfikator projektu (zwykle identyfikowany jako PROJECT_ID). Jeśli nie podoba Ci się wygenerowany identyfikator, możesz wygenerować kolejny losowy. Możesz też spróbować własnych sił i sprawdzić, czy jest dostępna. Po wykonaniu tej czynności nie można jej już zmienić. Pozostanie ona przez cały czas trwania projektu.
  • Jest jeszcze trzecia wartość, numer projektu, z którego korzystają 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 Cloud Console, aby korzystać z zasobów Cloud/interfejsów API. Ukończenie tego ćwiczenia z programowania nic nie kosztuje. Aby wyłączyć zasoby w celu uniknięcia 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

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 chmurze.

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

84688aa223b1c3a2.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:

320e18fedb7fbe0.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 ramach tego ćwiczenia z programowania można wykonywać 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_ID i REGION. W przyszłości będzie można wybrać region z jednej 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 zapoznaj się z aplikacją Node.js, aby zrobić zrzuty ekranu stron internetowych i zapisać je w Cloud Storage. Później utworzysz obraz kontenera aplikacji i uruchomisz go jako zadanie w Cloud Run.

Aby skopiować kod aplikacji z tego repozytorium, uruchom w Cloud Shell to polecenie:

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

Przejdź do katalogu zawierającego aplikację:

cd jobs-demos/screenshot

Powinien pojawić 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 definiuje zależności biblioteki.
  • Dockerfile definiuje obraz kontenera.

4. Zapoznaj się z kodem

Aby zapoznać się z kodem, użyj wbudowanego edytora tekstu. W tym celu kliknij przycisk Open Editor u góry okna Cloud Shell.

15a2cdc9b7f6dfc6.png

Oto krótkie wyjaśnienie każdego z tych plików.

screenshot.js

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

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

Istnieje funkcja initBrowser do inicjowania Puppeteer oraz 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
  });
}

Kolejna funkcja pozwala pobrać lub utworzyć zasobnik Cloud Storage albo dodać do zasobnika zrzut ekranu strony internetowej:

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

Na koniec 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);
});

Zwróć uwagę na te kwestie 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 we wszystkich usługach Google Cloud.
  • Zmienna środowiskowa CLOUD_RUN_TASK_INDEX jest przekazywana przez zadania Cloud Run. Zadania Cloud Run mogą uruchamiać wiele kopii aplikacji jako unikalne zadania. CLOUD_RUN_TASK_INDEX reprezentuje indeks uruchomionego zadania. Wartość domyślna to 0, gdy kod jest uruchamiany poza zadaniami Cloud Run. Gdy aplikacja jest uruchamiana jako kilka zadań, każde zadanie/kontener pobiera URL, za który odpowiada, 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 określa obraz kontenera 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 uruchamiania zadania.

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

Przypisz do konta usługi rolę storage.admin, aby mogła być używana 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ę niezbędną 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 \
  --service-account=screenshot-sa@$PROJECT_ID.iam.gserviceaccount.com

Wykorzystuje wdrożenie na podstawie źródła i tworzy zadanie Cloud Run bez jego wykonania.

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

Możesz równolegle uruchamiać wiele kopii kontenera, określając liczbę zadań do uruchomienia z flagą --tasks. Każde zadanie odpowiada jednej bieżącej kopii 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 wybrany podzbiór danych. Zwróć uwagę na --tasks=2 w tym fragmencie. Dzięki temu 2 kontenery będą działać w przypadku 2 adresów URL, które chcemy przetworzyć.

Każde zadanie może działać przez maksymalnie 24 godziny. Możesz skrócić ten czas za pomocą flagi --task-timeout, jak w tym przykładzie. Aby zadanie zostało ukończone, wszystkie zadania muszą zostać zakończone powodzeniem. Domyślnie nieudane zadania nie są ponawiane. Możesz skonfigurować powtarzanie zadań, które mają być ponawiane w przypadku niepowodzenia. Jeśli któreś zadanie przekroczy liczbę ponownych prób, całe zadanie zakończy się niepowodzeniem.

Domyślnie Twoje zadanie będzie uruchamiane z jak największą liczbą zadań równolegle. Jest ona równa liczbie zadań dla Twojego zadania i może wynosić maksymalnie 100. Możesz zmniejszyć poziom równoległości w przypadku zadań uzyskujących dostęp do backendu o ograniczonej skalowalności. Może to być na przykład baza danych, która obsługuje ograniczoną liczbę aktywnych połączeń. Możesz zmniejszyć poziom równoległości za pomocą flagi --parallelism.

6. Uruchom zadanie

Przed uruchomieniem zadania wyświetl listę, aby sprawdzić, czy zostało 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. Powinny się wyświetlić 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

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

1afde14d65f0d9ce.png

W zasobniku Cloud Storage powinny być widoczne 2 utworzone pliki zrzutów ekranu:

7c4d355f6f65106.png

Czasami konieczne może być zatrzymanie wykonania przed jego zakończeniem – na przykład dlatego, że zdajesz sobie sprawę, że musisz uruchomić zadanie z innymi parametrami lub w kodzie występuje błąd i nie chcesz używać niepotrzebnego czasu obliczeniowego.

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

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

7. Aktualizowanie zadania

Nowe wersje kontenera nie będą automatycznie pobierane przez zadania Cloud Run przy następnym wykonaniu. Jeśli zmienisz kod zadania, musisz ponownie skompilować kontener i zaktualizować zadanie. Użycie otagowanych obrazów pomoże Ci ustalić, która wersja obrazu jest obecnie używana.

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

Zaktualizuj zadanie i zmień strony, których aplikacja robi zrzuty ekranu, we fladze --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

Uruchom zadanie ponownie. Ten przedział czasu we fladze --wait w oczekiwaniu na zakończenie wykonań:

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

Po kilku sekundach do zasobnika powinny pojawić się jeszcze 3 zrzuty ekranu:

ed0cbe0b5a5f9144.png

8. Zaplanuj zadanie

Obecnie uruchamiasz zadania ręcznie. W rzeczywistości możesz prawdopodobnie uruchamiać zadania w odpowiedzi na wydarzenie 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ę z informacjami o zadaniach Cloud Run i kliknij sekcję Triggers:

3ae456368905472f.png

Kliknij przycisk Add Scheduler Trigger:

48cbba777f75e1eb.png

Po prawej stronie wyświetli się panel. Utwórz zadanie algorytmu szeregowania, które będzie uruchamiane codziennie o 9:00 z tą konfiguracją, i wybierz Continue:

81fd098be0db216.png

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

fe479501dfb91f9f.png

Powinien zostać utworzony nowy aktywator Cloud Scheduler:

5a7bc6d96b970b92.png

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

Możesz zaczekać do 9:00, aż algorytm szeregowania zacznie działać. Możesz też ręcznie aktywować Cloud Scheduler, wybierając Force Run:

959525f2c8041a6a.png

Po kilku sekundach zadanie Cloud Scheduler powinno zostać wykonane:

d64e03fc84d61145.png

Powinny być również widoczne 3 dodatkowe zrzuty ekranu dodane przez wywołanie pochodzące z usługi Cloud Scheduler:

56398a0e827de8b0.png

9. Gratulacje

Gratulacje. Udało Ci się ukończyć ćwiczenia z programowania.

Czyszczenie (opcjonalnie)

Aby uniknąć opłat, warto wyczyścić zasoby.

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

gcloud projects delete $PROJECT_ID

Jeśli potrzebujesz projektu, możesz usuwać zasoby pojedynczo.

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 stronami internetowymi.
  • 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.