본문 바로가기

DevOps

Core DNS 성능개선 [ nodelocaldns / ndots & FQDN ]

먼저 CoreDNS 성능 개선을 위해 여러가지 테스트를 하기전에 
CoreDNS와 NodeLocalDNS가 무엇인지, 그리고 그 동작과정에 대해 먼저 알아보겠습니다.
이를 이해하기 위해서는 ndots과 search 도메인 , 그리고 절대 도메인과 상대 도메인에 대해서 이해해야 합니다.
NodeLocalDNS와 CoreDNS의 동작 과정

  • 쿠버네티스에서 기본적으로 사용되는 DNS는 CoreDNS입니다. NodeLocalDNS를 잘 활용한다면 CoreDNS까지 갈 필요도 없이 크리링 선에서 다 정리할수 있습니다.
목표
CoreDNS앞단에서 도메인 캐싱을 하여 도메인 쿼리 성능을 향상시켜 주는 NodeLocal DNS Cache에 대해 알아 보기 전에 resolv.conf 파일을 보면서 기본기를 다져봅시다.

  • A. search 도메인이란? 
    • search도메인에 대해 알기 위해 먼저 상대 도메인과 절대 도메인을 알아야 합니다.
    • 예를 들어서, 제가 nslookup에 kubernetes라고 조회를 하면 해당 search 도메인에 맨 앞에 있는 default.svc.cluster.local을 붙여서 조회를 합니다.
    • nslookup에서 리눅스 리졸버는 search도메인을 만들어 DNS요청을 합니다.
    • 하지만 리눅스 리졸버는 모든 search도메인을 요청하지 않습니다. DNS응답결과가 있을 때까지 즉, NOERROR응답을 받을 때까지 search도메인 DNS요청을 합니다. NXDOAMIN은 DNS응답이 없다는 뜻입니다.
  • B. nameserver IP (10.200.1.10) 는 kube-dns 서비스의 IP이다.
  • C. ndots은 뭘까?
    • ndots는 "dot"이라는 DNS에서 사용되는 구분자인 점(.)을 의미하며, 이 숫자는 도메인 이름에 포함된 점(.)의 개수를 나타냅니다. 즉, options ndots:5는 DNS 질의에서 도메인 이름에 점(.)이 5개 포함된 경우까지 검색을 시도한다는 것을 의미합니다.
    •  "foo.bar.svc.cluster.local" 다음과 같이 전체 도메인에 (.) 4개 포함된 경우는 nodts:4 인것이죠.
    • 아니 그럼? 5개 점을 포함한 경우도 있을까요?  이걸 FQDN 이라고 부릅니다. 
      • 절대 도메인과 상대 도메인의 차이를 알아야 합니다.
      • google.com은 상대도메인 - 상대 도메인인 경우엔 ndots의 점 개수를 보고 설정값보다 점 개수가 적으면 search 도메인으로 일일이 정해진 순서대로 모든 search 도메인을 붙여서 조회합니다. 그래서 불필요한 DNS 쿼리 요청이 생김
      • google.com.은 절대 도메인 이기 때문에 search 도메인을 붙이지 않고 한번에 바로 조회합니다. 그래서 불필요한 DNS 요청이 없는것이죠.

Summary

Search 도메인과 ndots 옵션을 통해 상대 도메인에 search 도메인을 붙여 차례로 DNS 쿼리를 처리한다.
# 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
  • 해당 정보들을 보면 유추할수 있는게 default.svc.cluster.local 즉, 요청이 있었던 pod가 소속되어있는 네임스페이스에서 가장 먼저 조회를 한다는 것입니다. 
  • 그 다음 우선순위로는 svc.cluster.local / cluster.local / ap-northxxx 등이 있습니다. 기본적으로 pod의 resolv.conf를 변경하는 방법은 다음과 같습니다. pod 배포 할때 dnsConfig를 수정하는것이죠.
apiVersion: v1
kind: Pod
metadata:
  namespace: default
  name: dns-example
spec:
  containers:
    - name: test
      image: nginx
  dnsPolicy: "None"
  dnsConfig:
    nameservers:
      - 1.2.3.4
    searches:
      - ns1.svc.cluster-domain.example
      - my.dns.search.suffix
    options:
      - name: ndots
        value: "2"
      - name: edns0

 

