App Engine 앱의 지연 시간 증가 문제 해결

많은 경우 애플리케이션의 지연 시간이 상승하면 5xx 서버 오류가 발생합니다. 오류와 지연 시간 급증의 근본 원인이 동일할 수 있으므로 지연 시간 문제를 해결하려면 다음 전략을 적용하세요.

  1. 지연 시간 문제 범위 지정하기
  2. 원인 파악하기
  3. 문제 해결

지연 시간 문제 범위 지정

다음 질문을 통해 문제의 범위를 정의합니다.

  • 이 문제는 어떤 애플리케이션, 서비스, 버전에 영향을 미치나요?
  • 이 문제가 서비스의 어떤 엔드포인트에 영향을 미치나요?
  • 이 문제는 전 세계 모든 클라이언트에 영향을 미치나요, 아니면 특정 클라이언트 하위 집합에만 영향을 미치나요?
  • 해당 이슈의 시작 및 종료 시간은 언제인가요? 시간대를 지정하는 것이 좋습니다.
  • 구체적인 오류는 무엇인가요?
  • 일반적으로 특정 백분위수의 증가로 지정되는 관측된 지연 시간 델타는 무엇인가요? 예를 들어 90번째 백분위수에서 2초 단위로 증가하는 지연 시간이 있습니다.
  • 지연 시간은 어떻게 측정되었나요? 특히 클라이언트에서 측정했나요? 아니면 Cloud Logging 또는 App Engine 서빙 인프라에서 제공하는 Cloud Monitoring 지연 시간 데이터로 확인 가능한가요?
  • 서비스의 종속 항목은 무엇이고 여기에 이슈가 발생한 적이 있나요?
  • 최근에 이 문제를 트리거한 코드, 구성, 워크로드 변경이 있었나요?

서비스에는 문제 범위를 더 좁히는 데 사용할 수 있는 자체 커스텀 모니터링 및 로깅이 포함될 수 있습니다. 문제 범위를 정의하면 가능한 근본 원인을 파악하고 다음 문제 해결 단계를 결정하는 데 도움이 됩니다.

원인 파악

요청 경로에서 지연 시간 또는 오류를 일으킬 가능성이 높은 구성요소를 확인합니다. 요청 경로의 기본 구성요소는 다음과 같습니다.

클라이언트 --> 인터넷 --> Google 프런트엔드 (GFE) --> App Engine 서빙 인프라 --> 서비스 인스턴스

이전 정보로 장애 원인을 파악할 수 없는 경우 서비스 인스턴스의 상태와 성능을 검토하면서 다음 전략을 적용합니다.

  1. App Engine 요청 로그를 모니터링합니다. 이러한 로그에 HTTP 상태 코드 오류 또는 지연 시간 증가가 표시되면 서비스가 실행되는 인스턴스에 문제가 있을 수 있습니다.

  2. 서비스 인스턴스 수가 트래픽 수준에 맞게 확장되지 않은 경우 인스턴스에 과부하가 발생하여 오류와 지연 시간이 증가할 수 있습니다.

  3. Cloud Monitoring에 증가한 오류 또는 지연 시간이 표시되면 App Engine 측정항목을 기록하는 부하 분산기의 업스트림에 문제가 있을 수 있습니다. 대부분의 경우 이는 서비스 인스턴스의 문제를 가리킵니다.

  4. 모니터링 측정항목에는 지연 시간 또는 오류가 증가했지만 요청 로그에는 이러한 문제가 표시되지 않으면 부하 분산 장애 또는 부하 분산기가 요청을 라우팅하지 못하게 하는 심각한 인스턴스 장애가 있음을 나타냅니다. 이러한 경우를 구분하려면 이슈가 시작되기 전에 요청 로그를 살펴보세요. 요청 로그에서 장애 발생 전에 지연 시간이 증가한 것으로 표시되면 부하 분산기가 요청 라우팅을 중지하기 전 애플리케이션 인스턴스에 장애가 발생하기 시작했음을 나타냅니다.

문제 해결

이 섹션에서는 요청 경로의 다음 구성요소에서 발생하는 지연 시간 증가 문제에 대한 문제 해결 전략을 설명합니다.

  1. 인터넷
  2. Google 프런트엔드 (GFE)
  3. App Engine 서빙 인프라
  4. 애플리케이션 인스턴스
  5. 애플리케이션 종속 항목

