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 레코드를 관리합니다.

SHELL
# 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)

SHELL
kubectl get configmap coredns -n kube-system -o yaml
PLAINTEXT
.: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

PLAINTEXT
<service-name>.<namespace>.svc.cluster.local
예제설명
user-service같은 네임스페이스에서 접근
user-service.production다른 네임스페이스에서 접근
user-service.production.svc.cluster.local전체 FQDN
SHELL
# 같은 네임스페이스 — 이름만으로 충분
curl http://user-service:8080

# 다른 네임스페이스 — 네임스페이스 포함
curl http://user-service.production:8080

# FQDN — 모호함 제거
curl http://user-service.production.svc.cluster.local:8080

SRV 레코드

Service의 포트 정보도 DNS로 조회할 수 있습니다.

PLAINTEXT
_<port-name>._<protocol>.<service>.<namespace>.svc.cluster.local
SHELL
# SRV 레코드 조회
nslookup -type=SRV _http._tcp.user-service.default.svc.cluster.local

Headless Service DNS

Headless Service는 Pod IP를 직접 반환합니다.

SHELL
# 일반 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로 접근할 수 있습니다.

PLAINTEXT
<pod-name>.<headless-service>.<namespace>.svc.cluster.local
SHELL
# 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가 자동으로 설정됩니다.

SHELL
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 항목 덕분에 짧은 이름으로도 서비스에 접근할 수 있습니다.

PLAINTEXT
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 쿼리가 발생할 수 있습니다.

PLAINTEXT
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를 낮춥니다.

SHELL
# FQDN에 점 추가 — search 도메인 스킵
curl http://api.github.com.

DNS Policy

Pod마다 DNS 동작을 제어할 수 있습니다.

YAML
spec:
  dnsPolicy: ClusterFirst  # 기본값
정책설명
ClusterFirstCoreDNS 먼저, 없으면 외부 DNS (기본값)
Default노드의 DNS 설정 사용
ClusterFirstWithHostNethostNetwork: true일 때 ClusterFirst 동작
NonednsConfig으로 직접 지정
YAML
# 커스텀 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 등)의 레코드를 자동으로 관리합니다.

YAML
# 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 레코드를 자동 생성합니다.

SHELL
# ExternalDNS 설치 (Helm)
helm install external-dns external-dns/external-dns \
  --set provider=aws \
  --set domainFilters[0]=example.com

DNS 트러블슈팅

DNS 문제는 Kubernetes에서 자주 발생합니다. 체계적으로 디버깅하는 방법입니다.

SHELL
# 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가 부족하여 부하 초과

주의할 점

1. ndots:5 기본 설정이 외부 도메인 조회를 느리게 만든다

Kubernetes는 기본적으로 ndots를 5로 설정합니다. api.example.com처럼 점이 5개 미만인 도메인을 조회하면 api.example.com.default.svc.cluster.local 같은 내부 도메인을 먼저 4번 시도한 후에야 실제 외부 DNS를 조회합니다. 외부 API 호출이 잦은 서비스는 Pod spec에서 dnsConfig.options로 ndots를 2~3으로 줄이거나, 외부 도메인 끝에 .을 붙여서(FQDN) 불필요한 검색을 피하세요.

2. Headless Service의 DNS가 반환하는 IP는 순서가 보장되지 않는다

StatefulSet과 Headless Service를 함께 쓸 때, nslookup으로 확인한 A 레코드의 순서가 항상 같을 거라고 가정하면 안 됩니다. DNS 라운드로빈의 순서는 매번 달라질 수 있습니다. 특정 Pod에 접근해야 하면 pod-0.headless-svc.namespace.svc.cluster.local 같은 개별 DNS 레코드를 사용하세요.

3. CoreDNS Pod이 죽으면 클러스터 전체의 서비스 통신이 끊긴다

CoreDNS는 클러스터 DNS의 단일 진입점입니다. CoreDNS Pod이 OOM으로 죽거나 리소스 부족으로 응답이 느려지면, 모든 서비스 간 통신이 실패합니다. CoreDNS의 replicas를 최소 2개 이상 유지하고, 리소스 requests/limits를 적절히 설정해야 합니다.

정리

Kubernetes의 서비스 디스커버리는 CoreDNS가 Service와 Pod의 DNS 레코드를 자동 관리하는 구조입니다. 같은 네임스페이스에서는 서비스 이름만으로, 다른 네임스페이스에서는 <이름>.<네임스페이스> 형식으로 접근합니다. ndots:5 설정으로 인한 외부 DNS 쿼리 지연과 CoreDNS의 리소스/replicas 관리는 실무에서 자주 만나는 이슈이니 기억해 두는 것이 좋습니다.

댓글 로딩 중...