Cloud Tasks로 큐에 함수 추가

태스크 큐 함수는 Google Cloud Tasks를 활용하여 앱이 기본 애플리케이션 흐름 외부에서 시간이 많이 걸리거나, 리소스를 많이 소모하거나, 대역폭이 제한적인 태스크를 비동기식으로 실행할 수 있게 해 줍니다.

예를 들어 현재 비율 제한이 있는 API에서 호스팅되는 대규모 이미지 파일 세트의 백업을 만든다고 가정해 보겠습니다. 책임감 있는 API 소비자가 되려면 비율 제한을 준수해야 합니다. 또한 이러한 종류의 장기 실행 작업은 시간 제한 및 메모리 한도로 인해 실패에 취약할 수 있습니다.

이러한 복잡성을 완화하려면 scheduleTimedispatchDeadline과 같은 기본 태스크 옵션을 설정하는 태스크 큐 함수를 작성하면 됩니다. 그러면 함수가 Cloud Tasks의 큐로 전달됩니다. Cloud Tasks 환경은 특히 이러한 종류의 작업에 효과적인 정체 제어와 재시도 정책을 제공하도록 설계되었습니다.

Firebase용 Cloud Functions의 Firebase SDK v3.20.1 이상은 Firebase Admin SDK v10.2.0 이상과 연동되어 태스크 큐 함수를 지원합니다.

Firebase에서 태스크 큐 함수를 사용하면 Cloud Tasks 처리 요금이 청구될 수 있습니다. 자세한 내용은 Cloud Tasks 가격 책정을 참조하세요.

태스크 큐 함수 만들기

태스크 큐 함수를 사용하려면 다음 워크플로를 따르세요.

  1. Cloud Functions용 Firebase SDK를 사용하여 태스크 큐 함수를 작성합니다.
  2. Firebase 로컬 에뮬레이터 도구 모음을 사용하여 함수를 테스트합니다.
  3. Firebase CLI로 함수를 배포합니다. 태스크 큐 함수를 처음으로 배포할 때 CLI가 소스 코드에서 지정된 옵션(비율 제한 및 재시도)으로 Cloud Tasks에서 태스크 큐를 만듭니다.
  4. 새로 생성된 태스크 큐에 태스크를 추가하고 필요한 경우 매개변수를 전달하여 실행 일정을 설정합니다. Admin SDK를 사용하여 코드를 작성하고 Firebase용 Cloud Functions에 배포하면 됩니다.

태스크 큐 함수 작성

onTaskDispatched를 사용하여 태스크 큐 함수 작성을 시작하세요. 태스크 큐 함수를 작성할 때 중요한 부분은 큐별 재시도와 비율 제한 구성을 설정하는 것입니다. 이 페이지에 나와 있는 코드 샘플은 NASA Astronomy Picture of the Day 사이트의 모든 이미지를 백업하는 서비스를 설정하는 앱을 기반으로 합니다.

태스크 큐 구성

태스크 큐 함수에는 태스크 큐의 비율 제한과 재시도 동작을 정밀하게 제어하는 강력한 구성 설정 모음이 함께 제공됩니다.

