CoreDNS
5주차 교육을 진행하였습니다. 이번 시간은 DNS에 대하여 미리 학습해야 진행이 수월합니다. MetalLB까지 교육을 진행하였지만, 역량 부족으로 CoreDNS만 정리 하였습니다.
DNS 참고 자료
- K8S Docs - Service DNS
- CoreDNS 소개 정리 - 링크
- MetalLB - 공홈 Github
- [생활코딩] DNS - 링크
- DNS 옵션 - 링크 & Root - 링크
- [ssup2] CoreDNS NodeLocal DNSCache SLB
실습 환경 구성
실습 환경은 K8S v1.22.6 , 노드 OS(Ubuntu 20.04.3) , CNI(Calico v3.21, VXLAN mode) , IPTABLES proxy mode 또한 실습 편의를 위해 ‘모든 VM’ 에 kubectl, calicoctl 설정을 추가하였습니다.
# K8S 배포에 사용할 폴더(디렉터리)를 생성 후 사용해주세요
# vagrant 파일 다운로드
$ curl -O https://raw.githubusercontent.com/gasida/KANS/main/5/Vagrantfile
# 배포
$ vagrant up
# 배포 확인 : k8s-rtr, k8s-m, k8s-w1, k8s-w2, k8s-pc
$ vagrant status
# 마스터노드 접속
$ vagrant ssh k8s-m
# 마스터노드에서 Calico 확인 (VXLAN mode)
$ calicoctl get ippool -o wide
CoreDNS
- 클러스터 안에서 서비스와 파드가 도메인을 통해 접근이 가능하게 DNS 서비스를 제공, 그외 외부 도메인 Query 처리
- 디플로이먼트(기본 파드 2개)로 배포되고, 서비스(Cluster IP)를 제공
- 기본 파드는 도메인 Query 를 CoreDNS 서비스(Cluster IP)에 보내서 응답을 받음
# (옵션) 터미널1
$ watch -d 'kubectl get pod,svc,ep -n kube-system -l k8s-app=kube-dns -o wide'
# 혹은
$ watch -d 'kubectl get pods,svc,ep -o wide;echo ; kubectl get pod,svc,ep -n kube-system -l k8s-app=kube-dns -o wide'
# 디플로이먼트(파드) 확인
$ kubectl get deployments.apps -n kube-system
NAME READY UP-TO-DATE AVAILABLE AGE
coredns 2/2 2 2 30h
$ kubectl get pod -n kube-system -o wide -l k8s-app=kube-dns
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
coredns-558bd4d5db-629rs 1/1 Running 0 6m19s 172.16.29.23 k8s-m <none> <none>
coredns-558bd4d5db-z92g9 1/1 Running 1 3h50m 172.16.29.21 k8s-m <none> <none>
# CoreDNS 서비스(Cluster IP) 확인
$ kubectl get svc -n kube-system kube-dns
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kube-dns ClusterIP **10.96.0.10** <none> 53/UDP,53/TCP,9153/TCP 30h
# coredns 컨피그맵 확인
$ kubectl describe configmaps -n kube-system coredns
Name: coredns
Namespace: kube-system
Labels: <none>
Annotations: <none>
Data
====
Corefile:
----
.:53 { # {플러그인 명시} - [링크](https://github.com/coredns/coredns/tree/master/plugin)
errors # 표준 출력으로 에러 로그 남김
health { # http://<Pod-IP>:8080/health 으로 CoreDNS 헬스 체크 가능
lameduck 5s
}
ready # http://<Pod-IP>8181/ready 으로 CoreDNS 준비 체크 가능
kubernetes cluster.local in-addr.arpa ip6.arpa { # 쿠버네티스의 **Service 도메인**과 POD IP 기반으로 DNS 쿼리를 응답
pods insecure # pods verified 는 같은 네임스페이스에 속한 파드끼리만 A 레코드에 관한 DNS 쿼리에 응답, # pods disabled 파드간 질의 불가능
fallthrough in-addr.arpa ip6.arpa # 도메인 찾을 시 실패했을 때 동작을 설정
ttl 30 # ttl 설정으로 타임아웃을 제어
}
prometheus :9153 # http://<Pod-IP>9153/metrics 으로 프로메테우스 형식의 메트릭 정보를 제공
**forward . /etc/resolv.conf** { # 클러스터 도메인으로 설정되지 않은 DNS 쿼리를 호스트의 /etc/resolv.conf 에 설정된 외부 DNS 서버로 보내서 처리
max_concurrent 1000
}
cache 30 # DNS 쿼리의 캐시 유지 시간을 30초로 설정
loop # 간단한 전달 루프(loop)를 감지하고, 루프가 발견되면 CoreDNS 프로세스를 중단(halt) - [링크](https://github.com/coredns/coredns/tree/master/plugin/loop#troubleshooting)
reload # Corefile 변경 감지하여 자동으로 설정 내용을 반영 (보통 2분 정도 이내)
loadbalance # 응답에 대하여 A, AAAA, MX 레코드의 순서를 무작위로 선정하는 라운드-로빈 방식 사용
}
# (옵션) 헬스체크(Liveness) , 준비체크(Readiness)
$ COREDNSPOD1=$(kubectl get pod -n kube-system -l k8s-app=kube-dns -o jsonpath='{.items[0].status.podIP}')
$ echo $COREDNSPOD1
$ curl $COREDNSPOD1:8080/health; echo
OK
$ curl $COREDNSPOD1:8181/ready; echo
OK
- 파드마다 .spec.dnsPolicy를 사용하여 도메인 이름을 어떤 우선순위로 찾을 수 있는지 설정할 수 있다 - CoreDNS configmap 옵션 - 링크
.spec.dnsPolicy | 동작방법 |
---|---|
Default | 파드가 실행중인 노드의 DNS 설정 사용 |
ClusterFirst (기본값) | 클러스터와 일치하지 않는 경우, 외부 DNS 인 업스트립 DNS 에 도메인 이름을 질의 |
ClusterFirstWithHostNet | 파드를 hostNetwork 실행할 때 반드시 사용해야하는 필드 |
None | 클러스터 안의 DNS 설정을 무시하고 .spec.dnsPolicy 하위 필드로 DNS 설정 |
파드에서 DNS Query 과정
- 테스트 단순화를 위해서 CoreDNS 파드를 1개로 줄이자 - 링크
kubectl get pod -n kube-system -l k8s-app=kube-dns
kubectl scale deployment -n kube-system coredns --replicas=1
kubectl get pod -n kube-system -l k8s-app=kube-dns
- 테스트용 파드 생성 후 DNS 정보 확인
# 파드 생성
$ kubectl run -it --rm netdebug --image=nicolaka/netshoot --restart=Never -- zsh
-------------
# 파드에서 네임서버 확인
$ cat /etc/resolv.conf
nameserver 10.96.0.10
search default.svc.cluster.local svc.cluster.local cluster.local
options ndots:5
# 도메인 쿼리 기본 방식 : ndots:5 설정으로 최종 호출 전에 설정되어 있는 검색 도메인(search)을 추가하여 먼저 쿼리 호출
$ nslookup -type=A google.com -debug
$ nslookup -type=A google.com -debug | grep QUESTIONS -A1
QUESTIONS:
google.com.default.svc.cluster.local, type = A, class = IN
--
QUESTIONS:
google.com.svc.cluster.local, type = A, class = IN
--
QUESTIONS:
google.com.cluster.local, type = A, class = IN
--
QUESTIONS:
google.com, type = A, class = IN
-------------
# coreDNS 파드가 동작하는 노드에서 cali 인터페이스 패킷 캡쳐 확인
$ ps -ef | grep coredns
$ lsns -t net
$ ip -c link
$ CoreDNSveth=$(calicoctl get workloadEndpoint -n kube-system | grep coredns | awk '{print $5}' | cut -d "/" -f 1)
$ tcpdump -i $CoreDNSveth -nn udp port 53
22:38:51.989852 IP 172.16.228.90.54107 > 172.16.29.25.53: 7433+ A? naver.com.default.svc.cluster.local. (53)
22:38:51.990255 IP 172.16.29.25.53 > 172.16.228.90.54107: 7433 NXDomain*- 0/1/0 (146)
22:38:51.991170 IP 172.16.228.90.45098 > 172.16.29.25.53: 3600+ A? naver.com.svc.cluster.local. (45)
22:38:51.991533 IP 172.16.29.25.53 > 172.16.228.90.45098: 3600 NXDomain*- 0/1/0 (138)
22:38:51.992099 IP 172.16.228.90.52731 > 172.16.29.25.53: 550+ A? naver.com.cluster.local. (41)
22:38:51.992433 IP 172.16.29.25.53 > 172.16.228.90.52731: 550 NXDomain*- 0/1/0 (134)
22:38:51.995699 IP 172.16.228.90.54055 > 172.16.29.25.53: 7984+ A? naver.com. (27)
22:38:51.995980 IP 172.16.29.25.57886 > 10.0.2.3.53: 7984+ [1au] A? naver.com. (38)
22:38:51.999990 IP 10.0.2.3.53 > 172.16.29.25.57886: 7984 4/3/4 A 125.209.222.141, A 125.209.222.142, A 223.130.195.200, A 223.130.195.95 (205)
22:38:52.000292 IP 172.16.29.25.53 > 172.16.228.90.54055: 7984 4/3/3 A 223.130.195.200, A 125.209.222.141, A 125.209.222.142, A 223.130.195.95 (324)
# 혹은
$ ngrep -tW byline -d $CoreDNSveth '' 'udp port 53'
# 외부 도메인일 경우 맨 끝. 입력을 통해 빠르게 쿼리 가능 (위 쿼리 후 30초 후 아래 쿼리 할 것!) 이유는?
# absolute domain name 절대 도메인 이름 : 도메인 이름을 TLD 까지 생략하지 않고 표기하며 끝에 루트를 나타나는 '.'을 붙인 도메인 이름.
# Relative Domain Name 상대 도메인 이름 : 도메인 이름을 생략해서 표기. 예시) test.kr 기준 'www' 등
# FQDN (Fully Qualified Domain Name) 전체 주소 도메인 이름 : TLD 까지의 모든 라벨을 포함한 도메인 이름. 절대 도메인 이름과는 달리 끝에 '.' 을 붙이는 것을 설정하는 상황에 따라 구분. URL 이나 메일 주소 표기 등 모든 라벨을 포함한 정보를 반드시 설정해야 할 때는 끝에 '.' 을 붙이지 않는 것이 일반적.
$ nslookup -type=A google.com. -debug
$ nslookup -type=A google.com. -debug | grep QUESTIONS -A1
QUESTIONS:
google.com, type = A, class = IN
좋은 강의를 받으며 사실 소화를 다 못하고 있습니다. 특히 시간에 쫓기며 정리를 제대로 못하는게 안타깝네요. 제가 지금 작성하는 내용은 “가시다”님이 제공해주는 자료를 따라하며서 발췌한 자료 입니다.