Cloud Tasks를 사용하면 트래픽이 큐 및 다운스트림 서비스에 도달하는 속도를 제어하면서 대량의 비동기 태스크를 디스패치할 수 있습니다. 점진적 램프업 패턴과 비율 제어를 적용하면 인프라가 적응할 수 있는 것보다 트래픽이 더 빠르게 증가할 때 큐 지연 시간 급증이나 대상 과부하를 유발하지 않고 워크로드를 원활하게 확장할 수 있습니다.
개요
이 문서에서는 트래픽이 많은 큐에서 우수한 Cloud Tasks 성능을 유지하기 위한 권장사항 가이드라인을 제공합니다. TPS가 많은 큐는 초당 생성되거나 전달되는 태스크(TPS)가 500개 이상인 큐입니다. 높은 TPS 큐 그룹은 초당 총 2,000개 이상의 태스크가 생성되거나 디스패치되는 연속적인 큐 집합입니다(예: [queue0001, queue0002, …, queue0099]). Cloud Monitoring 측정항목, CreateTask 작업의 경우 api/request_count, 태스크 시도의 경우 queue/task_attempt_count를 사용하여 큐 또는 큐 그룹의 이전 TPS를 볼 수 있습니다. 트래픽이 많은 큐 및 큐 그룹은 다음 두 가지 광범위한 범주의 오류가 발생하기 쉽습니다.
큐 과부하는 개별 큐 또는 큐 그룹에 대한 태스크 생성 및 디스패치가 큐 인프라가 적응할 수 있는 것보다 빠르게 증가할 때 발생합니다. Google 인프라는 트래픽 패턴에 따라 부하를 동적으로 재할당하는 적응형 레이어를 사용하며, 이 적응에는 시간이 걸립니다. 마찬가지로 대상 과부하는 태스크 디스패치 속도로 인해 다운스트림 대상 인프라에서 트래픽 급증이 발생할 때 발생합니다. 두 경우 모두 500/50/5 패턴을 따릅니다. TPS 500개를 초과하여 확장하는 경우 5분마다 트래픽을 50% 이하로 늘립니다.
TPS가 높은 워크로드의 500/50/5 패턴과 별개로 초기 큐 램프업은 콜드 또는 유휴 상태에서 시작할 때 모든 큐에 영향을 미칩니다. 여기에는 적당한 디스패치 속도로 구성된 큐도 포함됩니다. 이 문서에서는 확장 위험을 초래할 수 있는 다양한 시나리오를 검토하고 큐 램프업을 관리하고 500/50/5 패턴을 적용하는 방법의 예시를 제공합니다.
초기 대기열 램프업
500/50/5 패턴은 이미 안정적인 상태로 작동하는 TPS가 높은 큐에 적용됩니다. 초기 큐 램프업은 20~100TPS와 같은 적당한 디스패치 속도로 구성된 큐를 포함한 모든 큐가 콜드 또는 유휴 상태에서 안정적인 상태 작업으로 전환될 때 적용되는 별도의 시스템 메커니즘입니다.
큐는 다음 상황 중 하나에서 콜드 또는 유휴 상태로 전환됩니다.
큐가 새로 생성되거나 일시중지 후 재개됩니다.
활성 디스패치가 없는 상태로 큐가 몇 분 동안 비활성 상태였습니다.
다운스트림 HTTP
429또는5xx오류 응답에서 백오프한 후 큐가 복구 중입니다. 오류 백오프가 디스패치 속도에 미치는 영향에 대한 자세한 내용은 백오프 오류 및 시행 속도를 참조하세요.
태스크가 콜드 또는 유휴 큐에 도착하면 Cloud Tasks는 구성된 max_dispatches_per_second 비율로 태스크를 즉시 디스패치하지 않습니다. 다운스트림 타겟을 갑작스러운 트래픽 급증으로부터 보호하기 위해 Cloud Tasks는 약 1TPS와 같은 낮은 기준 속도로 디스패치를 시작합니다. 그런 다음 주기적인 평가 간격에 따라 시행되는 디스패치 속도를 점진적으로 높입니다. 큐 수요가 포화 상태로 유지되고 대상 핸들러가 HTTP 2xx 응답을 반환하는 한 시행되는 비율은 계속 증가합니다. 목표 비율에 따라 높은 max_dispatches_per_second 값에 도달하는 데 몇 분 정도 걸릴 수 있습니다.
워크로드가 몇 분 이상의 짧은 유휴 기간으로 구분된 급증 트래픽을 전송하는 경우 큐는 버스트 사이에 콜드 상태로 재설정되고 각 버스트에서 이 램프업 프로세스를 반복합니다. 버스트에 민감한 워크로드의 램프업 지연을 최소화하려면 다음 권장사항을 따르세요.
기준 트래픽을 꾸준히 유지하여 버스트 사이에 지연 시간에 민감한 큐를 준비 상태로 유지합니다.
큐를 자주 삭제하고 다시 만드는 대신 기존 큐를 재사용합니다.
핸들러가 HTTP
2xx응답을 반환하고 시행된 디스패치 속도를 재설정하는 HTTP429또는5xx오류 응답을 반환하지 않도록 충분한 다운스트림 대상 용량을 프로비저닝합니다.
큐 제한 진단
큐가 구성된 max_dispatches_per_second 비율보다 느리게 작업을 전달하는 경우 콜드 또는 유휴 큐 램프업, 대상 혼잡 백오프 또는 동시 실행 한도로 인해 병목 현상이 발생하는지 확인합니다.
콜드 또는 유휴 큐 램프업
다음 조건이 모두 적용되는 경우 초기 큐 램프업이 원인일 수 있습니다.
- 큐가 최근에 생성되었거나, 일시중지 후 재개되었거나, 트래픽이 도착하기 전에 몇 분 동안 유휴 상태였습니다.
- 태스크 시도가 대상 오류 없이 성공하여 Monitoring 측정항목
cloudtasks.googleapis.com/queue/task_attempt_count에response_code="OK"이 표시됩니다. - 진행 중인 디스패치는
max_concurrent_dispatches미만으로 유지되지만cloudtasks.googleapis.com/queue/depth또는cloudtasks.googleapis.com/queue/task_attempt_delays는 증가합니다. 동시에cloudtasks.googleapis.com/queue/task_attempt_count의 디스패치 속도는 주기적인 평가 간격에 따라 점진적으로 증가합니다.
Monitoring 측정항목에 대한 자세한 내용은 Monitoring에 측정항목 전송을 참조하세요.
대상 과부하 및 혼잡 백오프
다운스트림 대상이 HTTP 503 Service Unavailable와 같은 HTTP 429 Too Many Requests 또는 HTTP 5xx 오류 응답을 반환하면 Cloud Tasks는 디스패치 속도를 동적으로 줄입니다. 이 혼잡 백오프는 대상 오버로드를 방지합니다.
대상 오류로 인해 Cloud Tasks가 디스패치를 제한하는지 확인하려면 response_code 라벨로 그룹화된 Monitoring 측정항목 cloudtasks.googleapis.com/queue/task_attempt_count을 확인합니다. 큐 로깅이 사용 설정된 경우 로그 탐색기에서 시도 응답 로그를 검사할 수도 있습니다.
resource.type="cloud_tasks_queue"
jsonPayload.attemptResponseLog.status!="OK"
response_code 및 jsonPayload.attemptResponseLog.status 모두에서 Cloud Tasks는 숫자 HTTP 상태 코드 대신 표준 gRPC 상태 코드를 기록합니다. 예를 들어 HTTP 429는 RESOURCE_EXHAUSTED에 매핑되고, HTTP 503는 UNAVAILABLE에 매핑되고, HTTP 500는 INTERNAL에 매핑되고, HTTP 504 또는 제한 시간은 DEADLINE_EXCEEDED에 매핑됩니다.
대상에서 RESOURCE_EXHAUSTED, UNAVAILABLE 또는 기타 서버 오류 상태가 관찰되면 큐 디스패치 한도를 늘리기 전에 대상 서비스 인스턴스를 수직 확장하거나 대상 동시 실행을 늘립니다.
큐 로그 사용 설정에 대한 자세한 내용은 큐와 함께 Cloud Logging 사용을 참조하세요.
동시 실행 병목 현상
리틀의 법칙에 따르면 다운스트림 목표 지연 시간은 최대 디스패치 처리량을 제한합니다.
Max Throughput (tasks/sec) = min(
max_dispatches_per_second,
max_concurrent_dispatches / Downstream Latency in seconds
)
예를 들어 다운스트림 실행 지연 시간이 2초이고 max_concurrent_dispatches가 100로 설정된 경우 max_dispatches_per_second가 500로 구성되어 있더라도 처리량은 초당 50개 태스크로 제한됩니다.
동시 실행이 병목 현상인지 진단하려면 max_concurrent_dispatches를 Logging의 jsonPayload.attemptResponseLog.attemptDuration 필드에 기록된 대상 응답 지연 시간으로 나눕니다. 이 계산된 기준점을 Monitoring 측정항목 cloudtasks.googleapis.com/queue/task_attempt_count의 실제 디스패치 비율과 비교합니다. cloudtasks.googleapis.com/queue/depth 또는 cloudtasks.googleapis.com/queue/task_attempt_delays가 증가하는 동안 실제 디스패치가 이 기준점 근처에서 정체되면 다음 작업 중 하나를 수행합니다.
- 다운스트림 대상에 여유 용량이 있는 경우(예: 태스크가 대부분의 시간을 외부 I/O를 기다리는 데 소비하는 경우)
max_concurrent_dispatches를 늘립니다. - 다운스트림 대상이 이미 높은 리소스 사용률을 보이고 있는 경우
max_concurrent_dispatches를 늘리면 대상 과부하가 악화될 수 있습니다. 대신 대상 인스턴스를 수평 확장하거나 핸들러 실행 지연 시간을 줄이세요.
큐 과부하
트래픽이 갑자기 증가할 때마다 큐 또는 큐 그룹이 과부하 상태가 될 수 있습니다. 따라서 이러한 큐에 다음 상황이 발생할 수 있습니다.
- 작업 생성 지연 시간 증가
- 작업 생성 오류율 증가
- 전달 비율 감소
이를 방지하기 위해 큐 또는 큐 그룹의 생성 및 전달 비율이 갑자기 급증할 수 있는 모든 상황에 대해 컨트롤을 설정합니다. 콜드 큐 또는 큐 그룹에 대해 초당 최대 500개의 작업으로 시작한 다음 5분마다 트래픽을 50% 씩 늘립니다. 이론적으로 이 램프업 일정을 사용하면 90분 후에 초당 740, 000회 작업으로 늘릴 수 있습니다. 이러한 상황은 여러 가지 경우에 발생할 수 있습니다.
예를 들면 다음과 같습니다.
- Cloud Tasks 사용량이 많은 새로운 기능을 실행하는 경우
- 큐 간에 트래픽을 이동하는 경우
- 큐 전반에서 트래픽을 재분산하는 경우
- 많은 수의 태스크를 삽입하는 일괄 작업 실행
이러한 경우 500/50/5 패턴을 따르세요.
App Engine 트래픽 분할 사용
App Engine 앱이 태스크를 만드는 경우 App Engine 트래픽 분할(App Engine 표준 환경 또는 App Engine 가변형 환경)을 활용하여 트래픽 증가를 원활하게 처리할 수 있습니다. 버전 간에 트래픽을 분할하면(표준 환경 또는 가변형 환경) 시간이 지남에 따라 비율 관리 요청을 늘려 큐 상태를 보호할 수 있습니다. 예를 들어 새로 확장된 큐 그룹으로 트래픽을 늘리는 경우를 생각해 보겠습니다. [queue0000, queue0199] 를 피크 시 총 100,000개의 TPS 생성을 수신하는 TPS가 많은 큐 시퀀스라고 가정해 보겠습니다.
[queue0200, queue0399]를 새 큐 시퀀스로 가정합니다. 모든 트래픽을 이동하면 시퀀스의 큐 수가 두 배가 되고 새 큐 범위가 시퀀스의 총 트래픽의 50% 를 수신합니다.
큐 수를 늘리는 버전을 배포할 때 트래픽 분할을 사용하여 트래픽을 먼저 새 버전으로 점진적으로 증가시켜 결과적으로 새 큐로 증가시키세요.
- 트래픽의 1%를 새 버전으로 이동하기 시작합니다. 예를 들어 100,000개 TPS의 1% 중 50% 는 새 큐 세트에 500개의 TPS를 이동시킵니다.
- 다음 표에 설명된 대로 5분마다 새 출시 버전으로 전송되는 트래픽을 50%씩 늘립니다.
| 배포 시작 후 경과 시간(분) | 새 버전으로 이동된 총 트래픽 비율(%) | 새 큐에 대한 총 트래픽 비율(%) | 이전 큐에 대한 총 트래픽 비율(%) |
|---|---|---|---|
| 0 | 1.0 | 0.5 | 99.5 |
| 5 | 1.5 | 0.75 | 99.25 |
| 10 | 2.3 | 1.15 | 98.85 |
| 15 | 3.4 | 1.7 | 98.3 |
| 20 | 5.1 | 2.55 | 97.45 |
| 25 | 7.6 | 3.8 | 96.2 |
| 30 | 11.4 | 5.7 | 94.3 |
| 35 | 17.1 | 8.55 | 91.45 |
| 40 | 25.6 | 12.8 | 87.2 |
| 45 | 38.4 | 19.2 | 80.8 |
| 50 | 57.7 | 28.85 | 71.15 |
| 55 | 86.5 | 43.25 | 56.75 |
| 60 | 100 | 50 | 50 |
출시로 인한 트래픽 급증
큐 또는 큐 그룹에 대한 트래픽을 크게 증가시키는 버전을 출시할 때 점진적 출시가 이러한 증가를 원활하게 처리하는 중요한 메커니즘입니다. 초기 실행 시 새 큐에 대한 총 작업이 초당 500개를 초과하지 않도록 인스턴스를 점진적으로 출시하여 트래픽을 5분마다 50% 이하로 늘리세요.
TPS가 많은 새 큐 또는 큐 그룹
새로 만든 큐는 특히 취약합니다. 큐 그룹(예: [queue0000, queue0001, …, queue0199])은 초기 출시 단계 동안 단일 큐만큼 중요합니다. 이러한 큐에는 점진적 출시가 중요한 전략입니다. TPS가 많은 큐 또는 큐 그룹을 만드는 새로운 서비스 또는 업데이트된 서비스를 단계적으로 실행하세요. 초기 부하를 500TPS 미만으로 유지하고 5분 이상 간격을 두고 50% 이하로 증가하도록 단계적으로 실행하세요.
새로 확장된 큐 그룹
큐 그룹의 총 용량을 늘릴 때(예: [queue0000-queue0199에서 queue0000-queue0399]로 확장) 500/50/5 패턴을 따르세요. 출시 절차의 경우 새 큐 그룹이 개별 큐과 다르지 않게 동작합니다. 그룹 내 개별 큐가 아니라 새 큐 그룹 전체에 500/50/5 패턴을 적용하세요.
이러한 큐 그룹 확장의 경우 점진적 출시가 중요한 전략입니다. 트래픽 소스가 App Engine인 경우 트래픽 분할을 사용할 수 있습니다. 증가된 큐 수에 태스크를 추가하기 위해 서비스를 마이그레이션할 때 초기 실행 시 새 큐에 대한 총 작업이 500개를 초과하지 않도록 인스턴스를 점진적으로 출시하고 트래픽을 5분마다 50% 이하로 늘리세요.
긴급 큐 그룹 확장
경우에 따라 큐 그룹이 작업을 전달하는 것보다 더 빠른 속도로 작업이 큐 그룹에 추가될 것으로 예상되므로 기존 큐 그룹을 확장할 수 있습니다. 새 큐의 이름을 사전순으로 기존 큐 이름 사이에 고르게 분산하면 해당 큐로 트래픽을 즉시 전송할 수 있습니다. 새 인터리브 처리된 큐가 50% 를 넘지 않고 각 큐에 대한 트래픽이 500TPS 미만인지 확인합니다. 이 방법은 트래픽 분할 및 점진적 출시를 사용하는 대신 사용할 수 있습니다.
이러한 유형의 인터리브 처리된 큐의 이름은 짝수로 끝나는 큐에 서픽스를 추가하는 방식으로 지정할 수 있습니다. 예를 들어 기존 큐가 [queue0000-queue0199] 으로 200개이고 100개의 새 큐를 만들려는 경우 [queue0200-queue0299] 대신 [queue0000a, queue0002a, queue0004a, …, queue0198a]를 새 큐 이름으로 선택합니다.
추가로 늘려야 하는 경우 5분마다 최대 50% 이상의 큐를 인터리브 처리할 수 있습니다.
작업을 대규모 또는 일괄로 큐에 추가
수백만 개 또는 수십억 개와 같이 많은 수의 태스크를 추가해야 하는 경우 이중 삽입 패턴이 유용할 수 있습니다. 단일 작업에서 태스크를 만드는 대신 인젝터 큐를 사용하세요. 인젝터 큐에 추가된 각 태스크는 팬아웃 처리되고 100개의 태스크를 관련 큐 또는 큐 그룹에 추가합니다. 시간 경과에 따라 인젝터 큐의 속도를 높일 수 있습니다. 예를 들어 TPS 5개로 시작하여 대상 큐 또는 큐 그룹에서 TPS 500개를 생성한 다음 5분마다 속도를 50% 씩 늘릴 수 있습니다.
이름이 지정된 태스크
새 태스크를 만들면 Cloud Tasks는 기본적으로 태스크에 고유한 이름을 할당합니다. name 파라미터를 사용하면 태스크 이름을 직접 할당할 수 있습니다. 그러나 이렇게 하면 상당한 성능 오버헤드를 유발하며, 이에 따라 지연 시간이 증가하고 명명된 작업과 관련하여 오류율이 상승할 수 있습니다. 타임스탬프와 같은 순차적 태스크 이름은 이 오버헤드를 크게 확대할 수 있습니다.
자체 이름을 할당하는 경우 콘텐츠의 해시와 같이 잘 분산된 접두사를 태스크 이름에 사용합니다. 태스크 이름 지정에 대한 자세한 내용은 Task.name 필드 참조를 확인하세요.
대상 과부하
큐 디스패치가 갑자기 급증하면 Cloud Tasks가 App Engine, Datastore 모드의 Firestore(Datastore), 네트워크와 같이 사용하는 다운스트림 서비스 및 리소스에 과부하를 일으킬 수 있습니다. 작업의 백로그가 누적된 경우 큐의 일시중지를 해제하면 일시적으로 서비스에 과부하가 걸릴 수 있습니다. 대상 과부하를 방지하려면 500/50/5 패턴을 따르세요. 즉, 큐가 TPS 500개 이상을 전달하는 경우 큐에 의해 트리거된 트래픽을 5분마다 50% 이하로 늘립니다. 트래픽 증가를 사전에 모니터링하려면 Monitoring 측정항목을 사용합니다. Monitoring 알림을 사용하여 잠재적으로 위험한 상황을 감지할 수 있습니다.
TPS가 많은 큐의 일시중지 해지 또는 다시 시작
일시중지된 큐를 재개하면 Cloud Tasks는 초기 큐 램프업을 적용하고 낮은 기준에서 큐의 구성된 max_dispatches_per_second 비율로 디스패치 비율을 점진적으로 높입니다. 그러나 TPS가 높은 큐 또는 동시에 재개하는 여러 큐에 대규모 작업 백로그가 누적되면 디스패치 속도가 다운스트림 대상 서비스의 확장 속도보다 빠르게 증가할 수 있습니다. 백로그가 큰 큐를 재개할 때 대상 오버로드를 방지하려면 여러 큐를 재개할 때 시차를 두세요. 또는 max_dispatches_per_second 비율을 일시적으로 낮추고 500/50/5 패턴에 따라 점진적으로 늘립니다. 큐가 재개될 때 Cloud Tasks가 디스패치를 제한하고 늘리는 방법에 대한 자세한 내용은 백오프 오류 및 적용된 비율을 참조하세요.
예약된 일괄 작업
동시에 디스패치하도록 예약된 태스크가 많으면 대상 오버로드 위험이 발생할 수도 있습니다. 한번에 많은 수의 작업을 시작해야 하는 경우 큐 비율 제어를 사용하여 전달 비율을 점진적으로 늘리거나 사전에 대상 용량을 프로비저닝하는 방법을 고려하세요.
팬아웃 처리 증가
Cloud Tasks에서 호출하는 서비스를 업데이트할 때 원격 호출 수를 늘리면 프로덕션 위험이 발생할 수 있습니다. 예를 들어 TPS가 많은 큐의 태스크가 핸들러 /task-foo를 호출한다고 가정해 보겠습니다. 예를 들어 새 출시 버전에서 핸들러에 비용이 많이 드는 Datastore 호출을 여러 개 추가하는 경우 새 출시 버전으로 인해 /task-foo 호출 비용이 크게 증가할 수 있습니다.
이러한 출시의 최종 결과는 사용자 트래픽의 변화와 즉시 관련된 Datastore 트래픽의 대규모 증가입니다. 점진적 출시 또는 트래픽 분할을 사용하여 점진적 확대를 관리합니다.
재시도
Cloud Tasks API를 호출할 때 실패 시 코드가 재시도할 수 있습니다. 그러나 서버 측 오류로 인해 상당 부분의 요청이 실패하는 경우 높은 비율의 재시도가 큐에 과부하를 발생시킬 수 있고 복구 속도를 저하시킬 수 있습니다. 클라이언트가 서버 측 오류로 인해 상당 부분의 요청이 실패하는 것을 감지하면 발신 트래픽 양을 제한합니다. 예를 들어 사이트 안정성 엔지니어링 교재의 과부하 처리 장에 설명된 적응형 제한 알고리즘을 사용할 수 있습니다. Google의 gRPC 클라이언트 라이브러리는 이 알고리즘의 변형을 구현합니다.