본문 바로가기

DevOps

[AWS EKS] External DNS / Core DNS

CloudNet@팀의 EKS 스터디 AEWS 2기에 작성된 자료를 토대로 작성합니다.

 

✅   External DNS                                                                                                                                       

  • K8S 서비스/인그레스 생성 시 도메인을 설정하면, AWS(Route 53) 에 A 레코드(TXT 레코드)로 자동 생성/삭제
  • annotate 명령어로 service/인그레스에 도메인을 자동 생성 및 설정 할수 있음
kubectl annotate service tetris "external-dns.alpha.kubernetes.io/hostname=tetris.$MyDomain"

 

☑️ 실습 1

  • external dns 설치
# EKS 배포 시 Node IAM Role 설정되어 있음
# eksctl create cluster ... --external-dns-access ...

# 
MyDomain=<자신의 도메인>
MyDomain=taskoo.net

# 자신의 Route 53 도메인 ID 조회 및 변수 지정
MyDnzHostedZoneId=$(aws route53 list-hosted-zones-by-name --dns-name "${MyDomain}." --query "HostedZones[0].Id" --output text)

# 변수 확인
echo $MyDomain, $MyDnzHostedZoneId

# ExternalDNS 배포
curl -s -O https://raw.githubusercontent.com/gasida/PKOS/main/aews/externaldns.yaml
sed -i "s/0.13.4/0.14.0/g" externaldns.yaml
cat externaldns.yaml | yh
MyDomain=$MyDomain MyDnzHostedZoneId=$MyDnzHostedZoneId envsubst < externaldns.yaml | kubectl apply -f -

# 확인 및 로그 모니터링
kubectl get pod -l app.kubernetes.io/name=external-dns -n kube-system
kubectl logs deploy/external-dns -n kube-system -f
  • NLB ( 테트리스 deployment로 검증 )
# 터미널1 (모니터링)
watch -d 'kubectl get pod,svc'
kubectl logs deploy/external-dns -n kube-system -f

# 테트리스 디플로이먼트 배포
cat <<EOF | kubectl create -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: tetris
  labels:
    app: tetris
spec:
  replicas: 1
  selector:
    matchLabels:
      app: tetris
  template:
    metadata:
      labels:
        app: tetris
    spec:
      containers:
      - name: tetris
        image: bsord/tetris
---
apiVersion: v1
kind: Service
metadata:
  name: tetris
  annotations:
    service.beta.kubernetes.io/aws-load-balancer-nlb-target-type: ip
    service.beta.kubernetes.io/aws-load-balancer-scheme: internet-facing
    service.beta.kubernetes.io/aws-load-balancer-cross-zone-load-balancing-enabled: "true"
    service.beta.kubernetes.io/aws-load-balancer-backend-protocol: "http"
    #service.beta.kubernetes.io/aws-load-balancer-healthcheck-port: "80"
spec:
  selector:
    app: tetris
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80
  type: LoadBalancer
  loadBalancerClass: service.k8s.aws/nlb
EOF

# 배포 확인
kubectl get deploy,svc,ep tetris

# NLB에 ExternanDNS 로 도메인 연결
kubectl annotate service tetris "external-dns.alpha.kubernetes.io/hostname=tetris.$MyDomain"
while true; do aws route53 list-resource-record-sets --hosted-zone-id "${MyDnzHostedZoneId}" --query "ResourceRecordSets[?Type == 'A']" | jq ; date ; echo ; sleep 1; done

# Route53에 A레코드 확인
aws route53 list-resource-record-sets --hosted-zone-id "${MyDnzHostedZoneId}" --query "ResourceRecordSets[?Type == 'A']" | jq
aws route53 list-resource-record-sets --hosted-zone-id "${MyDnzHostedZoneId}" --query "ResourceRecordSets[?Type == 'A'].Name" | jq .[]

# 확인
dig +short tetris.$MyDomain @8.8.8.8
dig +short tetris.$MyDomain

# 도메인 체크
echo -e "My Domain Checker = https://www.whatsmydns.net/#A/tetris.$MyDomain"

