idea·blog

SERVER ERROR CLINIC

ETIMEDOUT (응답이 없음)

요청을 보냈는데 거절도 응답도 없이 조용합니다. 거부(ECONNREFUSED)와 달리 한참 기다린다는 점이 결정적 단서입니다.

이 도구는 여러분의 서버에 접속하지 않습니다. 답한 내용만으로 확인할 순서를 정해주는 도구이며, 실제 로그를 대신하지 않습니다.

  1. 브라우저요청을 보낸 곳
  2. 인터넷여기가 의심됩니다
  3. 프록시Nginx·Caddy
  4. Node·Spring·Django
  5. DB·외부 API앱이 부르는 곳

어느 로그를 볼까 연결 시간 초과라면 경로·방화벽·보안그룹을, 연결 뒤 응답 시간 초과라면 앱·DB·외부 API를 봅니다. 먼저 어느 단계의 timeout인지 구분합니다.

원인 후보 3

오류가 뜨기까지 얼마나 걸렸나요?

걸린 시간은 어느 설정의 제한시간에 걸렸는지 알려주는 지문입니다.

가장 먼저 확인할 것

중간에서 조용히 버려지고 있다

방화벽이나 보안그룹이 패킷을 거절하지 않고 그냥 버립니다. 그래서 거부 대신 한참 기다리다 실패합니다.

다른 후보가 하나씩 제외되면서 남은 원인입니다.

그 포트까지 실제로 길이 열려 있는지 확인합니다.

nc -vz -w 5 api.example.com 3000

그다음 서버의 방화벽만 보지 말고 클라우드 보안그룹도 함께 봅니다. 둘 중 하나만 막혀 있어도 결과는 같습니다.

패킷이 사라지면 생기는 일

아니라면 다음

상대가 살아 있지만 너무 느리다

외부 API나 데이터베이스가 응답을 못 내주고 있습니다. 연결은 됐는데 답이 안 오는 상태입니다.

연결까지는 빠른데 응답만 느린지 나눠서 잽니다.

curl -o /dev/null -s -w '연결 %{time_connect}s / 첫 바이트 %{time_starttransfer}s\n' https://api.example.com/

그다음 호출하는 쪽에 제한시간과 재시도 상한을 정해 둡니다. 제한시간이 없으면 느린 상대 하나가 내 서버 전체를 멈추게 합니다.

아니라면 다음

쉬는 동안 연결이 끊겨 있었다

오래 쓰지 않은 연결을 중간 장비가 조용히 정리했습니다. 앱은 끊긴 줄 모르고 그 연결을 다시 쓰다 기다립니다.

오래 붙잡고 있는 연결이 있는지 봅니다.

ss -tan state established | head -20

그다음 연결 풀의 최대 유휴 시간을 중간 장비의 정리 시간보다 짧게 잡습니다. 그래야 내가 먼저 정리합니다.

연결이 언제 닫히는지 보기

실험을 마쳤다면