본문 바로가기
스터디 이야기/25' Cilium

Cilium 환경에서 NodeLocal DNSCache 적용하기

by lakescript 2025. 7. 30.
728x90

NodeLocalDNS

NodeLocalDNS란?

 

NodeLocal DNSCache는 Kubernetes 클러스터의 각 Node에서 DNS 캐싱 기능을 수행하는 Agent를 DaemonSet으로 실행하여 DNS 성능과 안정성을 개선하는 기능입니다.

 

기본적인 kubernetes 아키텍처에서는 모든 DNS 쿼리가 kube-dns/CoreDNS의 Service IP를 통해 전달되는데, 이는 kube-proxy에 의해 추가된 iptables DNAT 규칙을 거쳐 최종적으로 kube-dns/CoreDNS 엔드포인트로 변환되었습니다. 하지만 이러한 방식은 경로가 복잡하고 트래픽에 대해 추적을 할 때나 디버깅할 때 매우 복잡하고 네트워크 복잡도가 높아 성능상 비효율적입니다. 

 

NodeLocal DNSCache는 각 Node에 위치한 로컬 DNS 캐시 Agent가 DNS 요청을 직접 수신함으로써 이러한 경로를 우회하게 합니다. 이를 통해 iptables을 생략하고, 네트워크 지연을 줄이며, DNS 트래픽의 추적 및 분석을 단순화할 수 있어 전체 DNS 성능을 향상시킵니다.

 

coredns와 nodelocaldns에 대해서 자세히 알고 싶다면 이전 블로그 게시글 를 참고해주세요.

 

실습

실습 환경 구성

실습 환경 소개

 

vagrant를 통해 k8s-ctr, k8s-w1, router로 총 3개의 실습 환경을 구성합니다.

 

이때, router는 loop1/loop2 dump 인터페이스 배치되고, 사내망 10.10.0.0/16대역과 통신하며, kubernetes cluster에 join 되지 않은 서버입니다. 추가로 cilium이 배포되어있는 환경입니다. 

 

NodeLocalDNS 설치

iptables 확인

iptables-save | tee before.txt

 

현재 iptables를 확인해보겠습니다.

 

상당히 복잡한 것을 알 수 있습니다.

 

nodelocaldns.yaml 다운로드

wget https://github.com/kubernetes/kubernetes/raw/master/cluster/addons/dns/nodelocaldns/nodelocaldns.yaml

 

NodeLocal DNSCache의 DaemonSet 구성 파일을 다운로드합니다.

 

환경 변수 지정 및 확인

kubedns=`kubectl get svc kube-dns -n kube-system -o jsonpath={.spec.clusterIP}`
domain='cluster.local'    ## default 값
localdns='169.254.20.10'  ## default 값

 

 

NodeLocal DNSCache는 DNS 쿼리 캐시 실패시에 kube-dns/CoreDNS 서비스의 IP로 쿼리를 포워딩하기 때문에 해당 값을 변수로 지정하고, Cluster의 기본 DNS 도메인(cluster.local)과 로컬 DNS 캐시가 사용할 IP(169.254.20.10)를 환경 변수로 지정합니다.

echo $kubedns $domain $localdns

 

 

지정된 환경 변수를 확인해보겠습니다.

 

nodelocaldns.yaml 수정

sed -i "s/__PILLAR__LOCAL__DNS__/$localdns/g; s/__PILLAR__DNS__DOMAIN__/$domain/g; s/__PILLAR__DNS__SERVER__/$kubedns/g" nodelocaldns.yaml

 

iptables 모드 사용 중으로 위 명령어를 통해 nodelocaldns.yaml 파일 내의 플레이스홀더(placeholder)를 수정합니다.

 

 __PILLAR__LOCAL__DNS__  은 로컬 DNS 캐시 에이전트가 수신할 IP 주소로 환경 변수로 지정한 $localdns로 변경하고, __PILLAR__DNS__DOMAIN__은 클러스터 DNS 도메인으로 환경 변수로 지정한 $domain으로 변경합니다.
마지막으로   __PILLAR__DNS__SERVER__  은 실제 kube-dns/CoreDNS 서비스의 ClusterIP인 $kubedns으로 변경합니다.

 

nodelocaldns 설치 및 확인

kubectl apply -f nodelocaldns.yaml

 

그렇게 수정한 nodelocaldns.yaml을 배포합니다.

 

kubectl get pod -n kube-system -l k8s-app=node-local-dns -owide

 

배포된 nodelocaldns 관련 pod들을 확인해보겠습니다.

 

2개의 node에 배포되어 있는 것을 확인하실 수 있습니다.

 

log 설정

kubectl edit cm -n kube-system node-local-dns

 

실제로 coreDNS의 활동을 확인해보기 위해 CoreDNS 설정에 로그를 추가합니다.

 

 

 'cluster.local'  과  '.:53'  에 log, debug 를 추가합니다.

이때, nodelocaldns pod는 2분마다 configmap을 갱신하기 때문에 재시작할 필요는 없습니다.

 

NodeLocalDNS 활동 확인

샘플 애플리케이션 배포

더보기

샘플 애플리케이션 pod