# 웹 접속 주소 확인 및 접속
echo -e "Tetris Game URL = http://tetris.$MyDomain"

✅   Core DNS                                                                                                                                            

  • 버전 1.13까지의 Kubernetes 클러스터에서 kube-dns는 Kubernetes에 대한 dns역할을 했었습니다.
  • 쿠버네티스에서 기본적으로 사용되는 DNS는 CoreDNS입니다. 
  • CoreDNS앞단에서 도메인 캐싱을 하여 도메인 쿼리 성능을 향상시켜 주는 NodeLocal DNS Cache
  • POD가 DNS 조회를 수행하면 먼저 자기 자신의 리졸브 파일을 확인하고 없으면 노드의 DNS 캐시로 쿼리를 전송함
  • 캐시에 요청된 조회 결과가 포함되어 있지 않다면 클러스터 DNS 서버로 전달됨

  • pod 내부의 resolv.conf 파일 확인 
    • A. 같은 클러스터에 svc가 생성될때, 물론 당연하게 pod에는 resolv.conf 파일에 아무런 변화가 없다 
    • B. 그렇다면 어디에 업데이트가 될까? 
    • C. ndots:5 에 비밀이 숨겨져 있다.
    • D. kubelet은 검색 도메인과 ndots 옵션을 통해 DNS 쿼리를 처리한다.
  • ndots는 "dot"이라는 DNS에서 사용되는 구분자인 점(.)을 의미하며, 이 숫자는 도메인 이름에 포함된 점(.)의 개수를 나타냅니다. 즉, options ndots:5는 DNS 질의에서 도메인 이름에 점(.)이 5개 포함된 경우까지 검색을 시도한다는 것을 의미합니다.
  • "foo.bar.svc.cluster.local" 다음과 같이 전체 도메인에 (.) 4개 포함된 경우는 nodts:4 인것이죠.
# k exec -it tetris-57fb4bb6f9-gr7gb /bin/sh
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.

# 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를 변경하는 방법은 다음과 같습니다.
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 파일이 변경되는것을 확인 할수 있습니다.


✅   도전과제 14                                                                                                                                           

[도전과제14] EKS에 NodeLocal DNS Cache 설정으로 클러스터의 DNS 성능 향상 - Docs 블로깅

  • 그림에서 nodelocaldns 부분 
  •  CoreDNS앞단에서 도메인 캐싱을 하여 도메인 쿼리 성능을 향상시켜 주는 NodeLocal DNS Cache
  •  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__LOCAL__DNS__ : NodeLocal DNSCache에 대해 선택된 로컬 수신 IP 주소
# __PILLAR__DNS__DOMAIN__ : 클러스터 도메인을 나타내며 기본값은 cluster.local
# __PILLAR__DNS__SERVER__ : kubectl get svc -n kube-system | grep kube-dns | awk '{ print $3 }'

# manifest 변수 수정 
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

# k 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 서버에서 응답을 기다릴 필요가 없으므로 응답 시간이 훨씬 빠릅니다

 

  • "NXDOMAIN"은 쿼리한 도메인에 대해 존재하지 않음을 나타냅니다. 즉, 해당 도메인에 대한 레코드가 없음을 의미합니다. 이런 경우 외부의 DNS 서버에 요청을 보내고 응답을 기다립니다.
  • "NOERROR"은 쿼리에 대한 성공적인 응답을 나타냅니다. 요청한 도메인에 대한 레코드가 존재하고, 해당 레코드를 찾았음을 의미합니다. 이처럼 같은 클러스터 내부에 있는 서비스를 찾을 경우 캐시를 이용합니다. 

✅   도전과제 19                                                                                                                                           

[도전과제19] Recent changes to the CoreDNS add-on - Link

  • topologySpreadConstraints 설정 추가
  • coredns 애드온에 PDB(Pod Disruption Budget) 추가
  • DNS 확인 실패를 최소화하기 위해 기본적으로 coreDNS 플러그인에 lameduck 옵션을 추가
  • CoreDNS의 readinessProbe 에서 /health 대신 /ready를 사용
  • EKS 관리형 Add-On Pod에 대한 라벨 설정