exports.backupapod = onTaskDispatched(
    {
      retryConfig: {
        maxAttempts: 5,
        minBackoffSeconds: 60,
      },
      rateLimits: {
        maxConcurrentDispatches: 6,
      },
    }, async (req) => {
  • retryConfig.maxAttempts=5: 태스크 큐의 각 태스크가 최대 5회까지 자동으로 재시도됩니다. 이렇게 하면 네트워크 오류 또는 종속된 외부 서비스의 일시적인 중단과 같은 일시적인 오류를 완화할 수 있습니다.
  • retryConfig.minBackoffSeconds=60: 각 시도마다 최소 60초 간격으로 태스크가 재시도됩니다. 이렇게 하면 각 시도 간에 긴 버퍼가 제공되므로 재시도 횟수 5회가 그다지 빨리 소진되지 않습니다.
  • rateLimits.maxConcurrentDispatch=6: 지정된 시간에 최대 6개의 태스크가 디스패치됩니다. 이렇게 하면 기본 함수에 대한 요청이 꾸준히 이루어지고 활성 인스턴스 및 콜드 스타트 횟수를 줄일 수 있습니다.

Firebase 로컬 에뮬레이터 도구 모음을 사용하여 태스크 큐 함수 테스트

Firebase 로컬 에뮬레이터 도구 모음의 태스크 큐 함수는 간단한 HTTP 함수로 노출됩니다. JSON 데이터 페이로드와 함께 HTTP POST 요청을 전송하여 에뮬레이션된 태스크 함수를 테스트할 수 있습니다.

 # start the Local Emulator Suite
 firebase emulators:start

 # trigger the emulated task queue function
 curl \
  -X POST                                            # An HTTP POST request...
  -H "content-type: application/json" \              # ... with a JSON body
  http://localhost:$PORT/$PROJECT_ID/$REGION/$NAME \ # ... to function url
  -d '{"data": { ... some data .... }}'              # ... with JSON encoded data

태스크 큐 함수 배포

Firebase CLI를 사용하여 태스크 큐 함수를 배포하세요.

$ firebase deploy --only functions:backupapod

태스크 큐 함수를 처음으로 배포할 때 CLI가 소스 코드에 지정된 옵션(비율 제한 및 재시도)으로 Cloud Tasks에서 태스크 큐를 만듭니다.

함수를 배포할 때 권한 오류가 발생하면 배포 명령어를 실행하는 사용자에게 적절한 IAM 역할이 할당되었는지 확인합니다.

큐에 함수 추가

태스크 큐 함수는 Node.js용 Firebase Admin SDK를 사용하여 Firebase용 Cloud Functions와 같은 신뢰할 수 있는 서버 환경에서 Cloud Tasks의 큐에 추가할 수 있습니다. Admin SDK를 처음 사용하는 경우 서버에 Firebase 추가를 참조하여 시작하세요.

일반적인 흐름에서 Admin SDK는 새 태스크를 만들어 Cloud Tasks의 큐에 추가하고 태스크용 구성을 설정합니다.

exports.enqueuebackuptasks = onRequest(
    async (_request, response) => {
      const queue = getFunctions().taskQueue("backupapod");
      const targetUri = await getFunctionUrl("backupapod");

      const enqueues = [];
      for (let i = 0; i <= BACKUP_COUNT; i += 1) {
        const iteration = Math.floor(i / HOURLY_BATCH_SIZE);
        // Delay each batch by N * hour
        const scheduleDelaySeconds = iteration * (60 * 60);

        const backupDate = new Date(BACKUP_START_DATE);
        backupDate.setDate(BACKUP_START_DATE.getDate() + i);
        // Extract just the date portion (YYYY-MM-DD) as string.
        const date = backupDate.toISOString().substring(0, 10);
        enqueues.push(
            queue.enqueue({date}, {
              scheduleDelaySeconds,
              dispatchDeadlineSeconds: 60 * 5, // 5 minutes
              uri: targetUri,
            }),
        );
      }
      await Promise.all(enqueues);
      response.sendStatus(200);
    });
  • scheduleDelaySeconds: 샘플 코드는 N번째 태스크의 N분 지연을 연결하여 태스크 실행을 분산하려고 시도합니다. 즉, 분당 약 1개의 태스크가 트리거됩니다. Cloud Tasks가 특정 시간에 태스크를 트리거하게 하려면 scheduleTime을 사용할 수도 있습니다.
  • dispatchDeadlineSeconds: Cloud Tasks에서 태스크가 완료될 때까지 기다리는 최대 시간입니다. Cloud Tasks는 큐의 재시도 구성에 따라 또는 이 기한에 도달할 때까지 태스크를 재시도합니다. 샘플에는 태스크를 최대 5회까지 재시도하도록 큐가 구성되어 있지만, 전체 프로세스(재시도 포함)가 5분 넘게 걸리는 경우에는 태스크가 자동으로 취소됩니다.

대상 URI 검색 및 포함

Cloud Tasks가 기본 태스크 큐 함수에 대한 요청을 인증하기 위한 인증 토큰을 만드는 방식으로 인해 태스크를 큐에 추가할 때 함수의 Cloud Run URL을 지정해야 합니다. 아래에 나온 것처럼 함수의 URL을 프로그래매틱 방식으로 검색하는 것이 좋습니다.

/**
 * Get the URL of a given v2 cloud function.
 *
 * @param {string} name the function's name
 * @param {string} location the function's location
 * @return {Promise<string>} The URL of the function
 */
async function getFunctionUrl(name, location="us-central1") {
  if (!auth) {
    auth = new GoogleAuth({
      scopes: "https://www.googleapis.com/auth/cloud-platform",
    });
  }
  const projectId = await auth.getProjectId();
  const url = "https://cloudfunctions.googleapis.com/v2beta/" +
    `projects/${projectId}/locations/${location}/functions/${name}`;

  const client = await auth.getClient();
  const res = await client.request({url});
  const uri = res.data?.serviceConfig?.uri;
  if (!uri) {
    throw new Error(`Unable to retreive uri for function at ${url}`);
  }
  return uri;
}

문제 해결

Cloud Tasks 로깅 사용 설정

Cloud Tasks의 로그에는 태스크와 연결된 요청의 상태와 같은 유용한 진단 정보가 포함됩니다. 프로젝트에서 대량의 로그가 생성될 수도 있으므로 기본적으로 Cloud Tasks의 로그는 사용 중지되어 있습니다. 태스크 큐 함수를 적극적으로 개발하고 디버깅하는 동안에는 디버그 로그를 사용 설정하는 것이 좋습니다. 로깅 사용 설정을 참조하세요.

IAM 권한

태스크를 큐에 추가할 때 또는 Cloud Tasks가 태스크 큐 함수를 호출하려고 시도할 때 PERMISSION DENIED 오류가 표시될 수 있습니다. 프로젝트에 다음 IAM 바인딩이 있는지 확인하세요.

  • 태스크를 Cloud Tasks의 큐에 추가하는 데 사용되는 ID에는 cloudtasks.tasks.create IAM 권한이 필요합니다.

    샘플에서는 App Engine 기본 서비스 계정입니다.

gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member=serviceAccount:${PROJECT_ID}@appspot.gserviceaccount.com \
  --role=roles/cloudtasks.enqueuer
  • 태스크를 Cloud Tasks의 큐에 추가하는 데 사용되는 ID에는 Cloud Tasks의 태스크와 연결된 서비스 계정을 사용할 권한이 필요합니다.

    샘플에서는 App Engine 기본 서비스 계정입니다.

App Engine 기본 서비스 계정을 App Engine 기본 서비스 계정의 사용자로 추가하는 방법에 대한 안내는 Google Cloud IAM 문서를 참조하세요.

  • 태스크 큐 함수를 트리거하는 데 사용되는 ID에는 cloudfunctions.functions.invoke 권한이 필요합니다.

    샘플에서는 App Engine 기본 서비스 계정입니다.

gcloud functions add-iam-policy-binding $FUNCTION_NAME \
  --region=us-central1 \
  --member=serviceAccount:${PROJECT_ID}@appspot.gserviceaccount.com \
  --role=roles/cloudfunctions.invoker