다음과 같이 배포시에 /etc/resolv.conf 파일이 변경되는것을 확인 할수 있습니다. 그렇다면 이런 방법으로 pod의 ndots 옵션을 조금 바꿔서 코드의 수정없이 쿼리 성능을 개선할수 있습니다. 뒤에 그런 방법을 소개해보겠습니다. 

  • nodelocalDNS
  • ndots
  • FQDN 

 


✅   도전과제 1                                                                                                                                          

[도전과제 1]  NodeLocal DNS Cache 설정으로 클러스터의 DNS 성능 향상 (https://kim-dragon.tistory.com/281 참고)

  •  CoreDNS앞단에서 도메인 캐싱을 하여 도메인 쿼리 성능을 향상시켜 주는 NodeLocal DNS Cache
    • Skipping iptables DNAT and connection tracking will help reduce conntrack races and avoid UDP DNS entries filling up conntrack table.
    • reducing the number of queries for the kube-dns service.
  •  DaemonSet으로 DNS캐싱 에이전트를 실행
  • Local DNS Cache가 DNS 정보를 갖고 있으면 Client에게 바로 응답 할 수 있기 때문에 성능 향상

 

    •  wget 으로 node local dns cache를 설치합니다. 
wget https://github.com/kubernetes/kubernetes/raw/master/cluster/addons/dns/nodelocaldns/nodelocaldns.yaml

# 환경변수 설정
kubedns=`kubectl get svc kube-dns -n kube-system -o jsonpath={.spec.clusterIP}`
domain=cluster.local
localdns=169.254.20.10

# __PILLAR__DNS__SERVER__ : kubectl get svc -n kube-system | grep kube-dns | awk '{ print $3 }'
# __PILLAR__DNS__DOMAIN__ : 클러스터 도메인을 나타내며 기본값은 cluster.local
# __PILLAR__LOCAL__DNS__ : NodeLocal DNSCache에 대해 선택된 로컬 수신 IP 주소 (169.254.20.10으로 고정입니다.)


# manifest 변수 수정 
sed -i "s/__PILLAR__LOCAL__DNS__/$localdns/g; s/__PILLAR__DNS__DOMAIN__/$domain/g; s/__PILLAR__DNS__SERVER__/$kubedns/g" nodelocaldns.yaml


### MAC 일경우
sed -i '' "s|__PILLAR__LOCAL__DNS__|$localdns|g; s|__PILLAR__DNS__DOMAIN__|$domain|g; s|__PILLAR__DNS__SERVER__|$kubedns|g" nodelocaldns.yaml
  • 배포 후 확인
kubectl create -f nodelocaldns.yaml

# kubectl get pods -n kube-system | grep node-local
node-local-dns-2fncz                       1/1     Running   0          98s
node-local-dns-cb422                       1/1     Running   0          98s
node-local-dns-f22bq                       1/1     Running   0          98s
  • 테스트 전 로그 설정
# kubectl -n kube-system edit configmap node-local-dns
...
apiVersion: v1
data:
Corefile: |
cluster.local:53 {
  log
  errors
  cache {
    success 9984 30
    denial 9984 5
  }
...

# nodelocaldns daemonset 배포
kubectl rollout restart daemonset -n kube-system node-local-dns


# 테스트 pod 배포
kubectl apply -f https://k8s.io/examples/admin/dns/dnsutils.yaml
  • 테스트 pod에 접속해서 nslookup 실행
# k exec -it dnsutils -- /bin/sh

# nslookup
> kubernetes.default
Server:		10.100.0.10
Address:	10.100.0.10#53

Name:	kubernetes.default.svc.cluster.local
Address: 10.100.0.1
> google.com
Server:		10.100.0.10
Address:	10.100.0.10#53

Non-authoritative answer:
Name:	google.com
Address: 142.250.206.238
  • nodelocaldns의 로그 확인
kubectl logs --namespace=kube-system -l k8s-app=node-local-dns -f

# 로그를 확인 해보면 클러스터 내부에 있는 서비스 예를 들면 default 네임스페이스의 kubernetes 같이
# DNS 쿼리를 외부로 보내거나 외부 DNS 서버에서 응답을 기다릴 필요가 없으므로 응답 시간이 훨씬 빠릅니다

[INFO] Added back nodelocaldns rule - {filter INPUT [-p tcp -d 10.200.1.10 --dport 53 -j ACCEPT -m comment --comment NodeLocal DNS Cache: allow DNS traffic]}
[INFO] Added back nodelocaldns rule - {filter INPUT [-p udp -d 10.200.1.10 --dport 53 -j ACCEPT -m comment --comment NodeLocal DNS Cache: allow DNS traffic]}
[INFO] Added back nodelocaldns rule - {raw OUTPUT [-p tcp -s 10.200.1.10 --sport 53 -j NOTRACK -m comment --comment NodeLocal DNS Cache: skip conntrack]}
[INFO] Added back nodelocaldns rule - {raw OUTPUT [-p udp -s 10.200.1.10 --sport 53 -j NOTRACK -m comment --comment NodeLocal DNS Cache: skip conntrack]}

# conntrack을 우회하도록 설정한 iptables 규칙이 추가되었음을 나타냅니다.
# conntrack의 비활성화로 성능을 최적화합니다.

# 1. Skipping iptables DNAT and connection tracking will help reduce conntrack races and avoid UDP DNS entries filling up conntrack table.
# 2. reducing the number of queries for the kube-dns service.

 

 

  • 왼쪽은 nodelocalDNS 에 조회된 로그 입니다.
  • 오른쪽은 coreDNS에 조회된 로그입니다. ( 기본적으로 30초간 캐쉬에 저장해놓고 그 동안에 요청이 들어올때는 캐쉬에서 처리 하기 때문에 요청이 들어오지 않는 모습)

 

ndots:1로 변경 한다면? 

 

 

[도전과제 2 ] ndots 설정을 통해 FQDN 조회와 비슷한 성능을 낼수 있다.

 

기본적으로 nslookup과 어플리케이션 또는 curl에서 coreDNS를 요청하는 방식이 상이 합니다.

  • nslookup은 시스템 리졸버를 통해 상대 도메인을 절대 도메인으로 변경합니다.
  •  pod는 리눅스의 커널 리졸버 설정을 따릅니다.
  •  예를 들어 점이 없는 google.com을 요청하면 자동으로 점이 붙습니다.

 

하지만 어플리케이션/curl 에서는 ndots 을 1로 수정하는 것 만으로도 FQDN을 조회하는 것처럼 1번에 조회가 가능하다. 

cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: netshoot
spec:
  terminationGracePeriodSeconds: 0
  containers:
  - name: netshoot
    image: nicolaka/netshoot
    command:
      - sleep
      - "infinity"
  dnsPolicy: "None"
  dnsConfig:
    nameservers:
      - 10.200.1.10
    searches:
      - default.svc.cluster.local
      - svc.cluster.local
      - cluster.local
      - ap-northeast-2.compute.internal
    options:
      - name: ndots
        value: "1"
EOF

 

이와 같이 ndots:1 로 변경 했습니다.

이와 같이 ndots:1 로 변경 했습니다.  --- curl 요청을 해보겠습니다.

이와 같이 ndots:1 로 변경 했습니다.  --- curl 요청을 해보겠습니다. ---- 다음과 같이 한번에 조회하는것을 알수 있습니다.

 

별첨으로 ndots:5 일때의 쿼리 요청 로그를 보겠습니다.

최악입니다. 5번의 조회를 할 뿐만 아니라, AAAA 쿼리 (ipv6)까지 합치면 총 10번의 조회가 필요합니다.

절대 도메인 (FQDN) 으로 어플리케이션의 코드를 변경하여 사용한다면? 

 

FQDN 사용 가능? 게이트웨이 코드 변경 이 가능한경우 사용할 법 하다.

cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: netshoot
spec:
  terminationGracePeriodSeconds: 0
  containers:
  - name: netshoot
    image: nicolaka/netshoot
    command:
      - sleep
      - "infinity"
  dnsPolicy: "None"
  dnsConfig:
    nameservers:
      - 10.200.1.10
    searches:
      - default.svc.cluster.local
      - svc.cluster.local
      - cluster.local
      - ap-northeast-2.compute.internal
    options:
      - name: ndots
        value: "5"
EOF

 

FQDN --- 게이트웨이 코드 변경 이 가능한경우 사용할 법 하다.

 

FQDN --- 게이트웨이 코드 변경 이 가능한경우 사용할 법 하다. ---- 1번의 조회만 필요하기 때문에 성능적 개선이 될수있다.