k8s dns ndots

xlwdn·2023년 8월 14일
1

ndots?


ndots란 FDQN으로 인지하기 시작하는 .(dot)의 개수이다.

$ cat /etc/resolv.conf
search default.svc.cluster.local svc.cluster.local cluster.local ap-northeast-2.compute.internal
nameserver 10.100.0.10
options ndots:5

위와 같이 조회할 수 있으며, ndots:5에서 5가 n의 역할을 하고 기본적으로 n의 기본값은 resolve.conf man 문서에서 다음과 같이 1이라 이야기한다.

ndots:n

sets a threshold for the number of dots which must appear in a name before an initial absolute query will be made. The default for n is 1, meaning that if there are any dots in a name, the name will be tried first as an absolute name before any search list elements are appended to it.

다만 k8s에서는 ndots의 기본값을 5로 지정해준다. 이는 즉, 질의하는 domain name이 5개 미만의 dots 포함 시 syscall이 순차적으로 모든 local search domain(위 /etc/resolv.conf에서는 default.svc.cluster.local, svc.cluster.local, cluster.local, ap-northeast-2.compute.internal) 을 먼저 조회하고 만약 실패 시 FQDN으로 처리한다는 뜻이다.

성능에 미치는 영향

application이 엄청난 외부 트래픽을 처리하게 되면, 각 TCP 연결은 정확한 쿼리 조회를 위해선 위와 같은 경우 4개의 local search domain에 대한 쿼리와 마지막 FQDN에 대한 쿼리까지 총 5개의 쿼리가 발행될 것이다.

실습

k8s에서 tcpdump pod를 띄우고, 해당 파드에 접속하여 dns query를 덤프떠서 와이어샤크로 확인한다.

$ k run tcpdump --image=corfr/tcpdump
$ kgp -o wide
NAME                           READY   STATUS    RESTARTS   AGE     IP               NODE                                                NOMINATED NODE   READINESS GATES
busybox                        1/1     Running   0          7h46m   192.168.55.77    ip-192-168-54-99.ap-northeast-2.compute.internal    <none>           <none>
nginx-hello-5594495875-j8q4k   1/1     Running   0          7h21m   192.168.28.193   ip-192-168-25-104.ap-northeast-2.compute.internal   <none>           <none>
nginx-hello-5594495875-t2wbl   1/1     Running   0          7h21m   192.168.73.121   ip-192-168-80-1.ap-northeast-2.compute.internal     <none>           <none>
tcpdump                        1/1     Running   0          8m2s    192.168.79.85    ip-192-168-80-1.ap-northeast-2.compute.internal     <none>           <none>
$ k exec -it tcpdump -- sh
/ # tcpdump-port-53-2023-08-15-04-00.pcap
// 다른 터미널을 연다.
/ # nslookup google.com
/ # nslookup google.com.
// 이후 다시 로컬로 돌아온다.
$ k cp default/tcpdump:tcpdump-port-53-2023-08-15-04-00.pcap tcpdump-port-53-2023-08-15-04-00.pcap

이후 해당 파일을 와이어샤크에서 열어보면

위 내용을 자세히 살펴보면 1 ~ 24번 패킷까지는 모두 google.com을 조회하는 패킷이다.

위 패킷들에서 확인할 수 있듯, google.com.default.svc.cluster.local, google.com.svc.cluster.local, google.com.cluster.local, google.com.ap-northeast-2.compute.internal 까지 모두 조회 후 no such name A/AAAA {record name} 메시지가 나온 이후에야 A 타입 google.com에 대해 조회한다.

하지만 두 번째 google.com.에 대한 조회는 바로 google.com에 대한 A/AAAA 레코드를 조회한다. 이는 도메인 이름에 대해 마지막에 .을 붙어주어 FDQN을 명시하였기 때문인데, 이를 통해 외부로 나아가고자 하는 쿼리에 대한 낭비를 제거할 수 있다.

해결법

위와 같이 FDQN에 대한 불필요한 쿼리 조회를 통한 리소스 낭비를 막기 위해서는 두 가지 방법이 존재한다.

  • 위 예시처럼 .을 붙여 FDQN을 명시해준다.
    • 다만 해당 방법은 개발자 측에 양해를 구해야한다.
  • ndots 값을 줄인다.

ndots 값을 줄이는 경우 프로그램 측의 수정 없이도 성공적으로 낭비를 줄일 수 있다. 대개의 경우, 같은 namespace 간에 dns를 쿼리하는 경우가 많기에 ndots를 2 또는 관리하는 서비스에 맞게 수정하여 manifest에 등록하면 된다.

$ kgp tcpdump -o yaml | k neat > tcpdump-pod.yaml
$ vi tcpdump-pod.yaml ^C
apiVersion: v1
kind: Pod
metadata:
  labels:
    run: tcpdump
  name: tcpdump
  namespace: default
spec:
  containers:
  - image: docker.io/corfr/tcpdump
    name: tcpdump
  dnsConfig: # 해당 부분이다.
    options:
      - name: ndots
        value: "2" # value로 2를 입력한다.
  preemptionPolicy: PreemptLowerPriority
  priority: 0
  serviceAccountName: default
  tolerations:
  - effect: NoExecute
    key: node.kubernetes.io/not-ready
    operator: Exists
    tolerationSeconds: 300
  - effect: NoExecute
    key: node.kubernetes.io/unreachable
    operator: Exists
    tolerationSeconds: 300

이후 파드를 해당 manifest로 재시작하고 ndots를 2로 맞추었으니 www.google.comwww.google.com.을 조회해보면

패킷의 개수도 16개로 줄어들었으며, .을 추가하여 FQDN을 명시한 쿼리와 그냥 쿼리를 날렸을 때가 전송하는 패킷의 개수가 동일하다.

0개의 댓글