cat << EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: webpod
spec:
  replicas: 2
  selector:
    matchLabels:
      app: webpod
  template:
    metadata:
      labels:
        app: webpod
    spec:
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - sample-app
            topologyKey: "kubernetes.io/hostname"
      containers:
      - name: webpod
        image: traefik/whoami
        ports:
        - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: webpod
  labels:
    app: webpod
spec:
  selector:
    app: webpod
  ports:
  - protocol: TCP
    port: 80
    targetPort: 80
  type: ClusterIP
EOF

 

curl-pod

cat << EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: curl-pod
  labels:
    app: curl
spec:
  containers:
  - name: curl
    image: nicolaka/netshoot
    command: ["tail"]
    args: ["-f", "/dev/null"]
  terminationGracePeriodSeconds: 0
EOF

 

 

로그 실시간 확인

kubectl -n kube-system logs -l k8s-app=node-local-dns -f

 

실제로 dns 질의 시 어떻게 동작하는지 확인해보기 위해 kube-dns와 node-local-dns pod에 로그를 실시간으로 확인해보겠습니다.

 

 

이제 curl-pod에서 webpod로 DNS 질의 했을 때의 로그를 확인해보겠습니다.

kubectl exec -it curl-pod -- nslookup http://www.google.com

 

 

 

node-local-dns pod에 아무런 변화가 없는 것을 확인할 수 있습니다.

Cilium에서 NodeLocalDNS 설정

Cilium은 kube-proxy 없이 eBPF를 사용하여 Service 트래픽을 처리하기 때문에, iptables 기반의 DNAT 경로가 설정되지 않습니다. 따라서 NodeLocal DNSCache가 바라보는 iptables 규칙이 생성되지 않으며, Pod의 DNS 요청이 NodeLocalAgent로 전달되지 않습니다. 이때, Local Redirect Policy 기능을 사용하여 Kubernetes Service 로 향하는 Pod의 트래픽을 eBPF를 사용하여 Node 내에 백엔드 pod로 리디렉션하도록 합니다.

 

localRedirectPolicy 활성화

helm upgrade cilium cilium/cilium --namespace kube-system --reuse-values \
  --set localRedirectPolicy=true

 

 

cilium-operator, cilium-agent 재시작

kubectl rollout restart deploy cilium-operator -n kube-system
kubectl rollout restart ds cilium -n kube-system

 

node-local-dns.yaml 설치

wget https://raw.githubusercontent.com/cilium/cilium/1.17.6/examples/kubernetes-local-redirect/node-local-dns.yaml

 

기존에 설치했던 node-local-dns말고, cilium에서 제공하는 node-local-dns yaml을 설치합니다.

 

환경변수 지정

kubedns=$(kubectl get svc kube-dns -n kube-system -o jsonpath={.spec.clusterIP})
sed -i "s/__PILLAR__DNS__SERVER__/$kubedns/g;" node-local-dns.yam

 

node-local-dns 배포

kubectl apply -f node-local-dns.yaml

 

 

node-local-dns configmap 수정

kubectl edit cm -n kube-system node-local-dns

 

이번에도 위에서 했던 것과 마찬가지로 node-local-dns configmap에서   'cluster.local'   과  '.:53'  에 log, debug를 추가합니다.

 

 

 

node-local-dns policy 다운로드 및 배포

kubectl apply -f https://raw.githubusercontent.com/cilium/cilium/1.17.6/examples/kubernetes-local-redirect/node-local-dns-lrp.yaml


이제 cilium에 맞게 설정된 network policy를 다운로드하고 배포합니다.
 

해당 정책은 kube-dns service로 향하는 트래픽을 동일 노드 내에 배포 되어있는 node-local-dns Pod로 eBPF를 통해 리디렉션하게끔 정책이 설정되어있습니다.  

 

node local dns 동작 확인

node local dns 로그 실시간 확인

kubectl -n kube-system logs -l k8s-app=node-local-dns -f


node-local-dns pod의 log를 실시간으로 출력해보겠습니다.

pod dns 질의

kubectl exec -it curl-pod -- nslookup http://www.google.com


curl pod에서 DNS 질의를 해보겠습니다.


정상적으로 로그가 쌓이고 DNS 질의가 진행된 것을 알 수 있습니다.

[INFO] 172.20.1.167:47375 - 45347 "A IN http://www.google.com.default.svc.cluster.local. udp 58 false 512" NXDOMAIN qr,aa,rd 151 0.003489541s
[INFO] 172.20.1.167:55665 - 15927 "A IN http://www.google.com.svc.cluster.local. udp 50 false 512" NXDOMAIN qr,aa,rd 143 0.00072225s
[INFO] 172.20.1.167:60302 - 37135 "A IN http://www.google.com.cluster.local. udp 46 false 512" NXDOMAIN qr,aa,rd 139 0.000447334s
[INFO] 172.20.1.167:55184 - 50464 "A IN http://www.google.com. udp 32 false 512" NOERROR qr,rd,ra 62 0.010364875s



위 로그를 살펴보면 DNS 질의가 search domain을 따라 순차적으로 시도되었다는 것을 알 수 있습니다. 

 

즉, pod 내부에 있는 /etc/resolv.conf에  search    default.svc.cluster.local     svc.cluster.local     cluster.local  등이 포함되어 있어서, 여기에 포함되지 않은 DNS인 http://www.google.com 에 대해 순차적으로 도메인을 붙여서 질의한 것을 알 수 있습니다.

 

728x90