開始使用 Cloud Run 工作

1. 簡介

1965fab24c502bd5.png

總覽

Cloud Run 服務適合用於無限期執行並監聽 HTTP 要求的容器,而 Cloud Run 工作則適合用於執行至完成 (目前最多 24 小時) 且不處理要求的容器。舉例來說,如果實作方式是 Cloud Run 工作,處理資料庫中的記錄、處理 Cloud Storage 值區中的檔案清單,或是計算 Pi 等長時間執行的作業,都會運作良好。

作業無法處理要求或監聽通訊埠。也就是說,工作不應像 Cloud Run 服務一樣,將網路伺服器封裝在一起。工作容器應在完成後結束。

在 Cloud Run 工作中,您可以指定工作數量,平行執行容器的多個副本。每個工作都代表容器的一個執行副本。如果每個工作都能獨立處理部分資料,使用多個工作就很有幫助。舉例來說,如果從 Cloud SQL 處理 10,000 筆記錄,或從 Cloud Storage 處理 10,000 個檔案,則可使用 10 項工作,每項工作平行處理 1,000 筆記錄或檔案,這樣就能更快完成。

使用 Cloud Run 工作時,須完成兩個步驟:

  1. 建立工作:這會封裝執行工作所需的所有設定,例如容器映像檔、區域、環境變數。
  2. 執行工作:這會建立新的工作執行作業。您可以選擇使用 Cloud Scheduler,依排程執行工作。

在本程式碼研究室中,您會先探索 Node.js 應用程式,瞭解如何擷取網頁螢幕截圖並儲存至 Cloud Storage。接著,您將為應用程式建構容器映像檔、在 Cloud Run 工作中執行該映像檔、更新工作以處理更多網頁,並使用 Cloud Scheduler 依排程執行工作。

課程內容

  • 如何使用應用程式擷取網頁螢幕截圖。
  • 如何建構應用程式的容器映像檔。
  • 如何為應用程式建立 Cloud Run 工作。
  • 如何以 Cloud Run 工作的形式執行應用程式。
  • 如何更新職缺。
  • 如何使用 Cloud Scheduler 安排工作排程。

2. 設定和需求條件

自行設定環境

  1. 登入 Google Cloud 控制台,然後建立新專案或重複使用現有專案。如果沒有 Gmail 或 Google Workspace 帳戶,請先建立帳戶

295004821bab6a87.png

37d264871000675d.png

96d86d3d5655cdbe.png

  • 專案名稱是這個專案參與者的顯示名稱。這是 Google API 未使用的字元字串。你隨時可以更新。
  • 專案 ID 在所有 Google Cloud 專案中都是不重複的,而且設定後即無法變更。Cloud 控制台會自動產生專屬字串,通常您不需要在意該字串為何。在大多數程式碼研究室中,您需要參照專案 ID (通常標示為 PROJECT_ID)。如果您不喜歡產生的 ID,可以產生另一個隨機 ID。你也可以嘗試使用自己的名稱,看看是否可用。完成這個步驟後就無法變更,且專案期間會維持不變。
  • 請注意,有些 API 會使用第三個值,也就是「專案編號」。如要進一步瞭解這三種值,請參閱說明文件
  1. 接著,您需要在 Cloud 控制台中啟用帳單,才能使用 Cloud 資源/API。完成這個程式碼研究室的費用不高,甚至可能完全免費。如要關閉資源,避免在本教學課程結束後繼續產生費用,您可以刪除建立的資源或專案。Google Cloud 新使用者可參加價值$300 美元的免費試用計畫。

啟動 Cloud Shell

雖然您可以透過筆電遠端操作 Google Cloud,但在本程式碼研究室中,您將使用 Google Cloud Shell,這是可在雲端執行的指令列環境。

Google Cloud 控制台中,點選右上角工具列的 Cloud Shell 圖示:

啟用 Cloud Shell

佈建並連線至環境的作業需要一些時間才能完成。完成後,您應該會看到如下的內容:

Google Cloud Shell 終端機的螢幕截圖,顯示環境已連線

