Node Local DNS Cache 설치 및 관리
Node Local DNS Cache는 각 워커 노드에서 DaemonSet으로 실행되는 DNS 캐시입니다. 파드의 DNS 요청을 같은 노드에서 처리하고, 캐시에 없는 요청만 클러스터 DNS로 전달하여 DNS 조회 지연 시간과 CoreDNS의 부하를 줄입니다. 자세한 내용은 Kubernetes NodeLocal DNSCache 공식 문서를 참고하시기 바랍니다.
Node Local DNS Cache는 Node Problem Detector와 독립적으로 설치하고 관리할 수 있습니다. 노드 문제 감지가 필요한 경우 Node Problem Detector 설치 및 관리를 참고하시기 바랍니다.
- Node Local DNS Cache는 Kubernetes 1.32 이상 지원 버전의 클러스터에서 수동 설치할 수 있으며, 자동으로 설치되지 않습니다.
- 이 가이드로 설치한 Node Local DNS Cache는 사용자가 관리하는 Helm 릴리스입니다. 설치, 설정, 업그레이드, 삭제 및 장애 복구는 사용자가 수행합니다.
- Cilium 1.14.4 클러스터에서는 Helm 차트만 설치하면 일반 파드에 캐시가 적용되지 않을 수 있습니다. 캐시를 사용할 워크로드의 파드 명세와 DNS 송신(egress) 정책을 변경할 수 없는 경우 CoreDNS 부하 감소 효과를 얻기 어려우므로 설치하지 않는 것을 권장합니다.
다음과 같은 DNS 성능 문제가 지속되는 경우 Node Local DNS Cache 도입을 검토할 수 있습니다.
- CoreDNS 파드의 CPU 제한(throttling) 또는 높은 CPU 사용률이 지속되는 경우
- CoreDNS 복제본(replica) 수를 조정한 뒤에도 DNS 조회 지연이 지속되는 경우
- 애플리케이션에서 DNS 요청 시간 초과(timeout)가 반복적으로 발생하는 경우
DNS 성능 문제가 없으면 설치하지 않아도 됩니다. 운영 환경에 배포하기 전에 개발 또는 스테이징 클러스터에서 CoreDNS의 CPU 사용량과 제한 발생 여부, DNS 지연 시간과 요청 시간 초과, Node Local DNS Cache 요청 메트릭을 배포 전후로 비교합니다.
Node Local DNS Cache는 대상 워커 노드마다 기본적으로 CPU 25m, 메모리 128Mi를 요청하고 메모리 한도를 128Mi로 설정합니다. system-node-critical 우선순위와 모든 NoSchedule, NoExecute 노드 테인트를 허용하는 toleration을 사용하므로, 자원이 부족한 노드에서는 낮은 우선순위의 파드를 선점할 수 있습니다.
Step 1. 사전 작업
클러스터와 도구 준비
- Node Local DNS Cache를 설치할 Kubernetes Engine 클러스터를 생성합니다.
- 생성한 클러스터에 노드 풀을 생성하고 워커 노드가
Ready상태인지 확인합니다. - 생성한 클러스터에 명령을 실행할 수 있도록 kubectl 제어 설정을 수행합니다.
- Helm 공식 문서를 참고하여 Helm 3을 설치합니다.
- 호스트 네트워크를 사용하는 파드의 포트를 확인할 수 있도록 jq를 설치합니다.
프라이빗 서브넷에 배치된 워커 노드는 인터넷에 직접 접근할 수 없습니다. Node Local DNS Cache 컨테이너 이미지와 진단용 이미지를 가져올 수 있도록 NAT 인스턴스 등의 외부 통신 경로를 구성하고, 워커 노드에서 이미지 레지스트리에 접근할 수 있는지 확인합니다. 자세한 내용은 NAT 인스턴스 사용을 참고하시기 바랍니다.
대상 클러스터의 kubeconfig와 진단용 이미지를 환경 변수로 설정합니다. 진단용 이미지는 워커 노드에서 가져올 수 있도록 사용자가 관리하는 레지스트리에 준비해야 합니다. 다음은 busybox:1.36.1을 해당 레지스트리에 미러링한 예시입니다.
export KUBE_CONFIG=/path/to/cluster-kubeconfig.yaml
export CUSTOMER_REGISTRY=registry.example.com/project
export NODE_DEBUG_IMAGE=${CUSTOMER_REGISTRY}/busybox:1.36.1
export DNS_TEST_IMAGE=${CUSTOMER_REGISTRY}/busybox:1.36.1
KUBE_CONFIG와 CUSTOMER_REGISTRY를 실제 환경에 맞게 변경합니다. NODE_DEBUG_IMAGE에는 sh와 chroot 명령이, DNS_TEST_IMAGE에는 nslookup과 sleep 명령이 포함되어 있어야 합니다.
클러스터의 Kubernetes 버전을 확인합니다.
kubectl --kubeconfig=$KUBE_CONFIG version
Kubernetes minor 버전에 맞춰 환경 변수를 설정합니다. 다음은 Kubernetes 1.33 클러스터의 예시입니다.
export K8S_MINOR=1.33
export CHART_VERSION=${K8S_MINOR}.0
| Kubernetes 버전 | Helm 차트 버전 |
|---|---|
| 1.32 | 1.32.0 |
| 1.33 | 1.33.0 |
| 1.34 | 1.34.0 |
| 1.35 | 1.35.0 |
Node Local DNS Cache Helm 차트는 카카오클라우드 Public OCI 레지스트리에서 제공합니다. 별도의 Helm 저장소를 등록하지 않고 설치할 수 있습니다.
CNI 및 기존 배포 확인
클러스터에서 사용하는 CNI를 확인합니다. Cilium은 kube-system, Calico는 calico-system 네임스페이스에 배포되므로 전체 네임스페이스에서 확인합니다.
kubectl --kubeconfig=$KUBE_CONFIG get daemonset -A \
| grep -E 'cilium|calico' || true
Kubernetes Engine에서 검증한 Cilium 1.14.4 환경에서는 kubeProxyReplacement=false이고 kube-proxy가 정상인 경우에도 일반 파드의 DNS 요청이 Node Local DNS Cache가 설정한 NOTRACK 경로를 거치지 않습니다. Cilium에서 Node Local DNS Cache를 사용하려면 Cilium 워크로드에 DNS 설정 적용에 따라 캐시를 사용할 워크로드의 설정을 변경해야 합니다.
기존에 설치된 Node Local DNS Cache가 있는지 확인합니다.
helm --kubeconfig=$KUBE_CONFIG list -n kube-system -a
kubectl --kubeconfig=$KUBE_CONFIG get daemonset -n kube-system \
| grep -E 'node-local-dns' || true
기존 배포가 있으면 중복으로 설치하지 말고 Helm 릴리스 이름, 적용된 설정값 및 관리 주체를 먼저 확인합니다. Node Local DNS Cache를 중복으로 배포하면 동일한 노드의 DNS IP, 포트 및 iptables 규칙을 동시에 변경합니다. 이때 한 배포가 종료되면 다른 배포에서 사용하는 네트워크 설정까지 제거될 수 있습니다.
helm list 출력에는 ke-cilium, ke-tigera-operator와 같이 Kubernetes Engine이 관리하는 시스템 릴리스가 표시될 수 있습니다. 시스템 릴리스는 수정하거나 삭제하지 마세요.
레지스트리와 네트워크 충돌 확인
워커 노드에서 다음 레지스트리에 접근할 수 있는지 확인합니다.
ke-container-registry.kr-central-2.kcr.dev
Node Local DNS Cache에서 사용할 IP가 노드의 다른 인터페이스 또는 경로에서 사용 중인지 확인합니다. {node-name}을 실제 워커 노드 이름으로 변경합니다.
kubectl --kubeconfig=$KUBE_CONFIG debug node/{node-name} -it \
--image=$NODE_DEBUG_IMAGE -- \
chroot /host sh -c 'ip addr show | grep 169.254.20.25 || true'
kubectl --kubeconfig=$KUBE_CONFIG debug node/{node-name} -it \
--image=$NODE_DEBUG_IMAGE -- \
chroot /host sh -c 'ip route get 169.254.20.25 || true'
Node Local DNS Cache는 NET_ADMIN 권한(capability)과 호스트 네트워크를 사용하여 노드의 네트워크 설정을 변경합니다. 기존 네트워크 인터페이스나 호스트 에이전트가 169.254.20.25를 사용하고 있으면 배포하지 마세요.
Node Local DNS Cache가 워커 노드에서 사용하는 포트의 충돌 여부를 확인합니다.
| DNS | 상태 확인 | 메트릭 |
|---|---|---|
| TCP/UDP 53 | TCP 8080 | TCP 9253, TCP 9353 |
다음 명령으로 호스트 네트워크를 사용하는 파드에 설정된 포트를 확인합니다. containerPort는 필수 설정이 아니므로, 이 결과만으로 포트 충돌이 없다고 판단할 수 없습니다.
kubectl --kubeconfig=$KUBE_CONFIG get pod -A -o json \
| jq -r '.items[] | select(.spec.hostNetwork == true) | [.metadata.namespace, .metadata.name, ([.spec.containers[].ports[]?.containerPort] | join(","))] | @tsv'
워커 노드에 접근할 수 있으면 기본 포트의 실제 사용 여부도 확인합니다.
ss -lntup | grep -E ':(53|8080|9253|9353)\b' || true
상태 확인 포트 8080이 충돌하면 사용하지 않는 포트를 선택하여 설정 파일의 config.healthPort에 입력합니다. DNS 포트 53과 메트릭 포트 9253, 9353은 이 Helm 차트에서 변경할 수 없습니다. 해당 포트가 충돌하면 원인을 해결하기 전까지 설치하지 마세요.
Step 2. Node Local DNS Cache 설치
설정 파일 작성
클러스터의 CoreDNS 서비스인 kube-dns의 클러스터 IP(ClusterIP)를 확인합니다.
kubectl --kubeconfig=$KUBE_CONFIG \
-n kube-system get service kube-dns \
-o jsonpath='{.spec.clusterIP}'
echo
node-local-dns-values.yaml 파일을 작성합니다.
config:
bindIp: true
dnsServer: '<CLUSTER_DNS_IP>'
localDns: 169.254.20.25
healthPort: 8080 # 포트 충돌 확인에서 변경했다면 선택한 값으로 수정
enableLogging: false
<CLUSTER_DNS_IP>를 위 명령으로 출력된 실제 서비스 IP로 변경합니다. 셸 환경 변수는 YAML 파일에 자동으로 반영되지 않으므로 자리표시자가 남아 있지 않은지 확인합니다. 다른 IP를 입력하면 캐시가 동작하지 않거나 해당 IP의 서비스 트래픽이 Node Local DNS Cache로 전달될 수 있습니다.
Helm 차트의 config.enableLogging 기본값은 true입니다. 쿼리 로깅을 활성화하면 정상 처리된 DNS 쿼리도 기록되어 로그 사용량이 증가하고 조회한 도메인 정보가 로그에 남을 수 있습니다. 이 가이드에서는 운영 환경의 기본값으로 false를 권장하므로, 기본 설정으로 설치하지 말고 위의 node-local-dns-values.yaml 파일을 반드시 적용합니다. 장애 분석을 위해 쿼리 로그가 필요한 경우에만 Step 6. 장애 발생 시 복구에 따라 일시적으로 활성화합니다.
config.bindIp를 false로 변경해도 노드 네트워크 설정은 비활성화되지 않습니다. 설치 후 Step 3. 배포 확인의 메트릭 검증을 반드시 수행합니다.
Helm 차트 확인 및 설치
Helm 차트의 기본값과 생성될 리소스를 확인합니다.
helm show values \
oci://ke-container-registry.kr-central-2.kcr.dev/ke-helm-public/node-local-dns \
--version $CHART_VERSION
helm template node-local-dns \
oci://ke-container-registry.kr-central-2.kcr.dev/ke-helm-public/node-local-dns \
--version $CHART_VERSION \
--namespace kube-system \
--values node-local-dns-values.yaml \
| kubectl --kubeconfig=$KUBE_CONFIG apply --dry-run=server -f -
Helm 차트의 기본값인 config.setupInterface: true와 config.setupIptables: true는 로컬 DNS IP와 클러스터 DNS 서비스 IP를 노드 네트워크에 설정하고 관련 네트워크 인터페이스 및 iptables 규칙을 추가합니다. kube-proxy의 iptables 경로를 사용하는 Calico 클러스터에서는 Node Local DNS Cache 파드가 준비된 후 네트워크 규칙이 적용되기까지 환경에 따라 수십 초에서 1분 정도 걸릴 수 있습니다. 적용이 완료되면 해당 노드의 기존 ClusterFirst 파드가 Node Local DNS Cache를 사용하며 파드를 재시작할 필요는 없습니다.
Node Local DNS Cache는 DaemonSet으로 배포되므로 모든 대상 워커 노드의 DNS 경로가 비슷한 시점에 변경될 수 있습니다. 운영 클러스터에서는 이 점을 고려하여 서비스 영향이 적은 시간에 설치합니다.
Node Local DNS Cache 파드가 생성, 교체 또는 삭제되는 설치, 설정 변경, 롤백 및 제거 과정에서는 해당 노드의 DNS 조회가 일시적으로 실패하거나 지연될 수 있습니다. 서비스 영향이 적은 시간에 작업하고, 완료한 후 즉시 배포 상태를 확인합니다.
helm --kubeconfig=$KUBE_CONFIG upgrade --install node-local-dns \
oci://ke-container-registry.kr-central-2.kcr.dev/ke-helm-public/node-local-dns \
--version $CHART_VERSION \
--namespace kube-system \
--values node-local-dns-values.yaml \
--wait \
--timeout 5m
Cilium 워크로드에 DNS 설정 적용
Cilium 클러스터에서는 DNS 송신(egress) 정책을 먼저 적용한 후 Node Local DNS Cache를 사용할 워크로드의 파드 명세를 변경합니다. Calico 클러스터에서는 이 절차를 건너뜁니다.
DNS 송신을 제한하는 워크로드는 파드 명세를 변경하기 전에 169.254.20.25/32의 TCP 및 UDP 53번 포트로 통신할 수 있어야 합니다. 다음 규칙을 기존 CiliumNetworkPolicy의 egress에 추가하고 정책이 적용되었는지 확인합니다.
egress:
- toCIDR:
- 169.254.20.25/32
toPorts:
- ports:
- port: '53'
protocol: TCP
- port: '53'
protocol: UDP
toFQDNs 정책 사용 시 주의기존 CiliumNetworkPolicy에서 toFQDNs로 송신 트래픽을 제한하는 경우, Cilium DNS 프록시가 DNS 응답을 확인해야 도메인 이름과 IP 주소의 매핑을 학습할 수 있습니다. toCIDR 규칙만 추가한 후 워크로드의 DNS 서버를 169.254.20.25로 변경하면 DNS 요청이 Cilium DNS 프록시를 거치지 않아 toFQDNs로 허용한 대상에 연결하지 못할 수 있습니다.
toFQDNs를 사용하는 워크로드에서는 Node Local DNS Cache로 향하는 toCIDR 규칙의 toPorts에 다음과 같이 rules.dns를 추가합니다. 기존 toFQDNs 규칙은 별도의 egress 항목으로 유지합니다.
egress:
- toCIDR:
- 169.254.20.25/32
toPorts:
- ports:
- port: '53'
protocol: TCP
- port: '53'
protocol: UDP
rules:
dns:
- matchPattern: '*'
정책 적용을 확인한 뒤 Deployment, StatefulSet 등의 spec.template.spec에 다음 설정을 추가합니다.
dnsPolicy: None
dnsConfig:
nameservers:
- '169.254.20.25'
- '<CLUSTER_DNS_IP>'
searches:
- '<NAMESPACE>.svc.cluster.local'
- 'svc.cluster.local'
- 'cluster.local'
options:
- name: ndots
value: '5'
<CLUSTER_DNS_IP>를 실제 CoreDNS 서비스 IP로, <NAMESPACE>를 워크로드 네임스페이스로 변경합니다. 첫 번째 DNS 서버는 Node Local DNS Cache이며, 두 번째 DNS 서버는 CoreDNS 대체 경로입니다. 첫 번째 DNS 서버가 응답하지 않으면 DNS 리졸버가 응답을 기다린 후 두 번째 DNS 서버에 요청하므로 조회가 지연될 수 있습니다. 따라서 두 번째 DNS 서버를 즉시 전환되는 장애 대응 경로로 간주하지 마세요.
파드 템플릿을 변경하면 대상 워크로드의 파드가 순차적으로 재시작됩니다. 운영 워크로드의 업데이트 전략과 PDB(PodDisruptionBudget)를 확인하고 트래픽 영향이 적은 시간에 적용합니다. 이 설정을 적용하지 않은 일반 파드는 기존 CoreDNS 경로를 사용하므로 캐시 효과를 얻을 수 없습니다.
Step 3. 배포 확인
Helm 릴리스와 DaemonSet 확인
Helm 릴리스 상태를 확인합니다.
helm --kubeconfig=$KUBE_CONFIG status node-local-dns -n kube-system
DaemonSet과 파드가 모든 대상 워커 노드에서 정상 실행되는지 확인합니다.
kubectl --kubeconfig=$KUBE_CONFIG get daemonset \
node-local-dns -n kube-system
kubectl --kubeconfig=$KUBE_CONFIG get pod -n kube-system -o wide \
-l k8s-app=node-local-dns
kubectl --kubeconfig=$KUBE_CONFIG rollout status \
daemonset/node-local-dns -n kube-system
DNS 조회 확인
클러스터 내부 서비스와 외부 도메인의 DNS 조회를 확인합니다.
kubectl --kubeconfig=$KUBE_CONFIG run dns-test \
--rm -it \
--restart=Never \
--image=$DNS_TEST_IMAGE \
-- nslookup kubernetes.default.svc.cluster.local
kubectl --kubeconfig=$KUBE_CONFIG run dns-test-external \
--rm -it \
--restart=Never \
--image=$DNS_TEST_IMAGE \
-- nslookup kakao.com
Node Local DNS Cache의 주소를 직접 지정하여 조회합니다.
kubectl --kubeconfig=$KUBE_CONFIG run dns-test-node-local \
--rm -it \
--restart=Never \
--image=$DNS_TEST_IMAGE \
-- nslookup kubernetes.default.svc.cluster.local 169.254.20.25
직접 조회 성공은 Node Local DNS Cache가 169.254.20.25에서 응답하는지만 확인합니다. 일반 파드의 DNS 요청이 실제로 캐시를 통과하는지는 다음 절에서 별도로 확인합니다.
일반 파드의 DNS 경로 확인
Calico 클러스터에서는 일반 ClusterFirst 파드를 생성합니다.
kubectl --kubeconfig=$KUBE_CONFIG run dns-path-test \
--restart=Never \
--image=$DNS_TEST_IMAGE \
--command -- sleep 3600
Cilium 클러스터에서는 다음 파일의 <CLUSTER_DNS_IP>를 실제 CoreDNS 서비스 IP로 변경합니다. <DNS_TEST_IMAGE>는 echo $DNS_TEST_IMAGE 명령으로 확인한 이미지로 변경합니다. 그런 다음 Cilium 워크로드에 DNS 설정 적용과 동일한 설정을 가진 검증 파드를 생성합니다.
apiVersion: v1
kind: Pod
metadata:
name: dns-path-test
spec:
restartPolicy: Never
dnsPolicy: None
dnsConfig:
nameservers:
- '169.254.20.25'
- '<CLUSTER_DNS_IP>'
searches:
- 'default.svc.cluster.local'
- 'svc.cluster.local'
- 'cluster.local'
options:
- name: ndots
value: '5'
containers:
- name: dns-path-test
image: '<DNS_TEST_IMAGE>'
command: ['sleep', '3600']
kubectl --kubeconfig=$KUBE_CONFIG apply -f dns-path-test.yaml
검증 파드가 준비되면 같은 노드에서 실행되는 Node Local DNS Cache 파드를 확인합니다.
kubectl --kubeconfig=$KUBE_CONFIG wait pod/dns-path-test \
--for=condition=Ready --timeout=2m
export DNS_TEST_NODE=$(kubectl --kubeconfig=$KUBE_CONFIG \
get pod dns-path-test -o jsonpath='{.spec.nodeName}')
export NODE_LOCAL_DNS_POD=$(kubectl --kubeconfig=$KUBE_CONFIG \
get pod -n kube-system \
-l k8s-app=node-local-dns \
--field-selector spec.nodeName=$DNS_TEST_NODE \
-o jsonpath='{.items[0].metadata.name}')
echo $DNS_TEST_NODE
echo $NODE_LOCAL_DNS_POD
선택된 Node Local DNS Cache 파드의 메트릭 포트를 로컬로 전달한 뒤, 검증 파드에서 DNS 요청을 발생시키기 전과 후의 카운터를 비교합니다.
kubectl --kubeconfig=$KUBE_CONFIG port-forward -n kube-system \
pod/$NODE_LOCAL_DNS_POD 9253:9253 \
> /tmp/node-local-dns-port-forward.log 2>&1 &
export PORT_FORWARD_PID=$!
curl --retry 10 --retry-delay 1 --retry-connrefused -fsS \
http://127.0.0.1:9253/metrics >/dev/null
curl -s http://127.0.0.1:9253/metrics \
| grep '^coredns_dns_requests_total' || true
for i in 1 2 3 4 5; do
kubectl --kubeconfig=$KUBE_CONFIG exec dns-path-test -- \
nslookup kubernetes.default.svc.cluster.local >/dev/null
done
curl -s http://127.0.0.1:9253/metrics \
| grep '^coredns_dns_requests_total' || true
kill $PORT_FORWARD_PID
kubectl --kubeconfig=$KUBE_CONFIG delete pod dns-path-test
두 번째 출력의 coredns_dns_requests_total 값이 첫 번째 출력보다 증가하면 검증 파드의 DNS 요청이 Node Local DNS Cache를 경유한 것입니다. Calico에서는 일반 ClusterFirst 파드로 검증하고, Cilium에서는 dnsConfig가 적용된 파드로 검증합니다.
메트릭이 출력되지 않거나 값이 증가하지 않으면 운영 환경에 적용하지 마세요. Cilium에서는 먼저 워크로드의 dnsPolicy와 dnsConfig를 제거하고 기본 CoreDNS 경로를 확인한 후 Node Local DNS Cache를 삭제합니다. Calico에서는 바로 삭제 절차를 수행합니다.
검증 완료 기준
다음 항목이 모두 충족되어야 배포가 완료된 것으로 판단합니다.
- Helm 릴리스가
deployed상태입니다. - DaemonSet의 Ready 수가 대상 워커 노드 수와 일치합니다.
- 클러스터 내부 서비스와 외부 도메인의 DNS 조회가 성공합니다.
169.254.20.25를 DNS 서버로 지정한 직접 조회가 성공합니다.- Calico의 일반 파드 또는 Cilium에서
dnsConfig를 적용한 파드로 DNS 요청을 발생시킨 뒤 같은 노드의coredns_dns_requests_total값이 증가합니다. config.dnsServer가 실제 클러스터 DNS 서비스 IP와 일치합니다.- CoreDNS와 Node Local DNS Cache 로그에 지속적인 DNS 오류가 없습니다.
- 워커 노드가
Ready상태를 유지합니다.
Step 4. 운영 모니터링
배포 후 다음 항목을 모니터링합니다.
- DNS 조회 지연 시간과 실패율
- CoreDNS와 Node Local DNS Cache의
SERVFAIL,timeout,connection refused오류 로그 - Node Local DNS Cache 파드의 재시작 횟수
- 워커 노드의
Ready상태 변화
Node Local DNS Cache가 실제 DNS 경로에서 사용되는지 확인할 때는 Calico의 일반 파드 또는 Cilium에서 dnsConfig를 적용한 파드로 DNS 요청을 발생시킨 후 coredns_dns_requests_total 메트릭의 증가 여부를 함께 확인합니다.
실제 적용된 설정값과 로그는 다음 명령으로 확인할 수 있습니다.
helm --kubeconfig=$KUBE_CONFIG get values node-local-dns \
-n kube-system -a
kubectl --kubeconfig=$KUBE_CONFIG logs -n kube-system \
-l k8s-app=node-local-dns --tail=100
Step 5. Node Local DNS Cache 삭제
삭제할 Helm 릴리스를 확인합니다. helm list는 현재 네임스페이스의 릴리스만 표시하므로 kube-system 네임스페이스를 명시합니다.
helm --kubeconfig=$KUBE_CONFIG list -n kube-system -a \
| grep -E '^node-local-dns[[:space:]]'
필요한 경우 삭제 전에 현재 설정값을 백업합니다.
helm --kubeconfig=$KUBE_CONFIG get values node-local-dns \
-n kube-system -a -o yaml > node-local-dns-values-backup.yaml
Cilium 워크로드에 dnsPolicy와 dnsConfig를 적용했다면 Node Local DNS Cache를 삭제하기 전에 해당 설정을 제거하여 기본 CoreDNS 경로로 되돌립니다. 파드의 순차 재시작이 완료되고 기본 CoreDNS 경로의 DNS 조회가 정상인지 확인한 후 삭제합니다. 워크로드 또는 kubelet이 169.254.20.25를 DNS 서버로 직접 사용하고 있으면 삭제 후 DNS 장애 또는 조회 지연이 발생할 수 있습니다.
helm --kubeconfig=$KUBE_CONFIG uninstall node-local-dns \
--namespace kube-system \
--wait \
--timeout 5m
Helm 릴리스와 Kubernetes 리소스가 삭제되었는지 확인합니다.
helm --kubeconfig=$KUBE_CONFIG list -n kube-system -a \
| grep -E '^node-local-dns[[:space:]]' || true
kubectl --kubeconfig=$KUBE_CONFIG get daemonset -n kube-system \
| grep '^node-local-dns[[:space:]]' || true
기본 클러스터 DNS 경로에서 내부 서비스와 외부 도메인이 정상적으로 조회되는지 확인합니다.
kubectl --kubeconfig=$KUBE_CONFIG run dns-test-after-uninstall \
--rm -it \
--restart=Never \
--image=$DNS_TEST_IMAGE \
-- nslookup kubernetes.default.svc.cluster.local
kubectl --kubeconfig=$KUBE_CONFIG run dns-test-external-after-uninstall \
--rm -it \
--restart=Never \
--image=$DNS_TEST_IMAGE \
-- nslookup kakao.com
Step 6. 장애 발생 시 복구
DNS 조회 실패, 지연 시간 증가 또는 워커 노드의 NotReady 전환이 발생하면 먼저 상태와 로그를 확인합니다.
kubectl --kubeconfig=$KUBE_CONFIG get daemonset \
node-local-dns -n kube-system
kubectl --kubeconfig=$KUBE_CONFIG logs -n kube-system \
-l k8s-app=node-local-dns --tail=200
kubectl --kubeconfig=$KUBE_CONFIG logs -n kube-system \
-l k8s-app=kube-dns --tail=200
상세한 DNS 요청을 확인해야 할 때만 쿼리 로깅(query logging)을 활성화합니다. 정상 처리된 DNS 쿼리도 기록되어 로그 사용량이 증가할 수 있으므로 필요한 시간 동안만 활성화하고, 조사가 끝나면 비활성화합니다.
helm --kubeconfig=$KUBE_CONFIG upgrade node-local-dns \
oci://ke-container-registry.kr-central-2.kcr.dev/ke-helm-public/node-local-dns \
--version $CHART_VERSION \
--namespace kube-system \
--reuse-values \
--set config.enableLogging=true \
--wait \
--timeout 5m
조사가 끝나면 같은 명령에서 값을 false로 변경하여 다시 비활성화합니다.
Helm 릴리스 이력을 확인합니다.
helm --kubeconfig=$KUBE_CONFIG history node-local-dns -n kube-system
이전에 정상적으로 동작한 리비전(revision)이 있으면 해당 리비전으로 롤백합니다. 롤백 과정에서도 해당 노드의 DNS 조회가 일시적으로 실패하거나 지연될 수 있으므로 서비스 영향이 적은 시간에 진행합니다.
helm --kubeconfig=$KUBE_CONFIG rollback node-local-dns {revision} \
--namespace kube-system \
--wait \
--timeout 5m
첫 설치에 실패했거나 롤백 후에도 장애가 지속되면 Step 5. Node Local DNS Cache 삭제에 따라 Helm 릴리스를 삭제합니다. 롤백하거나 삭제한 후에는 클러스터 내부와 외부 도메인의 DNS 조회가 기본 클러스터 DNS 경로에서 정상 동작하는지 확인하고 검증 완료 기준을 다시 확인합니다.