1. 簡介
總覽
Cloud Run 服務適合會無限期監聽 HTTP 要求的容器,Cloud Run「工作」則更適合要執行完成 (目前最多 24 小時) 且不會處理要求的容器。例如,如果以 Cloud Run 工作的形式實作,就能處理資料庫記錄、處理 Cloud Storage 值區中的檔案清單,或計算 Pi 等長時間執行的作業。
工作無法提供要求或監聽通訊埠。這代表工作與 Cloud Run 服務不同,不應組合網路伺服器。工作容器應在完成後結束。
在 Cloud Run 工作中,您可以指定多個任務,並行執行多個容器副本。每個工作都代表一個執行中的容器副本。如果每項工作都能獨立處理部分資料,這時就適合使用多項工作。舉例來說,處理 Cloud SQL 中的 10,000 筆記錄或 Cloud Storage 中的 10,000 個檔案,可以分別處理 10 項工作或 10 項工作,加快處理速度。
使用 Cloud Run 工作分為兩個步驟:
- 建立工作:包含執行工作所需的所有設定,例如容器映像檔、區域、環境變數。
- 執行工作:這會建立新的工作執行作業。或者,您也可以使用 Cloud Scheduler 將工作設為按排程執行。
在本程式碼研究室中,您將先探索 Node.js 應用程式,用於擷取網頁的螢幕截圖,並儲存至 Cloud Storage。接著,您會建構應用程式的容器映像檔、在 Cloud Run 工作中執行、更新工作以處理更多網頁,並透過 Cloud Scheduler 按排程執行工作。
課程內容
- 如何使用應用程式擷取網頁的螢幕截圖。
- 如何建構應用程式的容器映像檔。
- 如何為應用程式建立 Cloud Run 工作。
- 如何將應用程式做為 Cloud Run 工作執行。
- 如何更新工作。
- 如何使用 Cloud Scheduler 排定工作時程。
2. 設定和需求
自修環境設定
- 登入 Google Cloud 控制台,建立新專案或重複使用現有專案。如果您還沒有 Gmail 或 Google Workspace 帳戶,請先建立帳戶。
- 「專案名稱」是這項專案參與者的顯示名稱。這是 Google API 未使用的字元字串。您可以隨時更新付款方式。
- 所有 Google Cloud 專案的專案 ID 均不得重複,而且設定後即無法變更。Cloud 控制台會自動產生一個不重複的字串。但通常是在乎它何在在大部分的程式碼研究室中,您必須參照專案 ID (通常為
PROJECT_ID
)。如果您對產生的 ID 不滿意,可以隨機產生一個 ID。或者,您也可以自行嘗試,看看是否支援。在這個步驟後,這個名稱即無法變更,而且在專案期間內仍會保持有效。 - 資訊中的第三個值是專案編號,部分 API 會使用這個編號。如要進一步瞭解這三個值,請參閱說明文件。
- 接下來,您需要在 Cloud 控制台中啟用計費功能,才能使用 Cloud 資源/API。執行本程式碼研究室不會產生任何費用 (如果有的話)。如要關閉資源,以免產生本教學課程結束後產生的費用,您可以刪除自己建立的資源或刪除專案。新使用者符合 $300 美元免費試用計畫的資格。
啟動 Cloud Shell
雖然 Google Cloud 可以從筆記型電腦遠端操作,但在本程式碼研究室中,您將使用 Google Cloud Shell,這是一種在 Cloud 中執行的指令列環境。
在 Google Cloud 控制台,按一下右上方的工具列上的 Cloud Shell 圖示:
佈建並連線至環境的作業只需幾分鐘的時間。完成後,您應該會看到類似下方的內容:
這部虛擬機器都裝載了您需要的所有開發工具。提供永久的 5 GB 主目錄,而且在 Google Cloud 中運作,大幅提高網路效能和驗證能力。本程式碼研究室的所有工作都可以在瀏覽器中完成。不必安裝任何程式。
設定 gcloud
在 Cloud Shell 中設定專案 ID 和要部署 Cloud Run 工作的區域。將其儲存為 PROJECT_ID
和 REGION
變數。您日後可以從其中一個 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
按鈕,使用內建的文字編輯器。
以下是每個檔案的簡短說明。
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 值區的函式,以及另一個可將網頁螢幕截圖上傳至值區的函式:
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 工作之外執行時,預設值為零。當應用程式以多項工作的形式執行時,每個工作/容器都會收到負責的網址並擷取螢幕截圖,然後將圖片儲存至值區。
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
角色授予服務帳戶,以便用來建立值區和物件。
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 \ --service-account=screenshot-sa@$PROJECT_ID.iam.gserviceaccount.com
這會使用以來源為基礎的部署方式,並在不執行的情況下建立 Cloud Run 工作。
請注意網頁是以引數的形式傳入。用來儲存螢幕截圖的值區名稱會以環境變數的形式傳入。
您可以使用 --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
您也可以前往 Cloud 控制台的 Cloud Run 工作頁面查看狀態:
如果您查看 Cloud Storage 值區,應該會看到已建立的兩個螢幕截圖檔案:
您有時可能需要在執行作業完成前就停止執行作業。這可能是因為您覺得必須使用不同參數來執行工作,或是程式碼發生錯誤,因此您不需要使用不必要的運算時間。
如要停止執行工作,您需要刪除執行作業:
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 張螢幕截圖:
8. 排定工作
到目前為止,您都是以手動方式執行工作。在現實生活中,您可能會想依照事件或排程執行工作。我們來看看如何使用 Cloud Scheduler 依排程執行螢幕截圖工作。
首先,請確認 Cloud Scheduler API 已啟用:
gcloud services enable cloudscheduler.googleapis.com
前往 Cloud Run 工作詳細資料頁面,然後按一下 Triggers
部分:
選取 Add Scheduler Trigger
按鈕:
右側隨即會開啟面板。以這個設定建立每天 9:00 執行的排程器工作,然後選取 Continue
:
在下一頁選取預設的運算服務帳戶,然後選取 Create
:
現在,您應該會看到新建立的 Cloud Scheduler 觸發條件:
按一下 View Details
前往「Cloud Scheduler」頁面。
您可以等到上午 9 點再啟動排程器,也可以選取 Force Run
,手動觸發 Cloud Scheduler:
幾秒後,您應該會看到 Cloud Scheduler 工作已成功執行:
您也會看到 Cloud Scheduler 呼叫新增了 3 張螢幕截圖:
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 值區:
gcloud storage rm --recursive gs://screenshot-$PROJECT_ID
涵蓋內容
- 如何使用應用程式擷取網頁的螢幕截圖。
- 如何建構應用程式的容器映像檔。
- 如何為應用程式建立 Cloud Run 工作。
- 如何將應用程式做為 Cloud Run 工作執行。
- 如何更新工作。
- 如何使用 Cloud Scheduler 排定工作時程。