호스트 이름을 확인하는 데 5초가 걸립니다.

호스트 이름을 확인하는 데 5초가 걸립니다.

bind9다음을 사용하여 IPv4(Debian Jessie)에서 실행되는 마스터 DNS 서버와 2개의 슬레이브 서버가 있습니다 /etc/bind/named.conf.

listen-on-v6 { none; };

다른 서버에서 연결을 시도하면 각 연결에 최소 5초가 걸립니다(저는요셉의 시간정보디버깅용):

$ curl -w "@curl-format.txt" -o /dev/null -s https://example.com
            time_namelookup:  5.512
               time_connect:  5.512
            time_appconnect:  5.529
           time_pretransfer:  5.529
              time_redirect:  0.000
         time_starttransfer:  5.531
                            ----------
                 time_total:  5.531

에 따르면 curl조회에 대부분의 시간이 걸렸지만 기준은 nslookup매우 빠릅니다.

$ time nslookup example.com > /dev/null 2>&1

real    0m0.018s
user    0m0.016s
sys     0m0.000s

IPv4를 강제로 curl사용하면 상황이 훨씬 좋아집니다.

$ curl -4 -w "@curl-format.txt" -o /dev/null -s https://example.com

            time_namelookup:  0.004
               time_connect:  0.005
            time_appconnect:  0.020
           time_pretransfer:  0.020
              time_redirect:  0.000
         time_starttransfer:  0.022
                            ----------
                 time_total:  0.022

호스트 시스템에서 IPv6을 비활성화했습니다.

echo 1 > /proc/sys/net/ipv6/conf/eth0/disable_ipv6

문제는 여전히 존재하지만. strace시간 초과의 원인을 확인하기 위해 실행을 시도했습니다 .

write(2, "*", 1*)                        = 1
write(2, " ", 1 )                        = 1
write(2, "Hostname was NOT found in DNS ca"..., 36Hostname was NOT found in DNS cache
) = 36
socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP) = 4
close(4)                                = 0
mmap(NULL, 8392704, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_STACK, -1, 0) = 0x7f220bcf8000
mprotect(0x7f220bcf8000, 4096, PROT_NONE) = 0
clone(child_stack=0x7f220c4f7fb0, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, parent_tidptr=0x7f220c4f89d0, tls=0x7f220c4f8700, child_tidptr=0x7f220c4f89d0) = 2004
rt_sigaction(SIGPIPE, {SIG_IGN, [PIPE], SA_RESTORER|SA_RESTART, 0x7f22102e08d0}, NULL, 8) = 0
rt_sigaction(SIGPIPE, NULL, {SIG_IGN, [PIPE], SA_RESTORER|SA_RESTART, 0x7f22102e08d0}, 8) = 0
rt_sigaction(SIGPIPE, {SIG_IGN, [PIPE], SA_RESTORER|SA_RESTART, 0x7f22102e08d0}, NULL, 8) = 0
rt_sigaction(SIGPIPE, {SIG_IGN, [PIPE], SA_RESTORER|SA_RESTART, 0x7f22102e08d0}, NULL, 8) = 0
poll(0, 0, 4)                           = 0 (Timeout)
rt_sigaction(SIGPIPE, NULL, {SIG_IGN, [PIPE], SA_RESTORER|SA_RESTART, 0x7f22102e08d0}, 8) = 0
rt_sigaction(SIGPIPE, {SIG_IGN, [PIPE], SA_RESTORER|SA_RESTART, 0x7f22102e08d0}, NULL, 8) = 0
rt_sigaction(SIGPIPE, {SIG_IGN, [PIPE], SA_RESTORER|SA_RESTART, 0x7f22102e08d0}, NULL, 8) = 0
poll(0, 0, 8)                           = 0 (Timeout)
rt_sigaction(SIGPIPE, NULL, {SIG_IGN, [PIPE], SA_RESTORER|SA_RESTART, 0x7f22102e08d0}, 8) = 0
rt_sigaction(SIGPIPE, {SIG_IGN, [PIPE], SA_RESTORER|SA_RESTART, 0x7f22102e08d0}, NULL, 8) = 0
rt_sigaction(SIGPIPE, {SIG_IGN, [PIPE], SA_RESTORER|SA_RESTART, 0x7f22102e08d0}, NULL, 8) = 0
poll(0, 0, 16)                          = 0 (Timeout)
rt_sigaction(SIGPIPE, NULL, {SIG_IGN, [PIPE], SA_RESTORER|SA_RESTART, 0x7f22102e08d0}, 8) = 0
rt_sigaction(SIGPIPE, {SIG_IGN, [PIPE], SA_RESTORER|SA_RESTART, 0x7f22102e08d0}, NULL, 8) = 0
rt_sigaction(SIGPIPE, {SIG_IGN, [PIPE], SA_RESTORER|SA_RESTART, 0x7f22102e08d0}, NULL, 8) = 0
poll(0, 0, 32)                          = 0 (Timeout)
rt_sigaction(SIGPIPE, NULL, {SIG_IGN, [PIPE], SA_RESTORER|SA_RESTART, 0x7f22102e08d0}, 8) = 0
rt_sigaction(SIGPIPE, {SIG_IGN, [PIPE], SA_RESTORER|SA_RESTART, 0x7f22102e08d0}, NULL, 8) = 0
rt_sigaction(SIGPIPE, {SIG_IGN, [PIPE], SA_RESTORER|SA_RESTART, 0x7f22102e08d0}, NULL, 8) = 0
poll(0, 0, 64)                          = 0 (Timeout)

