LabHub
배우기 러닝패스 코스

ネットワークトラブルシューティング

curl -wで遅い区間を名指しする

LabHub 에서 이어서 보기

한국어 원문으로 표시합니다.

한 줄 요약

curl -w 의 타이밍 필드는 요청 하나를 이름 해석 / 연결 / TLS / 첫 바이트 / 전체 다섯 구간으로 쪼개 준다. "느리다" 는 신고를 구간 이름으로 바꾸는 것이 진단의 시작이다.

フロー図: 이름 해석 / 연결 / TLS / 첫 바이트 / 전체・요청 시작으로부터의 누적 시간・DNS 를 건드리지 않고・502 를 "업스트림이 죽었다" 로 단정하는 것.

왜 이게 필요했나

"API 가 느려요" 는 정보가 거의 없는 문장이다. 이름 해석이 느린 것과 TLS 핸드셰이크가 느린 것과 백엔드 쿼리가 느린 것은 완전히 다른 문제이고 담당 팀도 다르다. 그런데 이 셋은 사용자 눈에는 똑같이 "느리다" 로 보인다.

어떻게 동작하나

한 줄이면 끝난다.

curl -o /dev/null -s -w \
 'dns=%{time_namelookup} conn=%{time_connect} tls=%{time_appconnect} ttfb=%{time_starttransfer} total=%{time_total} code=%{http_code}\n' \
 https://api.example.com/health

각 필드는 요청 시작으로부터의 누적 시간이다. 그래서 구간별 소요는 뺄셈으로 얻는다.

구간 계산 길면 의심할 곳
이름 해석 time_namelookup 리졸버, search 도메인, ndots
TCP 연결 time_connect - time_namelookup 방화벽, 거리, SYN 재전송
TLS time_appconnect - time_connect 인증서 체인, OCSP 조회, 프로토콜 협상
서버 처리 time_starttransfer - time_appconnect 애플리케이션, DB
본문 전송 time_total - time_starttransfer 대역폭, 응답 크기

읽는 요령: connect 가 타임아웃까지 늘면 방화벽, appconnect 만 길면 TLS, appconnect 는 빠른데 ttfb 만 길면 애플리케이션이나 DB.

진단에 꼭 필요한 나머지 옵션들.

curl -v https://api.example.com/health                 # 요청/응답 헤더 전체
curl --resolve api.example.com:443:10.0.1.50 https://api.example.com/health
curl -H 'Host: api.example.com' http://10.0.1.10:8080/health
curl -sD - -o /dev/null https://api.example.com/       # 헤더만
curl -L --max-redirs 10 -v https://example.com 2>&1 | grep '< [Ll]ocation'

--resolveDNS 를 건드리지 않고 특정 이름을 특정 IP 로 보내게 한다. 배포 전에 새 서버를 실제 이름으로 시험할 때 필수다. -H 'Host:' 는 IP 로 직접 붙되 가상 호스트 라우팅은 원래대로 태우는 방법이다. LB 뒤 백엔드를 직접 두드릴 때 Host 를 안 맞추면 비교 자체가 무의미해진다.

-I 는 HEAD 요청을 보낸다. 서버가 HEAD 를 다르게 처리하거나 아예 막아 둔 경우 GET 과 결과가 달라지므로, 의심스러우면 -sD - -o /dev/null 로 GET 하면서 헤더만 보는 편이 안전하다.

자주 나오는 오진

502 를 "업스트림이 죽었다" 로 단정하는 것. 502 는 프록시가 업스트림에서 유효하지 않은 응답을 받았다는 뜻이다. 응답 헤더가 프록시 버퍼보다 크면 업스트림이 멀쩡한데도 502 가 난다. 그리고 502 에 타임아웃을 늘리는 것은 무효다 — 그건 504 의 처방이다.

만료일만 보고 인증서를 넘기는 것. 브라우저는 중간 인증서를 캐시하므로 개발자 PC 에서는 정상으로 보인다. 서버 대 서버 호출은 캐시가 없어 실패한다. 체인 개수를 세어 확인한다.

echo | openssl s_client -connect api.example.com:443 -servername api.example.com -showcerts 2>/dev/null | grep -c 'BEGIN CERTIFICATE'

리다이렉트 루프를 애플리케이션에서 찾는 것. 보통은 TLS 종료 지점과 애플리케이션의 HTTPS 강제가 서로를 모르는 상황이다. X-Forwarded-Proto 를 전달하고 경계에서 덮어써야 한다.

리다이렉트 코드도 정확히 알아 두자. 302 는 POST 를 GET 으로 바꿔도 되고, 303 은 GET 을 강제하며, 307/308 은 메서드를 보존한다. 프로토콜 강제에는 308, 폼 제출 후 화면 전환에는 303 이 맞다.

현장에서 만나는 모습

"curl 은 되는데 브라우저만 실패합니다." 이 경우 서버가 아니라 브라우저 고유의 규칙을 봐야 한다. CORS 프리플라이트, SameSite 쿠키, 혼합 콘텐츠 차단, HSTS, HTTP/2 커넥션 병합(421) 이 후보다. curl 은 이 규칙들을 전혀 적용하지 않는다.

다음 실습에서 할 것

진단용 HTTP 서버를 띄우고 상태 코드·헤더·리다이렉트·가상 호스트·타이밍을 차례로 뽑는다. --resolve 로 DNS 없이 이름 접속을 만들어 보는 단계가 하이라이트다.