가시다님의 Kubernetes Advanced Networking Study에 참여하게 되어, 스터디 때 다룬 주제를 정리하려고 합니다.
6주차는 Ingress & Gateway API + CoreDNS를 주제로 진행되었습니다.
이번 글에서는 CoreDNS와 NodeLodalDNS에 대해 다루고 있습니다.
CoreDNS
CoreDNS는 Kubernetes 클러스터 내부 DNS 서버입니다.
클러스터에서 Pod와 Service가 생성되면 자동으로 이에 대한 DNS 레코드가 생성됩니다.
Corefile
CoreDns deployment에는 아래 configmap을 /etc/coredns에 마운트하여 사용하고 있습니다.
apiVersion: v1
data:
Corefile: |
.:53 {
errors
health
ready
kubernetes cluster.local in-addr.arpa ip6.arpa {
pods insecure
fallthrough in-addr.arpa ip6.arpa
}
hosts /etc/coredns/NodeHosts {
ttl 60
reload 15s
fallthrough
}
prometheus :9153
forward . /etc/resolv.conf
cache 30
loop
reload
loadbalance
import /etc/coredns/custom/*.override
}
import /etc/coredns/custom/*.server
NodeHosts: |
192.168.10.10 k3s-s
192.168.10.101 k3s-w1
192.168.10.102 k3s-w2
192.168.10.103 k3s-w3
kind: ConfigMap
metadata:
name: coredns
namespace: kube-system
Corefile은 CoreDNS 설정 파일이며, 기본적으로 아래와 같은 형태를 갖습니다.
ZONE:[PORT] {
[PLUGIN]...
}
사용 가능한 플러그인 목록은 공식 문서에 있습니다.
현재 클러스터(k3s로 구성)의 Corefile 내용과 몇가지 플러그인을 살펴보겠습니다.
.:53 { ## 모든 도메인(zone)의 53(DNS 기본 포토)번 포트를 처리
errors ## error logging 활성화
health ## health check endpoint 활성화
ready ## readiness check 활성화
kubernetes cluster.local in-addr.arpa ip6.arpa { ## k8s 클러스터에서 zone 데이터를 가져옴
pods insecure ## POD-MODE 설정, 항상 A 레코드를 IP 주소와 함께 반환
fallthrough in-addr.arpa ip6.arpa ## DNS 쿼리 결과가 NXDOMAIN일 때 in-addr.arpa, ip6.arpa에 대해서만 fallthrough 적용
}
hosts /etc/coredns/NodeHosts { ## hosts 파일 위치 지정
ttl 60 ## dns TTL
reload 15s ## hosts 파일 변경 반영을 위한 리로드 주기
fallthrough ## 해당 플러그인이 권한을 갖는 모든 zone에 대해 fallthrough
}
prometheus :9153 ## prometheus 메트릭 활성화
forward . /etc/resolv.conf ## 상위 resolver로 DNS 메세지 프록시
cache 30 ## frontend cache 활성화
loop ## 간단한 forwarding 루프를 감지하고 프로세스 중지
reload ## Corefile의 변경 사항을 자동 reload
loadbalance ## A, AAAA, MX 레코드의 응답을 무작위로 섞어서 여러 IP간 분산
import /etc/coredns/custom/*.override ## Corefile에 포함할 파일 지정
}
kubernetes 플러그인
참고) https://github.com/coredns/coredns/tree/master/plugin/kubernetes
kubernetes [ZONES...] {
endpoint URL
tls CERT KEY CACERT
kubeconfig KUBECONFIG [CONTEXT]
namespaces NAMESPACE...
labels EXPRESSION
pods POD-MODE
endpoint_pod_names
ttl TTL
noendpoints
fallthrough [ZONES...]
ignore empty_service
}
클러스터 내부 도메인(cluster.local)에 대한 질의을 처리하기 위해 사용합니다.
pods : 파드 A record를 처리하는 방법 지정
- disabled : Pod에 대한 DNS 요청을 처리하지 않으며, 항상 NXDOMAIN(도메인이 존재하지 않음) 응답을 반환
- insecure : A 레코드를 IP와 함께 항상 반환
- verified : 요청된 IP(resolve된 IP)와 일치하는 Pod가 동일한 네임스페이스에 존재하는 경우에만 A 레코드를 반환
fallthrough : DNS 쿼리 결과가 NXDOMAIN일 때의 동작을 결정하며, 설정 시 쿼리가 다른 플러그인으로 전달
hosts 플러그인
참고) https://github.com/coredns/coredns/tree/master/plugin/hosts
hosts [FILE [ZONES...]] {
[INLINE]
ttl SECONDS
no_reverse
reload DURATION
fallthrough [ZONES...]
}
/etc/hosts 형식의 파일에서 zone 데이터를 제공하며, 파일을 지정하지 않으면 기본적으로 /etc/hosts 참조
forward 플러그인
참고) https://github.com/coredns/coredns/tree/master/plugin/forward
forward FROM TO... {
except IGNORED_NAMES...
force_tcp
prefer_udp
expire DURATION
max_fails INTEGER
tls CERT KEY CA
tls_servername NAME
policy random|round_robin|sequential
health_check DURATION [no_rec] [domain FQDN]
max_concurrent MAX
next RCODE_1 [RCODE_2] [RCODE_3...]
}
업스트림 DNS 서버에 DNS 메세지 프록시하는 역할
- FROM : 요청을 전달하기 위해 일치해야하는 도메인 설정
- TO : 전달할 대상 엔드포인트
forward . /etc/resolv.conf 로 설정시 모든 도메인에 대한 DNS 질의를 /etc/resolv.conf 파일에 정의된 업스트림 DNS 서버로 전달하도록 지정하며, 이를 통해 kubernetes 외부 도메인에 대한 질의를 처리합니다.
참고로, coredns 파드의 /etc/resolv.conf는 아래와 같이 설정되어 있습니다.
VPC 대역이 192.168.0.0/16으로 VPC의 nameserver인 192.168.0.2로 설정된 것을 볼 수 있습니다.
coredns-7b98449c4-tlmtw ~ cat /etc/resolv.conf
search ap-northeast-2.compute.internal
nameserver 192.168.0.2
이제 파드를 생성하여 해당 파드의 설정과 질의 과정을 살펴보겠습니다.
파드 생성 후 파드의 /etc/resolv.conf 파일 설정을 확인해보겠습니다.
webpod1 ~ cat /etc/resolv.conf
search default.svc.cluster.local svc.cluster.local cluster.local ap-northeast-2.compute.internal
nameserver 10.10.200.10
options ndots:5
search : 나열된 도메인 목록을 차례대로 붙여가며 추가로 DNS 서버를 호출
nameserver : 질의를 전달할 서버, CoreDNS Service의 Cluster IP
options ndots : FQDN으로 처리할 도메인에 포함될 '.'의 최소 개수, default 5
노드 및 파드 정보
(⎈|default:N/A) root@k3s-s:~# k get no -owide
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
k3s-s Ready control-plane,master 102m v1.30.5+k3s1 192.168.10.10 <none> Ubuntu 22.04.5 LTS 6.8.0-1015-aws containerd://1.7.21-k3s2
k3s-w1 Ready <none> 102m v1.30.5+k3s1 192.168.10.101 <none> Ubuntu 22.04.5 LTS 6.8.0-1015-aws containerd://1.7.21-k3s2
k3s-w2 Ready <none> 102m v1.30.5+k3s1 192.168.10.102 <none> Ubuntu 22.04.5 LTS 6.8.0-1015-aws containerd://1.7.21-k3s2
k3s-w3 Ready <none> 102m v1.30.5+k3s1 192.168.10.103 <none> Ubuntu 22.04.5 LTS 6.8.0-1015-aws containerd://1.7.21-k3s2
(⎈|default:N/A) root@k3s-s:~# k get po -owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
webpod1 1/1 Running 0 30m 172.16.3.2 k3s-w1 <none> <none>
webpod2 1/1 Running 0 30m 172.16.1.3 k3s-w2 <none> <none>
webpod3 1/1 Running 0 30m 172.16.2.3 k3s-w3 <none> <none>
(⎈|default:N/A) root@k3s-s:~# k get po -n kube-sysetm -owide
No resources found in kube-sysetm namespace.
(⎈|default:N/A) root@k3s-s:~# k get po -n kube-system -owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
coredns-7b98449c4-tlmtw 1/1 Running 0 102m 172.16.1.2 k3s-w2 <none> <none>
local-path-provisioner-6795b5f9d8-8qmxr 1/1 Running 0 102m 172.16.0.2 k3s-s <none> <none>
metrics-server-cdcc87586-crzps 1/1 Running 0 102m 172.16.2.2 k3s-w3 <none> <none>
(⎈|default:N/A) root@k3s-s:~# k get svc -n kube-system
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kube-dns ClusterIP 10.10.200.10 <none> 53/UDP,53/TCP,9153/TCP 122m
metrics-server ClusterIP 10.10.200.244 <none> 443/TCP 122m
(⎈|default:N/A) root@k3s-s:~# k get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.10.200.1 <none> 443/TCP 123m
svc-clusterip ClusterIP 10.10.200.207 <none> 9000/TCP 51m
외부 도메인 질의
webpod1에서 nslookup 실행 시 coredns 파드에 요청이 전달되고, coredns 파드에서는 /etc/resolv.conf에 설정된 nameserver 192.168.0.2에 질의를 하는 것을 확인할 수 있습니다. 해당 통신은 coredns 파드가 배포된 노드에서도 확인할 수 있습니다.
추가로 google.com와 google.com.에 대해 질의를 비교해보겠습니다.
google.com 질의 시 type = A 레코드에 대해 5회의 질의를 하는 것을 확인할 수 있습니다.
nslookup -debug google.com
Server: 10.10.200.10
Address: 10.10.200.10#53
------------
QUESTIONS:
google.com.default.svc.cluster.local, type = A, class = IN
ANSWERS:
AUTHORITY RECORDS:
-> cluster.local
--
QUESTIONS:
google.com.svc.cluster.local, type = A, class = IN
ANSWERS:
AUTHORITY RECORDS:
-> cluster.local
--
QUESTIONS:
google.com.cluster.local, type = A, class = IN
ANSWERS:
AUTHORITY RECORDS:
-> cluster.local
--
QUESTIONS:
google.com.ap-northeast-2.compute.internal, type = A, class = IN
ANSWERS:
AUTHORITY RECORDS:
-> ap-northeast-2.compute.internal
--
QUESTIONS:
google.com, type = A, class = IN
ANSWERS:
-> google.com
internet address = 142.250.206.238
--
QUESTIONS:
google.com, type = AAAA, class = IN
ANSWERS:
-> google.com
has AAAA address 2404:6800:400a:80e::200e
google.com. 질의 시 type = A 레코드에 대해 1회의 질의를 하는 것을 확인할 수 있습니다.
webpod1 ~ nslookup -debug google.com.
Server: 10.10.200.10
Address: 10.10.200.10#53
------------
QUESTIONS:
google.com, type = A, class = IN
ANSWERS:
-> google.com
internet address = 142.250.206.238
--
QUESTIONS:
google.com, type = AAAA, class = IN
ANSWERS:
-> google.com
has AAAA address 2404:6800:400a:80e::200e
이는 FQDN으로 간주하는 도메인이냐 아니냐에 따라 질의 횟수가 다르다고 볼 수 있으며, 맨 뒤에 '.'이 있는 도메인은 FQDN으로 인식하여 한번의 질의를 하지만, 그렇지 않은 도메인의 경우 현재 ndots 설정이 5로 되어있기 때문에 '.'이 5개보다 적다면 search에 포함된 도메인들을 차례로 추가하며 질의를 합니다.
질의 횟수가 많으면 CoreDNS에 부하가 생길 수 있고, 더 빠르게 DNS 질의 결과를 받기 위해서는 모든 도메인 뒤에 '.'를 포함하거나, ndots를 1로 설정하는 것이 좋습니다.
해당 설정은 파드의 spec에서 변경이 가능합니다.
내부 도메인 질의
webpod1에서 nslookup 실행 시 coredns 파드에 요청이 전달됩니다. 외부 도메인 질의와 다르게 VPC nameserver에 별도로 질의를 하지 않습니다.
Pod dnsPolicy
파드 개별로 DNS 정책을 설정할 수 있고 여러 옵션을 살펴보겠습니다.
참고) https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/#pod-s-dns-policy
Default : 노드 설정(ex. /etc/resolv.conf)에 정의된 DNS 서버를 사용하여 DNS 질의하며, CoreDNS, NodeLocal DNS 모두 해당 설정으로 사용
ClusterFirst(기본 옵션) : 클러스터 도메인 접미사(cluster.local 등)와 일치하지 않는 DNS 질의는 업스트림 DNS 서버로 전달하며, dnsPolicy를 지정하지 않았을 때 사용하는 기본 옵션
ClusterFirstWithHostNet : hostNetwork로 실행되는 파드에 사용하며, hostNetwork 파드에서 ClusterFirst를 사용하는 경우 Default 정책을 사용
None : 파드가 클러스터 내 DNS 설정을 무시하고 모든 설정은 파드의 dnsConfig 필드를 통해 제공
NodeLocal DNSCache
NodeLocal DNS는 CoreDNS를 보조하는 기능으로, 모든 노드에 배포(DaemonSet)되어 local cache를 사용합니다.
도메인 질의 발생 시 1차적으로 NodeLocal DNS에서 응답하며 해당 도메인이 cache에 있다면 바로 반환하고, cache에 없다면 업스트림 DNS 서버(CoreDNS)에 요청을 전달합니다.
참고) NodeLocal DNSCache의 이점
NodeLocal DNS 설치
NodeLocal DNS를 설치하고 DNS 질의의 흐름을 살펴보겠습니다.
참고)
- https://kubernetes.io/docs/tasks/administer-cluster/nodelocaldns/#configuration
- https://github.com/kubernetes/kubernetes/tree/master/cluster/addons/dns/nodelocaldns
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' ## default 값
localdns='169.254.20.10' ## default 값
## iptables 모드 사용 중으로 아래 명령어 수행
sed -i "s/__PILLAR__LOCAL__DNS__/$localdns/g; s/__PILLAR__DNS__DOMAIN__/$domain/g; s/__PILLAR__DNS__SERVER__/$kubedns/g" nodelocaldns.yaml
## nodelocaldns 설치
kubectl apply -f nodelocaldns.yaml
## nodelocaldns 설치 확인
(⎈|default:N/A) root@k3s-s:~# k get po -n kube-system -owide | grep dns
coredns-7b98449c4-tlmtw 1/1 Running 0 3h15m 172.16.1.2 k3s-w2 <none> <none>
node-local-dns-5c958 1/1 Running 0 14m 192.168.10.101 k3s-w1 <none> <none>
node-local-dns-g2ss2 1/1 Running 0 14m 192.168.10.103 k3s-w3 <none> <none>
node-local-dns-ml8zp 1/1 Running 0 14m 192.168.10.10 k3s-s <none> <none>
node-local-dns-q4qnc 1/1 Running 0 14m 192.168.10.102 k3s-w2 <none> <none>
node-local-dns 파드의 로그를 보면, CoreDNS ClusterIP와 local dns IP에 대한 iptables 규칙을 추가하는 것을 볼 수 있습니다.
이를 통해 기존 CoreDNS Service에 대한 요청을 NodeLocal DNS에서 처리하도록 합니다.
클러스터 외부 도메인 질의
webpod에서 google.com.으로 질의를 하면, CoreDNS 파드를 거치지 않고 node-local-dns 파드에서 처리되는 것을 확인할 수 있습니다.
cache에 없는 경우의 통신 흐름을 확인하기 위해 의도적으로 존재하지 않는 도메인을 질의해보겠습니다.
NodeLocal DNS의 경우 upstream DNS 서버로 요청 시 TCP를 사용하기 때문에 tcpdump 옵션을 수정했습니다.
google.com.xxx에 대해 질의 시 NodeLocal DNS를 통해 CoreDns에 요청하는 것을 확인할 수 있습니다.
클러스터 내부 도메인 질의의 경우에도 cache에 있는 경우, 없는 경우에 따라 CoreDNS에 요청 전달 여부는 동일하게 동작하여 따로 tcpdump를 남기지 않았습니다.
큰 규모의 클러스터에서는 DNS 질의가 다량 발생하므로 그에 비례하게 CoreDNS 파드 수와 스펙을 증가시킵니다.
서비스와 파드 수에 따라 CoreDNS 리소스 사용량이 증가할 수 있으므로 모니터링이 필요합니다.
이때 NodeLocal DNS를 사용하면 해당 파드가 배포된 노드의 cache에 의해서 1차적으로 DNS 쿼리가 수행되기 때문에 CoreDNS의 부하를 줄일 수 있습니다.
참고 자료)
'Kubernetes > Network Study' 카테고리의 다른 글
Cilium CNI와 eBPF (4) | 2024.10.26 |
---|---|
istio 이해하기 - Envoy proxy (0) | 2024.10.19 |
kube-proxy IPVS 모드 (2) | 2024.10.18 |
kube-proxy 모니터링 환경 구성 실습(serviceMonitor, podMonitor) (3) | 2024.10.16 |
Kubernetes Service 분석 - ClusterIP, NodePort (3) | 2024.09.28 |