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 項工作,每項工作平行處理 1,000 筆記錄或檔案,這樣就能更快完成。
使用 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 未使用的字元字串。你隨時可以更新。
- 專案 ID 在所有 Google Cloud 專案中都是不重複的,而且設定後即無法變更。Cloud 控制台會自動產生專屬字串,通常您不需要在意該字串為何。在大多數程式碼研究室中,您需要參照專案 ID (通常標示為
PROJECT_ID)。如果您不喜歡產生的 ID,可以產生另一個隨機 ID。你也可以嘗試使用自己的名稱,看看是否可用。完成這個步驟後就無法變更,且專案期間會維持不變。 - 請注意,有些 API 會使用第三個值,也就是「專案編號」。如要進一步瞭解這三種值,請參閱說明文件。
- 接著,您需要在 Cloud 控制台中啟用帳單,才能使用 Cloud 資源/API。完成這個程式碼研究室的費用不高,甚至可能完全免費。如要關閉資源,避免在本教學課程結束後繼續產生費用,您可以刪除建立的資源或專案。Google Cloud 新使用者可參加價值$300 美元的免費試用計畫。
啟動 Cloud Shell
雖然您可以透過筆電遠端操作 Google Cloud,但在本程式碼研究室中,您將使用 Google Cloud Shell,這是可在雲端執行的指令列環境。
在 Google Cloud 控制台中,點選右上角工具列的 Cloud Shell 圖示:

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

這部虛擬機器搭載各種您需要的開發工具,並提供永久的 5GB 主目錄,而且在 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 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 工作頁面,瞭解狀態:

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

有時您可能需要在執行完成前停止執行,例如發現需要使用不同參數執行作業,或是程式碼有錯誤,不想浪費不必要的運算時間。
如要停止執行作業,請刪除執行作業:
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:

8. 排定工作
到目前為止,您都是手動執行工作。在實際使用情況中,您可能想在事件發生時或依排程執行工作。接下來,我們將瞭解如何使用 Cloud Scheduler 依排程執行螢幕截圖工作。
首先,請確認已啟用 Cloud Scheduler API:
gcloud services enable cloudscheduler.googleapis.com
前往 Cloud Run 工作詳細資料頁面,然後按一下 Triggers 區段:

選取「Add Scheduler Trigger」按鈕:

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

在下一頁中,選取預設的 Compute 服務帳戶,然後選取 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 bucket:
gcloud storage rm --recursive gs://screenshot-$PROJECT_ID
涵蓋內容
- 如何使用應用程式擷取網頁螢幕截圖。
- 如何建構應用程式的容器映像檔。
- 如何為應用程式建立 Cloud Run 工作。
- 如何以 Cloud Run 工作的形式執行應用程式。
- 如何更新職缺。
- 如何使用 Cloud Scheduler 安排工作排程。