‘폰 서버 안정화 일지’ 다섯 번째 글이다. 느린 건 서버가 아니라 측정기였다는 걸 두 번이나 겪었다. 한 번은 블로그 성능 진단에서, 한 번은 메인 터널 감시에서. 둘 다 범인은 DNS였다.
사건 1: TTFB 5.7초라는 위기
블로그 SEO 점검을 하다가 첫 바이트 도착 시간(TTFB)을 재 보니 5.6~5.8초가 나왔다. 검색엔진이 싫어할 숫자다. 서버 자체는 폰 안에서 직접 치면 20~80ms로 빨랐으니, 터널 구간을 의심했다. 실제로 cloudflared 로그엔 timeout: no recent network activity라는 QUIC 재연결 폭풍이 찍혀 있었고, 그걸 근거로 “터널이 느리다"고 결론 내렸다. 이 cloudflared 버전은 전송 프로토콜을 고르는 옵션이 제거돼 있어 설정으로 고칠 길이 없었고, 대안으로 Cloudflare Cache Rule을 만들어 엣지 캐시를 켰다.
캐시가 cf-cache-status: HIT로 맞는데도 TTFB는 여전히 5.4초였다. 그제야 의심의 방향을 바꿨다. 아무 상관없는 사이트도 같이 재 봤다.
curl -o /dev/null -s -w 'lookup=%{time_namelookup} ttfb=%{time_starttransfer}\n' https://example.com
lookup=5.17 ttfb=5.36
example.com도 5.3초였다. 그리고 time_namelookup만 5.17초였다. 내 블로그가 느린 게 아니라, 측정을 돌리던 proot 컨테이너의 로컬 DNS 리졸버가 어떤 이름이든 5초 넘게 걸리는 것이었다. IP로 직접 붙으면 0.13초였다.
진짜 수치는 DNS를 우회해서 얻었다. Cloudflare의 DNS-over-HTTPS JSON API로 주소를 받은 뒤 --resolve로 리졸버를 건너뛰었다.
curl -s -H 'accept: application/dns-json' 'https://cloudflare-dns.com/dns-query?name=<호스트>&type=A'
curl -o /dev/null -s -w '%{time_starttransfer}\n' --resolve '<호스트>:443:<받은 주소>' https://<호스트>/
콜드 0.60초, 웜 0.23초. 애초에 느린 적이 없었다. Cache Rule과 HTTPS 강제는 그 자체로 이득이라 남겼지만, “위기"는 오진이었다고 바로잡아 기록했다. 터널 로그의 QUIC 재연결은 사실이었지만 체감 속도의 원인은 아니었다.
사건 2: 메인 터널이 하루 14~15번 재시작
몇 주 뒤, 봇·기록기 쪽 메인 터널이 하루 14~15회 재시작되고 있는 걸 발견했다. 블로그 터널 때와 같은 모양이라 바로 점검 명령을 재현했다. 외부 주소 점검 40번이 전부 정상(403, 토큰 없이 붙으면 그렇게 답한다)이었지만 매번 약 5.7초가 걸렸고, 지연은 전부 DNS 조회였다.
curl -4 ... lookup=0.07
curl ... lookup=5.20
IPv4만 조회하면 0.07초, A와 AAAA를 동시에 조회하면 5.2초. 이 폰의 네트워크에서 AAAA 응답이 돌아오지 않아 리졸버가 타임아웃까지 기다리는 것이다. 같은 서버의 다른 호스트명도 전부 똑같았다. 워치독의 한도는 8초였으니 여유가 2초뿐이고, 재시도가 한 번 붙으면 실패로 판정되어 터널을 재시작했다. 재시작 한 번에 3~5초 끊기니, 역시 없던 불가용을 만들고 있었다.
수정: curl -4, 15초, 3회 연속
워치독만 고쳤다. 부팅 스크립트와 resolv.conf는 건드리지 않았다. 외부 주소 점검에 -4를 붙여 IPv4만 조회하고, 제한 시간을 8초에서 15초로 늘리고, 블로그 터널과 같은 방식으로 3회 연속 실패 때만 재시작하도록 했다. 실패 횟수는 /root/.maintunnel-fail-count에 기록하고 정상이면 0으로 돌린다. 재시작 명령 줄은 원래와 동일함을 diff로 대조했다.
curl -s -4 --max-time 15 -o /dev/null -w '%{http_code}' "$URL"
고친 직후 점검 한 번이 0.59초에 끝났다(전에는 약 5.7초). 11시간 뒤 재시작 0회, 그 사이 워치독은 “정상 — 조치 없음"을 135번 찍었다. 24시간, 그리고 35시간 뒤에도 0회였다. 전에는 하루 14~15회였다.
근본 원인인 리졸버 설정(options single-request)은 승인 범위 밖이라 두었다. 봇이 외부로 나가는 다른 호출의 5초 지연은 그대로 남아 있다는 것도 함께 기록했다.
정리
- 느리면 먼저 측정기를 의심한다. 상관없는 사이트를 같은 방법으로 재 보는 대조 실험이 가장 싸다.
curl -w의 단계별 시간(time_namelookup,time_connect,time_starttransfer)을 쪼개 보면 범인이 어느 구간인지 바로 나온다.- 로그에 찍힌 사실(QUIC 재연결)이 체감 문제의 원인이라는 보장은 없다. 상관과 원인을 분리해야 한다.
- 점검 명령은 운영 환경의 네트워크 특성(IPv6 응답 없음)에 맞춰야 한다.
-4한 글자가 하루 15번의 재시작을 0으로 만들었다.
🤖 이 포스팅은 AI의 도움을 받아 작성되었습니다.


💬 댓글