이는 동일한 DNS 서버를 사용 nslookup하거나 사용하기 때문에 방화벽 문제가 아닌 것 같습니다 . curl -4무엇이 잘못될 수 있는지 아시나요?

다음은 tcpdump원본 포스터의 내용입니다 tcpdump -vvv -s 0 -l -n port 53.

tcpdump: listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
20:14:52.542526 IP (tos 0x0, ttl 64, id 35839, offset 0, flags [DF], proto UDP (17), length 63)
    192.168.1.1.59163 > 192.168.1.2.53: [bad udp cksum 0xf9f3 -> 0x96c7!] 39535+ A? example.com. (35)
20:14:52.542540 IP (tos 0x0, ttl 64, id 35840, offset 0, flags [DF], proto UDP (17), length 63)
    192.168.1.1.59163 > 192.168.1.2.53: [bad udp cksum 0xf9f3 -> 0x6289!] 45997+ AAAA? example.com. (35)
20:14:52.543281 IP (tos 0x0, ttl 61, id 63674, offset 0, flags [none], proto UDP (17), length 158)
    192.168.1.2.53 > 192.168.1.1.59163: [udp sum ok] 45997* q: AAAA? example.com. 1/1/0 example.com. [1h] CNAME s01.example.com. ns: example.com. [10m] SOA ns01.example.com. ns51.domaincontrol.com. 2016062008 28800 7200 1209600 600 (130)
20:14:57.547439 IP (tos 0x0, ttl 64, id 36868, offset 0, flags [DF], proto UDP (17), length 63)
    192.168.1.1.59163 > 192.168.1.2.53: [bad udp cksum 0xf9f3 -> 0x96c7!] 39535+ A? example.com. (35)
20:14:57.548188 IP (tos 0x0, ttl 61, id 64567, offset 0, flags [none], proto UDP (17), length 184)
    192.168.1.2.53 > 192.168.1.1.59163: [udp sum ok] 39535* q: A? example.com. 2/2/2 example.com. [1h] CNAME s01.example.com., s01.example.com. [1h] A 136.243.154.168 ns: example.com. [30m] NS ns01.example.com., example.com. [30m] NS ns02.example.com. ar: ns01.example.com. [1h] A 136.243.154.168, ns02.example.com. [1h] A 192.168.1.2 (156)
20:14:57.548250 IP (tos 0x0, ttl 64, id 36869, offset 0, flags [DF], proto UDP (17), length 63)
    192.168.1.1.59163 > 192.168.1.2.53: [bad udp cksum 0xf9f3 -> 0x6289!] 45997+ AAAA? example.com. (35)
20:14:57.548934 IP (tos 0x0, ttl 61, id 64568, offset 0, flags [none], proto UDP (17), length 158)
    192.168.1.2.53 > 192.168.1.1.59163: [udp sum ok] 45997* q: AAAA? example.com. 1/1/0 example.com. [1h] CNAME s01.example.com. ns: example.com. [10m] SOA ns01.example.com. ns51.domaincontrol.com. 2016062008 28800 7200 1209600 600 (130)

편집하다: 바인딩 로그에 다음 메시지가 자주 나타납니다.

error sending response: host unreachable

그러나 모든 쿼리는 결국 응답됩니다(5초만 소요). 모든 시스템은 물리적 서버입니다(이것은 NAT의 결함이 아닙니다). 라우터에 의해 패킷이 차단될 가능성이 더 높습니다. 이것은 매우 관련성이 높은 질문입니다.DNS 조회에 때때로 5초가 소요됨.

답변1

짧은 답변:

해결 방법은 다음 줄을 추가하여 glibc조회 및 로깅을 위해 소켓을 강제로 재사용하는 것입니다.AAAAA/etc/resolv.conf

options single-request-reopen

이 문제의 실제 원인은 다음과 같습니다.

긴 답변:

curlwgetglibc의 기능과 유사하거나 이를 사용하는 프로그램주소정보 가져오기(), DNS 레코드를 병렬로 조회하여 IPv4 및 IPv6 호환을 시도합니다. 두 개의 레코드가 수신될 때까지 결과를 반환하지 않습니다.이 동작과 관련된 몇 가지 문제) - strace위와 같은 내용이 설명됩니다. 예를 들어 IPv4를 강제로 사용하는 경우 curl -4내부적으로 레코드만 gethostbyname()쿼리됩니다 A.