這部虛擬機器搭載各種您需要的開發工具,並提供永久的 5GB 主目錄,而且在 Google Cloud 運作,能大幅提升網路效能並強化驗證功能。您可以在瀏覽器中完成本程式碼研究室的所有作業。您不需要安裝任何軟體。

設定 gcloud

在 Cloud Shell 中,設定專案 ID 和要部署 Cloud Run 工作的區域。分別儲存為 PROJECT_IDREGION 變數。日後您將可從 Cloud Run 支援的地區中選取區域。

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

啟用 API

啟用所有必要服務:

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

3. 取得程式碼

您首先會探索 Node.js 應用程式,擷取網頁的螢幕截圖並儲存至 Cloud Storage。稍後,您會為應用程式建構容器映像檔,並在 Cloud Run 上以工作形式執行。

在 Cloud Shell 中執行下列指令,從這個存放區複製應用程式程式碼:

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

前往包含應用程式的目錄:

cd jobs-demos/screenshot

您應該會看到這個檔案版面配置:

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

以下簡要說明各個檔案:

  • screenshot.js 包含應用程式的 Node.js 程式碼。
  • package.json 定義程式庫依附元件。
  • Dockerfile 定義容器映像檔。

4. 探索程式碼

如要探索程式碼,請點選 Cloud Shell 視窗頂端的 Open Editor 按鈕,使用內建的文字編輯器。

15a2cdc9b7f6dfc6.png

以下簡要說明各個檔案。

screenshot.js

screenshot.js 會先將 Puppeteer 和 Cloud Storage 新增為依附元件。Puppeteer 是用來擷取網頁螢幕截圖的 Node.js 程式庫:

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

您可以使用 initBrowser 函式初始化 Puppeteer,並使用 takeScreenshot 函式擷取指定網址的螢幕截圖:

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

接著,有一個函式可取得或建立 Cloud Storage bucket,另一個函式則可將網頁螢幕截圖上傳至 bucket:

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

最後,main 函式是進入點:

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

請注意 main 方法的下列事項:

  • 網址會以引數的形式傳遞。
  • 值區名稱會以使用者定義的 BUCKET_NAME 環境變數形式傳遞。值區名稱在整個 Google Cloud 中不得重複。
  • Cloud Run 工作會傳遞 CLOUD_RUN_TASK_INDEX 環境變數。Cloud Run 工作可將應用程式的多個副本做為獨立工作執行。CLOUD_RUN_TASK_INDEX 代表正在執行的工作索引。如果程式碼是在 Cloud Run 工作以外執行,則預設為零。應用程式以多項工作形式執行時,每項工作/容器都會擷取負責的網址、擷取螢幕截圖,並將圖片儲存到 bucket。

package.json

package.json 檔案會定義應用程式,並指定 Cloud Storage 和 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 會定義應用程式的容器映像檔,其中包含所有必要程式庫和依附元件:

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

5. 部署工作

建立作業前,請先建立用於執行作業的服務帳戶。

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

storage.admin 角色授予服務帳戶,以便建立 bucket 和物件。

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

您現在可以部署 Cloud Run 工作,其中包含執行工作所需的設定。

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

這會使用以來源為基礎的部署作業,並建立 Cloud Run 作業,但不會執行。

請注意,網頁是以引數形式傳遞。儲存螢幕截圖的 bucket 名稱會以環境變數形式傳遞。

您可以指定要使用 --tasks 標記執行的工作數量,並行執行多個容器副本。每個工作都代表容器的一個執行副本。如果每個工作都能獨立處理部分資料,使用多個工作就很有幫助。為方便執行這項作業,每個工作都會知道自己的索引,並儲存在 CLOUD_RUN_TASK_INDEX 環境變數中。您的程式碼負責判斷哪些工作會處理哪些資料子集。請注意這個範例中的 --tasks=2。這樣可確保系統為要處理的 2 個網址執行 2 個容器。

每項工作最多可執行 24 小時。如要縮短逾時時間,請使用 --task-timeout 標記,如本範例所示。所有工作都必須成功完成,工作才會視同成功。根據預設,系統不會重試失敗的工作。您可以設定在工作失敗時重試。如果任何工作超過重試次數,整個工作就會失敗。

