DNS와 서비스 디스커버리 — Pod이 이름으로 서로를 찾는 원리
curl http://user-service:8080을 실행하면 Pod에 요청이 도달하는데, "user-service"라는 이름은 누가 IP로 바꿔주는 걸까요?
Kubernetes 클러스터에서 Pod끼리 서비스 이름으로 통신할 수 있는 이유는 내부 DNS 시스템 덕분입니다. CoreDNS가 Service와 Pod의 DNS 레코드를 자동으로 관리하고, Pod의 /etc/resolv.conf가 이 DNS 서버를 가리키도록 설정됩니다.
CoreDNS
CoreDNS는 Kubernetes의 기본 DNS 서버입니다. kube-system 네임스페이스에서 Pod으로 실행되며, 모든 Service와 Pod에 대한 DNS 레코드를 관리합니다.
# CoreDNS Pod 확인
kubectl get pods -n kube-system -l k8s-app=kube-dns
# NAME READY STATUS RESTARTS AGE
# coredns-5d78c9869d-abcde 1/1 Running 0 10d
# coredns-5d78c9869d-fghij 1/1 Running 0 10d
# CoreDNS Service (ClusterIP)
kubectl get svc -n kube-system kube-dns
# NAME TYPE CLUSTER-IP PORT(S)
# kube-dns ClusterIP 10.96.0.10 53/UDP,53/TCP
CoreDNS 설정 (Corefile)
kubectl get configmap coredns -n kube-system -o yaml
.:53 {
errors # 에러 로깅
health { # 헬스체크 엔드포인트
lameduck 5s
}
ready # readiness 체크
kubernetes cluster.local in-addr.arpa ip6.arpa { # 클러스터 도메인
pods insecure
fallthrough in-addr.arpa ip6.arpa
ttl 30
}
prometheus :9153 # 메트릭 노출
forward . /etc/resolv.conf { # 매칭 안 되면 외부 DNS로 포워딩
max_concurrent 1000
}
cache 30 # DNS 캐시 (30초)
loop # 루프 감지
reload # Corefile 변경 시 자동 리로드
loadbalance # 라운드 로빈
}
DNS 레코드 체계
Service DNS
<service-name>.<namespace>.svc.cluster.local
| 예제 | 설명 |
|---|---|
user-service | 같은 네임스페이스에서 접근 |
user-service.production | 다른 네임스페이스에서 접근 |
user-service.production.svc.cluster.local | 전체 FQDN |
# 같은 네임스페이스 — 이름만으로 충분
curl http://user-service:8080
# 다른 네임스페이스 — 네임스페이스 포함
curl http://user-service.production:8080
# FQDN — 모호함 제거
curl http://user-service.production.svc.cluster.local:8080
SRV 레코드
Service의 포트 정보도 DNS로 조회할 수 있습니다.
_<port-name>._<protocol>.<service>.<namespace>.svc.cluster.local
# SRV 레코드 조회
nslookup -type=SRV _http._tcp.user-service.default.svc.cluster.local
Headless Service DNS
Headless Service는 Pod IP를 직접 반환합니다.
# 일반 Service — ClusterIP 반환
nslookup my-service.default.svc.cluster.local
# Address: 10.96.45.12
# Headless Service — Pod IP 목록 반환
nslookup my-headless.default.svc.cluster.local
# Address: 10.244.1.5
# Address: 10.244.2.3
StatefulSet Pod DNS
StatefulSet과 Headless Service를 조합하면 개별 Pod에 DNS로 접근할 수 있습니다.
<pod-name>.<headless-service>.<namespace>.svc.cluster.local
# StatefulSet의 각 Pod에 직접 접근
nslookup mysql-0.mysql-headless.default.svc.cluster.local
nslookup mysql-1.mysql-headless.default.svc.cluster.local
Pod의 DNS 설정
모든 Pod은 생성 시 /etc/resolv.conf가 자동으로 설정됩니다.
kubectl exec my-pod -- cat /etc/resolv.conf
# nameserver 10.96.0.10 # CoreDNS Service IP
# search default.svc.cluster.local svc.cluster.local cluster.local
# options ndots:5
search 도메인
search 항목 덕분에 짧은 이름으로도 서비스에 접근할 수 있습니다.
user-service 를 조회하면:
1. user-service.default.svc.cluster.local ← 히트!
2. user-service.svc.cluster.local
3. user-service.cluster.local
4. user-service (외부 DNS)
ndots:5의 의미
이름에 점(dot)이 5개 미만이면 search 도메인을 먼저 붙여서 조회합니다. 외부 도메인(예: api.github.com)을 조회할 때 불필요한 내부 DNS 쿼리가 발생할 수 있습니다.
api.github.com 조회 시 (점 2개 < 5):
1. api.github.com.default.svc.cluster.local ← 불필요한 쿼리
2. api.github.com.svc.cluster.local ← 불필요한 쿼리
3. api.github.com.cluster.local ← 불필요한 쿼리
4. api.github.com ← 실제 쿼리
이 문제를 해결하려면 FQDN 끝에 점을 찍거나 ndots를 낮춥니다.
# FQDN에 점 추가 — search 도메인 스킵
curl http://api.github.com.
DNS Policy
Pod마다 DNS 동작을 제어할 수 있습니다.
spec:
dnsPolicy: ClusterFirst # 기본값
| 정책 | 설명 |
|---|---|
ClusterFirst | CoreDNS 먼저, 없으면 외부 DNS (기본값) |
Default | 노드의 DNS 설정 사용 |
ClusterFirstWithHostNet | hostNetwork: true일 때 ClusterFirst 동작 |
None | dnsConfig으로 직접 지정 |
# 커스텀 DNS 설정
spec:
dnsPolicy: None
dnsConfig:
nameservers:
- 8.8.8.8
- 8.8.4.4
searches:
- my-domain.local
options:
- name: ndots
value: "2"
ExternalDNS
ExternalDNS는 Kubernetes 리소스의 변경을 감지하여 외부 DNS 서비스(Route 53, CloudDNS 등)의 레코드를 자동으로 관리합니다.
# Service에 어노테이션 추가
apiVersion: v1
kind: Service
metadata:
name: web
annotations:
external-dns.alpha.kubernetes.io/hostname: web.example.com
spec:
type: LoadBalancer
# ... 생략
ExternalDNS가 이 어노테이션을 감지하고 Route 53에 web.example.com → LoadBalancer IP A 레코드를 자동 생성합니다.
# ExternalDNS 설치 (Helm)
helm install external-dns external-dns/external-dns \
--set provider=aws \
--set domainFilters[0]=example.com
DNS 트러블슈팅
DNS 문제는 Kubernetes에서 자주 발생합니다. 체계적으로 디버깅하는 방법입니다.
# 1. DNS 디버깅용 Pod 실행
kubectl run dnsutils --image=tutum/dnsutils --command -- sleep infinity
# 2. DNS 조회 테스트
kubectl exec dnsutils -- nslookup user-service
kubectl exec dnsutils -- nslookup user-service.default.svc.cluster.local
kubectl exec dnsutils -- nslookup kubernetes.default
# 3. 외부 DNS 테스트
kubectl exec dnsutils -- nslookup google.com
# 4. CoreDNS Pod 상태 확인
kubectl get pods -n kube-system -l k8s-app=kube-dns
kubectl logs -n kube-system -l k8s-app=kube-dns
# 5. resolv.conf 확인
kubectl exec dnsutils -- cat /etc/resolv.conf
자주 발생하는 문제
- DNS 조회 실패: CoreDNS Pod이 정상인지, Service(kube-dns)가 존재하는지 확인
- 외부 도메인 조회 느림: ndots:5로 인한 불필요한 쿼리 확인
- 간헐적 실패: CoreDNS replicas가 부족하여 부하 초과
정리
Kubernetes의 서비스 디스커버리는 CoreDNS가 Service와 Pod의 DNS 레코드를 자동 관리하는 구조입니다. 같은 네임스페이스에서는 서비스 이름만으로, 다른 네임스페이스에서는 <이름>.<네임스페이스> 형식으로 접근합니다. ndots:5 설정으로 인한 외부 DNS 쿼리 지연과 CoreDNS의 리소스/replicas 관리는 실무에서 자주 만나는 이슈이니 기억해 두는 것이 좋습니다.