Zettelkasten

APM에는 로드밸런서가 대신 만든 5xx가 안 잡힌다

·수정 1회

요약

  • APM은 앱이 직접 만든 응답만 기록한다. 앱이 죽거나 연결이 끊겨서 ALB가 대신 만든 502·503·504는 앱을 거치지 않으니 남지 않는다.
  • 어떤 서비스에서 7일간 ALB가 만든 502가 3,218건인데 앱이 만든 5xx는 643건이었다. APM만 보면 실패의 80%를 못 본다.
  • "APM에 에러가 별로 없으니 괜찮다"는 판단은 위험하다. CloudWatch를 따로 봐야 한다.

본문

왜 안 보이나

ALB가 세는 5xx는 두 종류다.

  • HTTPCode_Target_5XX_Count — 앱이 5xx를 응답했다. 앱을 거쳤으니 APM에도 남는다.
  • HTTPCode_ELB_5XX_Count — ALB가 스스로 만들었다. 타깃에 연결이 안 되거나, 응답 도중 끊기거나, 타임아웃이 났을 때다. 앱이 응답을 만든 적이 없으니 APM에 없다.

APM 에이전트는 앱 안에 붙어서 요청 시작과 끝을 잰다. 프로세스가 죽거나 워커가 응답 중간에 끊기면 기록을 남길 주체 자체가 사라진다.

얼마나 차이 나나

운영 중인 Django API 서비스에서 7일치를 봤다.

지표 7일 합계
전체 요청 약 8,490만
ALB가 만든 502 3,218
앱이 만든 5xx 643
연결 실패 0

ALB가 만든 실패가 앱이 만든 것의 5배다. 연결 실패가 0인데 502가 나온다는 건, 연결은 됐는데 응답 도중에 끊겼다는 뜻이다. gunicorn 워커 타임아웃이나 재시작이 유력하다.

한 장애 구간에서는 APM이 5xx를 3건으로 보여줬는데, 같은 5분에 CloudWatch는 502를 15건 세고 있었다. APM만 보고 장애 규모를 판단하면 5분의 1로 줄여서 본다.

특히 위험해지는 상황

로드밸런싱을 least outstanding requests로 바꿀 때다. 이 방식은 처리 중인 요청이 적은 타깃을 고르는데, 즉시 실패하는 타깃은 처리 중인 요청이 0에 가까워서 오히려 더 많이 받게 된다. 그런데 그 실패 모드가 정확히 APM에 안 보이는 종류다. "APM에서 5xx가 몇 건뿐이라 안전하다"는 근거는 볼 수 없는 데이터에 기댄 셈이 된다.

어떻게 확인하나

건수보다 뭉쳐 있는지가 중요하다. 타깃 하나가 갑자기 실패를 뿜는 패턴이 있었는지 5분 단위로 본다.

aws cloudwatch get-metric-statistics --namespace AWS/ApplicationELB \
  --metric-name HTTPCode_ELB_502_Count \
  --dimensions Name=LoadBalancer,Value=<lb-dimension> \
  --period 300 --statistics Sum

위 사례에서는 3일간 864개 창 중 621개에 502가 있었고, 창당 평균 2.3건에 최대 18건이었다. 늘 조금씩 깔려 있는 것이지 한 번에 터진 적은 없었다.

한계도 있다. CloudWatch는 ALB가 만든 5xx를 타깃별로 나눠주지 않는다. "뭉친 적 없다"까지가 여기서 알 수 있는 전부고, "특정 타깃에 몰리지 않았다"를 직접 보려면 ALB 액세스 로그를 뜯어야 한다.

관련 노트

참고