Theme:

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

정리

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

댓글 로딩 중...