확장 위험 관리

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 응답을 반환하고 시행된 디스패치 속도를 재설정하는 HTTP 429 또는 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 클라이언트 라이브러리는 이 알고리즘의 변형을 구현합니다.