1. 소개
개요
Cloud Run 서비스는 HTTP 요청을 무기한 리슨하는 컨테이너에 적합하지만, Cloud Run 작업은 완료될 때까지 (현재 최대 24시간) 실행되고 요청을 처리하지 않는 컨테이너에 더 적합합니다. 예를 들어 데이터베이스의 레코드 처리, Cloud Storage 버킷의 파일 목록 처리 또는 Pi 계산과 같은 장기 실행 작업이 Cloud Run 작업으로 구현된 경우 잘 작동합니다.
작업은 요청을 처리하거나 포트에서 리슨할 수 없습니다. 즉, Cloud Run 서비스와 달리 웹 서버를 번들로 제공해서는 안 됩니다. 대신 작업 컨테이너가 완료되면 작업을 종료해야 합니다.
Cloud Run 작업에서는 여러 태스크를 지정하여 컨테이너의 여러 복사본을 동시에 실행할 수 있습니다. 각 태스크는 실행 중인 컨테이너의 복사본 하나를 나타냅니다. 각 태스크가 데이터의 하위 집합을 독립적으로 처리할 수 있는 경우 여러 태스크를 사용하는 것이 유용합니다. 예를 들어 Cloud SQL에서 레코드 10,000개 또는 Cloud Storage에서 파일 10,000개를 레코드 또는 파일 1,000개를 각각 동시에 처리하는 태스크 10개를 통해 더 빠르게 처리할 수 있습니다.
Cloud Run 작업을 사용하는 과정은 두 단계로 이루어집니다.
- 작업 만들기: 컨테이너 이미지, 리전, 환경 변수 등 작업을 실행하는 데 필요한 모든 구성을 캡슐화합니다.
- 작업 실행: 작업의 새 실행을 만듭니다. 필요한 경우 Cloud Scheduler를 사용하여 일정에 따라 작업이 실행되도록 설정합니다.
이 Codelab에서는 먼저 Node.js 애플리케이션을 탐색하여 웹페이지의 스크린샷을 찍고 Cloud Storage에 저장합니다. 그런 다음 애플리케이션의 컨테이너 이미지를 빌드하고, Cloud Run 작업에서 실행하고, 더 많은 웹페이지를 처리하도록 작업을 업데이트하고, Cloud Scheduler를 사용하여 일정에 따라 작업을 실행합니다.
학습할 내용
- 앱을 사용하여 웹페이지의 스크린샷을 찍는 방법
- 애플리케이션의 컨테이너 이미지를 빌드하는 방법
- 애플리케이션의 Cloud Run 작업을 만드는 방법
- Cloud Run 작업으로 애플리케이션을 실행하는 방법
- 작업을 업데이트하는 방법
- Cloud Scheduler로 작업을 예약하는 방법
2. 설정 및 요구사항
자습형 환경 설정
- Google Cloud Console에 로그인하여 새 프로젝트를 만들거나 기존 프로젝트를 재사용합니다. 아직 Gmail이나 Google Workspace 계정이 없는 경우 계정을 만들어야 합니다.
- 프로젝트 이름은 이 프로젝트 참가자의 표시 이름입니다. 이는 Google API에서 사용하지 않는 문자열이며 언제든지 업데이트할 수 있습니다.
- 프로젝트 ID는 모든 Google Cloud 프로젝트에서 고유하며, 변경할 수 없습니다(설정된 후에는 변경할 수 없음). Cloud 콘솔은 고유한 문자열을 자동으로 생성합니다. 일반적으로는 신경 쓰지 않아도 됩니다. 대부분의 Codelab에서는 프로젝트 ID (일반적으로
PROJECT_ID
로 식별됨)를 참조해야 합니다. 생성된 ID가 마음에 들지 않으면 다른 임의 ID를 생성할 수 있습니다. 또는 직접 시도해 보고 사용 가능한지 확인할 수도 있습니다. 이 단계 이후에는 변경할 수 없으며 프로젝트 기간 동안 유지됩니다. - 참고로 세 번째 값은 일부 API에서 사용하는 프로젝트 번호입니다. 이 세 가지 값에 대한 자세한 내용은 문서를 참고하세요.
- 다음으로 Cloud 리소스/API를 사용하려면 Cloud 콘솔에서 결제를 사용 설정해야 합니다. 이 Codelab 실행에는 많은 비용이 들지 않습니다. 이 튜토리얼이 끝난 후에 요금이 청구되지 않도록 리소스를 종료하려면 만든 리소스 또는 프로젝트를 삭제하면 됩니다. Google Cloud 신규 사용자는 300달러(USD) 상당의 무료 체험판 프로그램에 참여할 수 있습니다.
Cloud Shell 시작
Google Cloud를 노트북에서 원격으로 실행할 수 있지만, 이 Codelab에서는 Cloud에서 실행되는 명령줄 환경인 Google Cloud Shell을 사용합니다.
Google Cloud Console의 오른쪽 상단 툴바에 있는 Cloud Shell 아이콘을 클릭합니다.
환경을 프로비저닝하고 연결하는 데 몇 분 정도 소요됩니다. 완료되면 다음과 같이 표시됩니다.
가상 머신에는 필요한 개발 도구가 모두 들어있습니다. 영구적인 5GB 홈 디렉터리를 제공하고 Google Cloud에서 실행되므로 네트워크 성능과 인증이 크게 개선됩니다. 이 Codelab의 모든 작업은 브라우저 내에서 수행할 수 있습니다. 아무것도 설치할 필요가 없습니다.
gcloud 설정
Cloud Shell에서 Cloud Run 작업을 배포할 프로젝트 ID와 리전을 설정합니다. 이러한 변수를 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');
Puppeteer를 초기화하는 initBrowser
함수와 지정된 URL의 스크린샷을 찍는 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
메서드에 관한 다음 사항에 유의하세요.
- URL이 인수로 전달됩니다.
- 버킷 이름이 사용자 정의
BUCKET_NAME
환경 변수로 전달됩니다. 버킷 이름은 모든 Google Cloud에서 전역적으로 고유해야 합니다. CLOUD_RUN_TASK_INDEX
환경 변수는 Cloud Run 작업에서 전달합니다. Cloud Run 작업은 애플리케이션의 여러 복사본을 고유한 태스크로 실행할 수 있습니다.CLOUD_RUN_TASK_INDEX
는 실행 중인 태스크의 색인을 나타냅니다. 코드가 Cloud Run 작업 외부에서 실행되는 경우 기본값은 0입니다. 애플리케이션이 여러 태스크로 실행되는 경우 각 태스크/컨테이너는 담당하는 URL을 선택하여 스크린샷을 찍고 이미지를 버킷에 저장합니다.
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
를 확인하세요. 이렇게 하면 처리하려는 URL 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 Console의 Cloud Run 작업 페이지에서 상태를 확인할 수도 있습니다.
Cloud Storage 버킷을 확인하면 생성된 스크린샷 파일 2개가 표시됩니다.
실행이 완료되기 전에 중지해야 하는 경우도 있습니다. 다른 매개변수를 사용하여 작업을 실행해야 하거나 코드에 오류가 있고 불필요한 컴퓨팅 시간을 사용하는 것을 원하지 않기 때문일 수 있습니다.
작업 실행을 중지하려면 실행을 삭제해야 합니다.
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시에 실행할 스케줄러 작업을 만들고 Continue
를 선택합니다.
다음 페이지에서 기본 컴퓨팅 서비스 계정을 선택하고 Create
을 선택합니다.
이제 새 Cloud Scheduler 트리거가 생성된 것을 볼 수 있습니다.
View Details
를 클릭하여 Cloud Scheduler 페이지로 이동합니다.
오전 9시까지 스케줄러가 활성화될 때까지 기다리거나 Force Run
를 선택하여 Cloud Scheduler를 수동으로 트리거할 수 있습니다.
몇 초 후에 Cloud Scheduler 작업이 성공적으로 실행된 것을 확인할 수 있습니다.
또한 Cloud Scheduler 호출로 인해 스크린샷이 3개 더 표시됩니다.
9. 축하합니다
축하합니다. Codelab을 완료했습니다.
삭제 (선택사항)
요금이 청구되지 않도록 하려면 리소스를 정리하는 것이 좋습니다.
프로젝트가 필요하지 않으면 프로젝트를 삭제하면 됩니다.
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로 작업을 예약하는 방법