인터넷

연결 상태가 좋지 않거나 대역폭이 낮아 애플리케이션에 지연 문제가 발생할 수 있습니다.

인터넷 연결 불량

인터넷 연결 불량이 문제인지 확인하려면 클라이언트에서 다음 명령어를 실행합니다.

$ curl -s -o /dev/null -w '%{time_connect}\n' <hostname>

time_connect의 값은 가장 가까운 Google 프런트엔드에 대한 클라이언트 연결의 지연 시간을 나타냅니다. 연결이 느린 경우 traceroute를 사용하여 추가 문제 해결을 수행해서 네트워크에서 지연을 일으키는 홉을 확인합니다.

여러 지리적 위치에 있는 클라이언트에서 테스트를 실행합니다. App Engine은 클라이언트 위치에 따라 가장 가까운 Google 데이터 센터로 요청을 자동으로 라우팅합니다.

대역폭 낮음

애플리케이션이 빠르게 응답할 수 있지만 네트워크 병목 현상으로 인해 App Engine 서빙 인프라가 네트워크를 통해 패킷을 빠르게 전송하지 못하여 응답 속도가 느려집니다.

Google 프런트엔드(GFE)

잘못된 라우팅, HTTP/2 클라이언트에서 전송된 동시 요청 또는 SSL 연결 종료로 인해 애플리케이션에 지연 문제가 발생할 수 있습니다.

지리적 리전에 클라이언트 IP 매핑

Google은 DNS 조회에 사용하는 클라이언트 IP 주소를 기반으로 App Engine 애플리케이션의 호스트 이름을 클라이언트에 가장 가까운 GFE로 변환합니다. 클라이언트의 DNS 리졸버가 EDNS0 프로토콜을 사용하지 않는 경우 Google에서 클라이언트 요청을 가장 가까운 GFE로 라우팅하지 않을 수 있습니다.

HTTP/2 행렬 막힘

여러 요청을 병렬로 전송하는 HTTP/2 클라이언트는 GFE에서 행렬 막힘으로 인해 지연 시간이 증가할 수 있습니다. 이 문제를 해결하려면 클라이언트가 QUIC 프로토콜을 사용해야 합니다.

커스텀 도메인을 위한 SSL 종료

GFE에서 SSL 연결을 종료할 수 있습니다. appspot.com 도메인 대신 커스텀 도메인을 사용하는 경우 SSL 종료에 추가 홉이 필요합니다. 이로 인해 일부 지역에서 실행되는 애플리케이션에 지연 시간이 추가될 수 있습니다. 자세한 내용은 커스텀 도메인 매핑을 참고하세요.

App Engine 서빙 인프라

서비스 전반의 문제 또는 자동 확장으로 인해 애플리케이션의 지연 시간이 늘어날 수 있습니다.

서비스 전체 이슈

Google은 서비스 상태 대시보드에 심각한 서비스 전반의 세부정보를 게시합니다. 하지만 Google에서는 점진적으로 출시하므로 서비스 전반의 이슈가 모든 인스턴스에 한 번에 영향을 주지 않을 가능성이 높습니다.

자동 확장

