1. 소개
개요
Cloud Run 서비스는 HTTP 요청을 무기한으로 리슨하는 컨테이너에 적합하지만 Cloud Run 작업은 완료될 때까지 실행되고 요청을 처리하지 않는 컨테이너에 더 적합할 수 있습니다. 예를 들어 데이터베이스의 레코드 처리, Cloud Storage 버킷의 파일 목록 처리 또는 Pi 계산과 같은 장기 실행 작업이 Cloud Run 작업으로 구현된 경우 잘 작동합니다.
작업은 요청을 처리하거나 포트에서 리슨할 수 없습니다. 즉, Cloud Run 서비스와 달리 웹 서버를 번들로 제공해서는 안 됩니다. 대신 작업 컨테이너가 완료되면 작업을 종료해야 합니다.
Cloud Run 작업에서는 여러 태스크를 지정하여 컨테이너의 여러 복사본을 동시에 실행할 수 있습니다. 각 태스크는 실행 중인 컨테이너의 복사본 하나를 나타냅니다. 각 태스크가 데이터의 하위 집합을 독립적으로 처리할 수 있는 경우 여러 태스크를 사용하는 것이 유용합니다. 예를 들어 Cloud SQL에서 레코드 10,000개 또는 Cloud Storage에서 파일 10,000개를 레코드 또는 파일 1,000개를 각각 동시에 처리하는 태스크 10개를 통해 더 빠르게 처리할 수 있습니다.
작업 워크플로
다음 두 단계만 거치면 Cloud Run 작업을 간편하게 사용할 수 있습니다.
- 작업을 만듭니다. 여기에는 컨테이너 이미지, 리전, 환경 변수 등 작업을 실행하는 데 필요한 모든 구성이 캡슐화됩니다.
- 작업을 실행합니다. 그러면 작업이 새로 실행됩니다. 필요한 경우 Cloud Scheduler를 사용하여 일정에 따라 작업이 실행되도록 설정합니다.
미리보기 제한사항
미리보기 중에는 Cloud Run 작업에 다음과 같은 제약 조건이 있습니다.
- 리전별로 프로젝트당 최대 동시 실행 수는 50회(동일하거나 다른 작업)입니다.
- Cloud Console의 Cloud Run 작업 페이지에서 기존 작업을 확인하고, 실행을 시작하고, 실행 상태를 모니터링할 수 있습니다. Cloud Console에서는 현재 새 작업 만들기가 지원되지 않으므로
gcloud
를 사용하여 새 작업을 만듭니다. - 프로덕션 워크로드에 Cloud Run 작업을 사용하지 마세요. 안정성이나 성능이 보장되지 않습니다. Cloud Run 작업은 GA 이전 알림 없이 이전 버전과 호환되지 않는 방식으로 변경될 수 있습니다.
이 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 Console은 고유한 문자열을 자동으로 생성합니다. 일반적으로 신경 쓰지 않아도 됩니다. 대부분의 Codelab에서는 프로젝트 ID를 참조해야 하며(일반적으로
PROJECT_ID
로 식별됨), 마음에 들지 않는 경우 임의로 다시 생성하거나 직접 지정해서 사용할 수 있는지 확인하세요. 프로젝트가 생성되면 프로젝트 ID가 '고정'됩니다. - 세 번째 값은 일부 API에서 사용하는 프로젝트 번호입니다. 이 세 가지 값에 대한 자세한 내용은 문서를 참조하세요.
- 다음으로 Cloud 리소스/API를 사용하려면 Cloud Console에서 결제를 사용 설정해야 합니다. 이 Codelab 실행에는 많은 비용이 들지 않습니다. 이 튜토리얼을 마친 후 비용이 결제되지 않도록 리소스를 종료하려면 Codelab의 끝에 있는 '삭제' 안내를 따르세요. Google Cloud 새 사용자에게는 미화 $300 상당의 무료 체험판 프로그램에 참여할 수 있는 자격이 부여됩니다.
Cloud Shell 시작
Google Cloud를 노트북에서 원격으로 실행할 수 있지만, 이 Codelab에서는 Cloud에서 실행되는 명령줄 환경인 Google Cloud Shell을 사용합니다.
Google Cloud Console의 오른쪽 상단 툴바에 있는 Cloud Shell 아이콘을 클릭합니다.
환경을 프로비저닝하고 연결하는 데 몇 분 정도 소요됩니다. 완료되면 다음과 같이 표시됩니다.
가상 머신에는 필요한 개발 도구가 모두 들어있습니다. 영구적인 5GB 홈 디렉토리를 제공하고 Google Cloud에서 실행되므로 네트워크 성능과 인증이 크게 개선됩니다. 이 실습의 모든 작업은 브라우저만으로 수행할 수 있습니다.
gcloud 설정
Cloud Shell에서 Cloud Run 작업을 배포할 프로젝트 ID와 리전을 설정합니다. 이러한 변수를 PROJECT_ID
및 REGION
변수로 저장합니다. Cloud Run 위치 중 하나에서 리전을 선택할 수 있습니다.
PROJECT_ID=[YOUR-PROJECT-ID] REGION=[YOUR-REGION] gcloud config set core/project $PROJECT_ID gcloud config set run/region $REGION
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 node:17-alpine # Installs latest Chromium (92) package. RUN apk add --no-cache \ chromium \ nss \ freetype \ harfbuzz \ ca-certificates \ ttf-freefont \ nodejs \ npm # Tell Puppeteer to skip installing Chrome. We'll be using the installed package. ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true \ PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium-browser # Add user so we don't need --no-sandbox. RUN addgroup -S pptruser && adduser -S -g pptruser pptruser \ && mkdir -p /home/pptruser/Downloads /app \ && chown -R pptruser:pptruser /home/pptruser \ && chown -R pptruser:pptruser /app # Install dependencies COPY package*.json ./ RUN npm install # Copy all files COPY . . # Run everything after as a non-privileged user. USER pptruser ENTRYPOINT ["node", "screenshot.js"]
5. 컨테이너 이미지 빌드 및 게시
Artifact Registry는 Google Cloud의 컨테이너 이미지 스토리지 및 관리 서비스입니다. 자세한 내용은 컨테이너 이미지 작업을 참조하세요. Artifact Registry는 Docker 저장소에 Docker 및 OCI 컨테이너 이미지를 저장할 수 있습니다.
containers
라는 새 Artifact Registry 저장소를 만듭니다.
gcloud artifacts repositories create containers --repository-format=docker --location=$REGION
컨테이너 이미지를 빌드하고 게시합니다.
gcloud builds submit -t $REGION-docker.pkg.dev/$PROJECT_ID/containers/screenshot:v1
몇 분 후에 Artifact Registry에 빌드되고 호스팅되는 컨테이너 이미지가 표시됩니다.
6. 작업 만들기
작업을 만들기 전에 작업을 실행하는 데 사용할 서비스 계정을 만들어야 합니다.
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 create screenshot \ --image=$REGION-docker.pkg.dev/$PROJECT_ID/containers/screenshot:v1 \ --args="https://example.com" \ --args="https://cloud.google.com" \ --tasks=2 \ --task-timeout=5m \ --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개가 실행됩니다.
각 태스크는 최대 1시간 동안 실행될 수 있습니다. 이 예시와 같이 --task-timeout
플래그를 사용하여 이 제한 시간을 줄일 수 있습니다. 작업이 성공적으로 완료되려면 모든 태스크가 성공해야 합니다. 실패한 태스크는 기본적으로 다시 시도되지 않습니다. 태스크가 실패할 경우 다시 시도되도록 구성할 수 있습니다. 태스크가 재시도 횟수를 초과하면 전체 작업이 실패합니다.
기본적으로 작업은 가능한 한 많은 태스크를 동시에 실행합니다. 작업의 태스크 수와 동일하며 최대 100개입니다. 확장성이 제한된 백엔드에 액세스하는 작업의 경우 동시 로드를 더 낮게 설정하는 것이 좋습니다. 제한된 수의 활성 연결을 지원하는 데이터베이스를 예시로 들 수 있습니다. --parallelism
플래그를 사용하여 동시 로드를 줄일 수 있습니다.
7 축하합니다
축하합니다. Codelab을 완료했습니다.
학습한 내용
- 앱을 사용하여 웹페이지의 스크린샷을 찍는 방법
- 애플리케이션의 컨테이너 이미지를 빌드하는 방법
- 애플리케이션의 Cloud Run 작업을 만드는 방법