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)
nslookup
(또는 curl -4
)이 동일한 DNS 서버를 사용하고 있어 방화벽 문제는 아닌 것 같습니다 . 무엇이 잘못되었을지 아시나요?
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
조회를 위해 소켓을 강제로 재사용하는 것 입니다 .AAAA
A
/etc/resolv.conf
options single-request-reopen
이 문제의 실제 원인은 다음과 같습니다.
- 잘못 구성된 방화벽이나 라우터(예:여기에 설명된 주니퍼 방화벽 구성) 이로 인해
AAAA
DNS 패킷이 삭제됩니다. - DNS 서버의 버그
긴 답변:
glibc의 기능을 사용 curl
하거나 사용하는 프로그램wget
getaddrinfo(), 두 DNS 레코드를 병렬로 조회하여 IPv4 및 IPv6 모두와 호환되도록 시도합니다. 두 레코드가 모두 수신될 때까지 결과를 반환하지 않습니다.그러한 행동과 관련된 몇 가지 문제) - strace
위의 내용을 설명합니다. curl -4
내부적 으로 레코드만 gethostbyname()
쿼리하는 것과 같이 IPv4가 강제되는 경우 .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.options
. 기본적으로 glibc는 버전 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 확인자 라이브러리를 사용하는 모든 앱을 다시 시작해야 합니다.
IPv6 연결 없이 BIND 서버를 사용하는 경우 IPv6을 비활성화 named
하고 루트 힌트 IPv6 주소에서 가져오는 것이 좋습니다. 분명히 여전히 AAAA 주소를 확인하려고 시도할 것입니다.
따라서 BIND 구성의 경우
/etc/default/bind9에서 IPv4 주소에 -4를 추가합니다.
OPTIONS="-4 -u bind"
에서 /etc/bind/db.root
AAAA 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