다음과 같은 자동 확장 시나리오로 인해 지연 시간이 늘어나거나 오류가 발생할 수 있습니다.

  • 트래픽을 너무 빠르게 확장: App Engine 자동 확장이 트래픽 증가만큼 빠르게 인스턴스를 확장하지 못해서 일시적으로 과부하가 발생할 수 있습니다. 일반적으로 오버로드는 트래픽이 최종 사용자가 아닌 컴퓨터 프로그램에 의해 생성될 때 발생합니다. 이 문제를 해결하려면 트래픽을 생성하는 시스템을 제한합니다.

  • 트래픽 급증: 트래픽 급증은 지연 시간에 영향을 주지 않고 자동 확장된 서비스를 가능한 것보다 더 빠르게 수직 확장해야 하는 경우에 지연 시간 증가를 일으킬 수 있습니다. 최종 사용자 트래픽은 일반적으로 잦은 트래픽 급증을 일으키지 않습니다. 트래픽이 급증하면 원인을 조사해야 합니다. 일괄 시스템이 일정 간격으로 실행되는 경우 트래픽을 원활하게 만들거나 다른 확장 설정을 사용할 수 있습니다.

  • 자동 확장 처리 설정: 자동 확장 처리는 서비스의 확장 특성을 기준으로 구성할 수 있습니다. 다음과 같은 시나리오에서는 설정 매개변수가 최적이 아닐 수 있습니다.

    • App Engine 표준 환경 확장 설정은 너무 공격적으로 설정된 경우 지연 시간을 일으킬 수 있습니다. 로그에 상태 코드 500 및 '요청을 처리하기 위해 너무 오래 기다린 후 요청이 중단되었습니다' 메시지가 포함된 서버 응답이 표시되면 이는 요청이 유휴 인스턴스를 기다리는 동안 대기 중인 큐에서 시간 초과되었음을 의미합니다.

    • 인스턴스를 충분히 프로비저닝한 경우에도 수동 확장을 사용할 때 대기 시간이 증가하는 것을 확인할 수 있습니다. 애플리케이션이 최종 사용자 트래픽을 처리하는 경우 수동 확장을 사용하지 않는 것이 좋습니다. 태스크 큐와 같은 워크로드에는 수동 확장이 더 좋습니다.

    • 기본 확장은 지연 시간을 희생하여 비용을 최소화합니다. 지연 시간에 민감한 서비스에는 기본 확장을 사용하지 않는 것이 좋습니다.

    • App Engine의 기본 확장 설정은 대부분의 서비스에서 최적의 지연 시간을 제공합니다. 여전히 대기 시간이 긴 요청이 표시되면 최소 인스턴스 수를 지정합니다. 유휴 인스턴스를 최소화하여 비용을 줄이도록 확장 설정을 조정할 경우에는 부하가 갑자기 증가할 때 지연 시간이 급증할 위험이 있습니다.

기본 확장 설정을 사용하여 성능을 벤치마크한 후 이러한 설정을 변경할 때마다 새로 벤치마크를 실행하는 것이 좋습니다.

배포

배포 후 잠시 동안 지연 시간이 증가하면 트래픽 마이그레이션 이전에 충분히 수직 확장이 수행되지 않았음을 나타냅니다. 신규 인스턴스에서 로컬 캐시가 워밍업되지 않아서 이전 인스턴스보다 처리가 더 느려질 수 있습니다.

지연 시간 급증을 방지하려면 기존 서비스 버전과 동일한 버전 이름을 사용하여 App Engine 서비스를 배포하지 마세요. 기존 버전 이름을 재사용하면 트래픽을 새 버전으로 느리게 마이그레이션할 수 없습니다. App Engine이 짧은 시간 내에 모든 인스턴스를 다시 시작하기 때문에 요청이 더 느릴 수 있습니다. 또한 이전 버전으로 되돌리려면 다시 배포해야 합니다.

애플리케이션 인스턴스

이 섹션에서는 성능을 최적화하고 지연 시간을 줄이기 위해 애플리케이션 인스턴스와 소스 코드에 적용할 수 있는 일반적인 전략을 설명합니다.

애플리케이션 코드

애플리케이션 코드의 문제는 간헐적이거나 재현되지 않을 때 특히 디버깅하기 어려울 수 있습니다.

문제를 해결하려면 다음 단계를 따르세요.

  • 문제를 진단하려면 로깅, 모니터링, 추적을 사용하여 애플리케이션을 계측하는 것이 좋습니다. Cloud Profiler를 사용할 수도 있습니다.

  • App Engine 내에서는 실행이 불가능할 수 있는 언어 특정 디버깅 도구를 실행할 수 있게 해주는 로컬 개발 환경에서 문제 재현을 시도해 보세요.

  • 애플리케이션이 실패하는 방식, 발생하는 병목 현상을 잘 이해하기 위해서는 실패할 때까지 애플리케이션 부하를 테스트할 수 있습니다. 최대 인스턴스 수를 설정한 후 애플리케이션이 실패할 때까지 점진적으로 부하를 늘립니다.

  • 지연 시간 문제가 새 버전의 애플리케이션 코드 배포와 상관된 경우 롤백을 통해 새 버전으로 인해 이슈가 발생했는지 확인할 수 있습니다. 그러나 지속적으로 배포하는 경우 배포가 충분히 빈번할 수 있으므로 배포가 시작된 시간을 기준으로 배포가 이슈의 원인이 되었는지 여부를 판단하기 어려울 수 있습니다.

  • 애플리케이션은 Datastore 내부 또는 다른 위치에 구성 설정을 저장할 수 있습니다. 구성 변경사항에 대한 타임라인을 만들어 이러한 변경사항이 지연 시간 증가의 시작 시간과 일치하는지 확인합니다.

