먼저 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번의 조회만 필요하기 때문에 성능적 개선이 될수있다.
'DevOps' 카테고리의 다른 글
[AWS EKS] EFS Controller와 S3 CSI Driver (0) | 2024.03.23 |
---|---|
[AWS EKS] EKS CSi driver와 NFS provisioner (0) | 2024.03.21 |
[AWS EKS] VPC CNI와 Loadbalancer (0) | 2024.03.09 |
[AWS EKS] EKS 중요한 3가지 특징 (4) | 2023.12.03 |
[AWS EKS] EKS 중요한건 꺽이지 않는 안정성 (0) | 2023.12.02 |