You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
사용자 클러스터에서 확인을 해보니 dns resolving시 5초 정도의 딜레이가 생기고 다시 리졸빙한 이력이 있다(tcpdump 내용)
A(ipv4), AAAA(ipv6)로 동시에 요청을 보내는데 응답이 안오는 경우가 간헐적으로 있고, 5초 정도의 딜레이가 생기고 다시 요청한다.
추가 확인
dns resolving시 pod → kube-dns → local dns resolver 로 dns search를 하는 과정에서 no such domain의 딜레이가 발생함
no such domain(NXDOMAIN)의 경우는 k8s의 dns resolving시 클러스터 dns의 kube-dns를 조회하는 과정에서 생긴 결과이고,
kube-dns에서 search domain이 결과가 없는 경우, 호스트의 resolv.conf를 이용하여 외부 nameserver search(클러스터 도메인 조회 과정)
하지만 A(ipv4), AAAA(ipv6) 리졸빙의 응답이 없는 경우가 발생함 - 이상한 부분...
# source pod
1. dns 질의를 요청하는 pod
2. 해당 pod에서 tcpdump를 보면 A, AAAA 두개의 요청 중 1개의 응답 미수신 (A의 응답은 있지만, AAAA의 응답이 없음 - 매번 같은 패턴 아님)
3. 5초 동안 기다리다 retry (dns query timeout이 5초)
4. 1의 요청 타임아웃
# dest pod(coredns)
1. A dns query 요청 수신
2. A dns query. 응답
-> AAAA dns query 없음
#AAAA(ipv6)가 응답이 없을 경우도 있고, A(ipv4)가 없을 경우도 발생함
확인 과정
물리 구간 확인
우선 네트워크 팀에서 물리 네트워크 및 가상 네트워크 구간의 tcpdump 확인
분석 결과의 내용을 확인해 보면 src에서는 a, aaaa로 dns 질의를 하는데(kube-dns 동작 방식)
둘중 하나만 요청의 결과가 오고(no such domain) 하나는 응답이 없어서 딜레이가 발생함
응답이 없는 경우 5초 되에 재시도 하고 응답 받음
dest인 core dns pod에서는 요청이 둘중 하나만 들어옴(core dns가 요청을 받기 이전에서 패킷이 유실됐을 가능성 확인)
재시도 요청은 정상적으로
src pod node → dest pod node간 스위치(tor)에서는 패킷 유실 없음
위와 같은 항목으로 dnat 규칙을 다시 통과할 필요가 없고, 관련 패킷의 대상 및 소스 주소를 그에 따라 수정할수 있다.
conntrack entry가 생성되면 그것은 처음에 confirm되지 않는다. 나중에 동일한 original 튜플이나 reply 튜플이 포함된 confirmed conntrack이 없는 경우 커널은 entry를 confirm한다.
A simplified flow of the conntrack creation and DNAT is shown below:
+---------------------------+ Create a conntrack for a given packet if
| | it does not exist; IP_CT_DIR_REPLY is
| 1. nf_conntrack_in | an invert of IP_CT_DIR_ORIGINAL tuple, so
| | src of the reply tuple is not changed yet.
+------------+--------------+
|
v
+---------------------------+
| |
| 2. ipt_do_table | Find a matching DNAT rule.
| |
+------------+--------------+
|
v
+---------------------------+
| | Update the reply tuples src part according
| 3. get_unique_tuple | to the DNAT rule in a way that it is not used
| | by any already confirmed conntrack.
+------------+--------------+
|
v
+---------------------------+
| | Mangle the packet destination port and address
| 4. nf_nat_packet | according to the reply tuple.
| |
+------------+--------------+
|
v
+----------------------------+
| | Confirm the conntrack if there is no confirmed
| 5. __nf_conntrack_confirm | conntrack with either the same original or
| | a reply tuple; increment insert_failed counter
+----------------------------+ and drop the packet if it exists.
conntrack race condition
문제는 두개의 udp(connection-less protocol)패킷이 같은 소켓으로 다른 스레드에서 동시에 보내질때 발생한다.
udp는 connection-less protocol 이고, linux connect(2-syscall)의 결과로 패킷을 보내지 않는다.
어떤 패킷도 nf_conntrack_in 단계에서 확인된 conntrack을 찾지 못한다. 두 패킷 모두에 대해 동일한 튜플을 가진 두개의 conntrack 항목이 생성됨 - (1)
하나의 패킷의 entry가 get_unique_tuple이 호출되기 전에 승인(먼저 승인된 패킷의 conntrack은 nat rule이 적용된 conntrack)된다. 그러면 또다른 패킷은 일반적으로 소스 포트가 변경되어 다른 reply 튜플을 얻는다. -(2)
맨 첫번째 경우와 마찬가지지만, 사로다른 엔드포인트가 선택된 두개의 룰이 ipt_do_table 단계에서 (하나는 nat rule이 적용된 conntrack, 다른 하나는 nat rule이 적용 안된 conntrack) -(3)
이 경쟁의 결과는 동일하다. - 하나의 패킷이 __nf_conntrack_confirm 단계에서 드롭된다.
dns case 에서 이 경우가 발생하는데 gnu c 라이브러리 그리고 musl libc 에서 A 그리고 AAAA dns lookup을 병렬로 진행한다. 하나의 udp 패킷은 이 경쟁중 커널에서 드롭된다. 그래서 클라이언트는 대부분 5초 의 타임아웃을 가지고 재시도 한다.
이건 k8s만이 아닌 모든 udp 패킷을 병렬로 보내는 리눅스 시스템에서 발생할수 있다.
심지어 nat룰이 없더라도 두번째 경쟁은 발생할수 있다. - get_unique_tuple의 호출을 활성화 하려면 nf_nat 커널 모듈을 로드 하는것만으로도 충분하다(nf_nat 커널 모둘이 로드 되면 get_unique_tuple이 호출된다. )
conntrack -S 를 이용하여 얻을수 있는 insert_failed 카운터는 문제가 발생했는지 여부를 확인하는 지표이다.