워크로드 변경

워크로드 변경은 지연 시간 증가를 일으킬 수 있습니다. 워크로드 변경을 나타낼 수 있는 일부 모니터링 측정항목에는 qps, API 사용량, 지연 시간이 포함됩니다. 요청 및 응답 크기의 변경사항도 확인합니다.

메모리 압력

모니터링 결과에 메모리 사용량이 톱니 패턴으로 표시되거나 배포와 상관되는 메모리 사용량 저하가 표시되면 메모리 누수로 인한 성능 문제일 수 있습니다. 메모리 누수는 잦은 가비지 컬렉션을 일으켜서 높은 지연 시간으로 이어질 수도 있습니다. 이 문제를 코드의 문제로 추적할 수 없는 경우 메모리가 더 많은 더 큰 인스턴스를 프로비저닝해 보세요.

리소스 누수

애플리케이션 인스턴스에 인스턴스 기간과 상관되는 지연 시간 증가가 표시되면 성능 문제를 일으키는 리소스 누수가 있을 수 있습니다. 배포가 완료되면 지연 시간이 줄어듭니다. 예를 들어 높은 CPU 사용량으로 인해 시간이 지날수록 더 느려지는 데이터 구조는 CPU에 바인딩된 워크로드가 더 느려지게 만들 수 있습니다.

코드 최적화

App Engine에서 지연 시간을 줄이려면 다음 메서드를 사용하여 코드를 최적화하세요.

  • 오프라인 작업: Cloud Tasks를 사용하여 사용자 요청으로 인해 메일 전송과 같은 작업이 완료될 때까지 기다리는 대기 작업이 차단되지 않도록 합니다.

  • 비동기 API 호출: API 호출이 완료될 때까지 기다리는 코드가 차단되지 않도록 합니다.

  • 일괄 API 호출: 일반적으로 개별 호출을 전송하는 것보다는 일괄적인 API 호출이 더 빠릅니다.

  • 데이터 모델 비정규화: 데이터 모델을 비정규화하여 데이터 지속성 레이어에 수행되는 호출의 지연 시간을 줄입니다.

애플리케이션 종속 항목

애플리케이션의 종속 항목을 모니터링하여 지연 시간 급증이 종속 항목 실패와 상관되었는지 감지합니다.

워크로드 변경과 트래픽 증가로 인해 종속 항목의 지연 시간이 증가할 수 있습니다.

비확장 종속 항목

App Engine 인스턴스 수가 늘어남에 따라 애플리케이션의 종속 항목이 확장되지 않으면 트래픽이 증가할 때 종속 항목에 과부하가 발생할 수 있습니다. 확장되지 않을 수 있는 종속 항목의 예시에는 SQL 데이터베이스가 있습니다. 애플리케이션 인스턴스 수가 많을수록 데이터베이스 연결 수가 늘어나서 데이터베이스가 시작되지 않아 연쇄적 실패가 발생할 수 있습니다. 이 문제를 해결하려면 다음 단계를 따르세요.

  1. 데이터베이스에 연결되지 않는 새로운 기본 버전을 배포합니다.
  2. 이전 기본 버전을 종료합니다.
  3. 데이터베이스에 연결되는 새로운 비기본 버전을 배포합니다.
  4. 트래픽을 새 버전으로 느리게 마이그레이션합니다.

예방 조치로 적응형 제한을 사용하여 종속 항목에 대한 요청을 줄이도록 애플리케이션을 설계합니다.

캐싱 레이어 실패

요청 속도를 높이려면 에지 캐싱, Memcache, 인스턴스 내 메모리와 같은 여러 캐싱 레이어를 사용하세요. 이러한 캐싱 레이어 중 하나에 실패가 발생하면 갑작스러운 지연 시간 증가가 발생할 수 있습니다. 예를 들어 Memcache 플러시는 더 많은 요청이 더 느린 Datastore로 이동하도록 만들 수 있습니다.