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 대기 행렬 막힘

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

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

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

App Engine 서빙 인프라

서비스 전체 이슈나 자동 확장으로 인해 애플리케이션에서 지연 시간이 증가할 수 있습니다.

서비스 전체 이슈

Google은 Service Health 대시보드에 심각한 서비스 전체 이슈의 세부정보를 게시합니다. 하지만 Google은 서비스 전체 이슈가 모든 인스턴스에 한 번에 영향을 주지 않도록 점진적으로 출시합니다.

자동 확장

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

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

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

  • 자동 확장 처리 설정: 자동 확장 처리는 서비스의 확장 특성을 기반으로 구성될 수 있습니다. 다음과 같은 시나리오에서는 확장 파라미터가 최적이 아닐 수 있습니다.

    • App Engine 가변형 환경 서비스는 CPU 사용률을 기반으로 확장됩니다. CPU 기반 확장이 발생하지 않으므로 이슈 중 애플리케이션이 I/O에 바인딩되어 다수의 요청으로 인스턴스에 과부하가 발생할 수 있습니다.

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

배포

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

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

애플리케이션 인스턴스

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

애플리케이션 코드

애플리케이션 코드의 문제는 특히 간헐적이거나 재현할 수 없는 경우에 디버깅하기 어려울 수 있습니다.

문제를 해결하려면 다음을 수행합니다.

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

  • App Engine 내에서는 실행이 불가능할 수 있는 언어별 디버깅 도구를 실행할 수 있게 해주는 로컬 개발 환경에서 문제를 재현해 봅니다.

  • SSH를 통해 인스턴스에 연결하고 스레드 덤프를 수집하여 현재 애플리케이션 상태를 확인할 수 있습니다. 부하 테스트에서 또는 애플리케이션을 로컬로 실행하여 문제를 재현합니다. 인스턴스 크기를 늘려 문제가 해결되는지 확인할 수 있습니다. 예를 들어 RAM을 늘리면 가비지 컬렉션으로 인해 지연이 발생하는 애플리케이션 문제가 해결될 수 있습니다.

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

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

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

워크로드 변경

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

상태 점검 실패

App Engine 가변형 환경 부하 분산기는 상태 점검이 실패한 인스턴스로의 요청 라우팅을 중지합니다. 그 결과 다른 인스턴스의 부하가 증가하고 연쇄적으로 실패가 발생할 수 있습니다. Nginx 로그에는 상태 점검이 실패한 인스턴스가 표시됩니다. 로그와 모니터링을 분석하여 인스턴스가 비정상적이게 된 이유를 확인하거나 일시적인 실패에 덜 민감하도록 상태 점검을 구성합니다. 부하 분산기가 비정상 인스턴스로의 트래픽 라우팅을 중지하기 전에 짧은 지연이 발생합니다. 이러한 지연으로 인해 부하 분산기가 요청을 재시도할 수 없으면 오류가 급증될 수 있습니다.

메모리 압력

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

리소스 누수

애플리케이션 인스턴스에 인스턴스 기간과 관련되는 지연 시간 증가가 표시되는 경우 이는 성능 문제를 일으키는 메모리 누수가 발생할 수 있음을 나타냅니다. 배포가 완료되면 지연 시간이 줄어듭니다. 예를 들어 CPU 사용량이 높아 시간이 지날수록 더 느려지는 데이터 구조로 인해 CPU에 바인딩된 워크로드가 더 느려질 수 있습니다.

코드 최적화

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

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

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

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

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

애플리케이션 종속 항목

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

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

비확장 종속 항목

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

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

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

캐싱 레이어 실패

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