이것으로부터 tcpdump우리는 다음을 볼 수 있습니다:

  • -> A?처음에 두 개의 요청을 보냅니다.
  • -> AAAA?(IPv6 주소 요청)
  • <- AAAA회신하다
  • -> A?IPv4 주소를 다시 요청하세요.
  • <- A답장을 받았습니다
  • -> AAAA?IPv6 다시 요청
  • <- AAAA회신하다

A어떤 이유로 답글이 삭제되었으며 다음과 같은 오류 메시지가 표시됩니다.

error sending response: host unreachable

AAAA그러나 두 번째 쿼리가 필요한 이유는 명확하지 않습니다 .

동일한 문제가 발생하는지 확인하려면 다음에서 시간 초과를 업데이트할 수 있습니다 /etc/resolv.conf.

options timeout:3

먼저 텍스트 파일을 만듭니다.맞춤 시간 보고 구성:

cat >./curl-format.txt  <<-EOF
   time_namelookup: %{time_namelookup}\n
      time_connect: %{time_connect}\n
   time_appconnect: %{time_appconnect}\n
     time_redirect: %{time_redirect}\n
  time_pretransfer: %{time_pretransfer}\n
time_starttransfer: %{time_starttransfer}\n
                    ----------\n
time_total: %{time_total}\n
EOF

그런 다음 요청을 보냅니다.

$ curl -w "@curl-format.txt" -o /dev/null -s https://example.com

            time_namelookup:  3.511
               time_connect:  3.511
            time_appconnect:  3.528
           time_pretransfer:  3.528
              time_redirect:  0.000
         time_starttransfer:  3.531
                            ----------
                 time_total:  3.531

다른 두 가지 관련 옵션이 있습니다 man resolv.conf.

단일 요청(glibc 2.10부터)설정 기본적 RES_SNGLKUP 으로 _res.optionsglibc는 버전 2.9부터 IPv4 및 IPv6 조회를 병렬로 수행합니다. 일부 장치 DNS 서버는 이러한 쿼리를 올바르게 처리하지 않아 요청 시간이 초과됩니다. 이 옵션은 이 동작을 비활성화하고 glibc가 IPv6 및 IPv4 요청을 순차적으로 수행하도록 합니다(구문 분석 프로세스가 느려지지만).

단일 요청 다시 열기(glibc 2.9부터) 확인자는 A 및 AAAA 요청에 동일한 소켓을 사용합니다. 일부 하드웨어는 하나의 응답만 잘못 돌려보냅니다. 이런 일이 발생하면 클라이언트 시스템은 앉아서 두 번째 응답을 기다립니다. 이 옵션을 켜면 이 동작이 변경되어 동일한 포트의 두 요청이 올바르게 처리되지 않으면 두 번째 요청을 보내기 전에 소켓을 닫고 새 소켓을 엽니다.

관련 질문:

답변2

@Tombart가 말했듯이 IPv6 해상도 시간 초과를 기다리면 지연이 발생합니다.

또 다른 가능성은 /etc/gai.conf에서 IPv4의 우선순위를 지정하는 것입니다.

/etc/gai.conf의 의견

#   For sites which prefer IPv4 connections change the last line to
#
precedence ::ffff:0:0/96  100

변경한 후 gai.conf변경 사항을 적용하려면 DNS 확인자 라이브러리를 사용하는 모든 애플리케이션을 다시 시작해야 합니다.

namedIPv6 연결 없이 BIND 서버를 사용하는 경우 IPv6를 비활성화 하고 루트 프롬프트에서 IPv6 주소를 가져오는 것이 좋습니다 . 분명히 여전히 AAAA 주소를 확인하려고 시도할 것입니다.

따라서 BIND 구성의 경우

/etc/default/bind9에서 IPv4 주소에 -4를 추가합니다.

OPTIONS="-4 -u bind"

그리고 .NET에서 /etc/bind/db.rootAAAA DNS 루트가 있는 모든 행을 삭제합니다 .

답변3

BIND9를 사용하여 비슷한 문제가 발생했습니다. 이 문제를 해결하려면 다음을 추가해야 합니다.

filter-aaaa-on-v4 yes;

내 옵션 named.conf.

(추가 정보)

답변4

누군가가 컬 형식.txt를 찾고 있다면. 이것을 쉘에 붙여넣으면 형식 파일이 생성됩니다. 원본 링크가 작동하지 않습니다. 이 예를 찾았습니다여기

cat >./curl-format.txt  <<-EOF
   time_namelookup: %{time_namelookup}\n
      time_connect: %{time_connect}\n
   time_appconnect: %{time_appconnect}\n
     time_redirect: %{time_redirect}\n
  time_pretransfer: %{time_pretransfer}\n
time_starttransfer: %{time_starttransfer}\n
                    ----------\n
time_total: %{time_total}\n
EOF

관련 정보