依預設,工作會盡可能平行執行多項工作。這會等於工作任務數量,最多 100 個。如果工作存取的後端可擴充性有限,建議您將平行處理數量設為較低的值。舉例來說,資料庫支援的有效連線數量有限。您可以使用 --parallelism 旗標降低平行處理。

6. 執行工作

執行工作前,請列出工作,確認工作已建立:

gcloud run jobs list

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

使用下列指令執行作業:

gcloud run jobs execute screenshot --region=$REGION

這樣就會執行工作。您可以列出目前和過去的執行作業:

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

說明執行作業。畫面上應會顯示綠色勾號和 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

您也可以查看 Cloud 控制台的 Cloud Run 工作頁面,瞭解狀態:

1afde14d65f0d9ce.png

檢查 Cloud Storage bucket 時,您應該會看到建立的兩個螢幕截圖檔案:

7c4d355f6f65106.png

有時您可能需要在執行完成前停止執行,例如發現需要使用不同參數執行作業,或是程式碼有錯誤,不想浪費不必要的運算時間。

如要停止執行作業,請刪除執行作業:

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

7. 更新職缺

Cloud Run 作業不會在下次執行時自動採用新版容器。如果變更工作程式碼,您必須重建容器並更新工作。使用已加上標記的映像檔,有助於識別目前使用的映像檔版本。

同樣地,如要更新部分設定變數,也需要更新工作。後續執行作業時,系統會使用新的容器和設定。

更新工作,並在 --args 標記中變更應用程式擷取螢幕截圖的頁面。同時更新 --tasks 標記,反映頁面數量。

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

再次執行工作。這次傳遞 --wait 旗標,等待執行作業完成:

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

幾秒後,您應該會看到 3 個螢幕截圖新增至 bucket:

ed0cbe0b5a5f9144.png

8. 排定工作

到目前為止,您都是手動執行工作。在實際使用情況中,您可能想在事件發生時或依排程執行工作。接下來,我們將瞭解如何使用 Cloud Scheduler 依排程執行螢幕截圖工作。

首先,請確認已啟用 Cloud Scheduler API:

gcloud services enable cloudscheduler.googleapis.com

前往 Cloud Run 工作詳細資料頁面,然後按一下 Triggers 區段:

3ae456368905472f.png

選取「Add Scheduler Trigger」按鈕:

48cbba777f75e1eb.png

右側隨即會開啟面板。建立排程器工作,每天在 9:00 執行,並使用這項設定選取 Continue

81fd098be0db216.png

在下一頁中,選取預設的 Compute 服務帳戶,然後選取 Create

fe479501dfb91f9f.png

現在應該會看到新建立的 Cloud Scheduler 觸發條件:

5a7bc6d96b970b92.png

按一下 View Details 前往 Cloud Scheduler 頁面。

你可以等到上午 9 點,讓排程器啟動,也可以選取 Force Run 手動觸發 Cloud Scheduler:

959525f2c8041a6a.png

幾秒後,您應該會看到 Cloud Scheduler 工作已順利執行:

d64e03fc84d61145.png

您也應該會看到 Cloud Scheduler 呼叫新增的另外 3 個螢幕截圖:

56398a0e827de8b0.png

9. 恭喜

恭喜,您已完成本程式碼研究室!

清除 (選用)

為避免產生費用,建議您清除資源。

如果您不需要專案,可以刪除專案:

gcloud projects delete $PROJECT_ID

如果需要保留專案,可以個別刪除資源。

刪除原始碼:

rm -rf ~/jobs-demos/

刪除 Artifact Registry 存放區:

gcloud artifacts repositories delete containers --location=$REGION

刪除服務帳戶:

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

刪除 Cloud Run 工作:

gcloud run jobs delete screenshot --region=$REGION

刪除 Cloud Scheduler 工作:

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

刪除 Cloud Storage bucket:

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

涵蓋內容

  • 如何使用應用程式擷取網頁螢幕截圖。
  • 如何建構應用程式的容器映像檔。
  • 如何為應用程式建立 Cloud Run 工作。
  • 如何以 Cloud Run 工作的形式執行應用程式。
  • 如何更新職缺。
  • 如何使用 Cloud Scheduler 安排工作排程。