ClusterFirst
클러스터 DNS를 먼저. 기본값이다.
이름이 IP가 되는 과정
kubectl get deploy coredns -n kube-systemkubectl get pods -n kube-system -l k8s-app=kube-dnskubectl get svc kube-dns -n kube-system# NAME TYPE CLUSTER-IP PORT(S)# kube-dns ClusterIP 10.96.0.10 53/UDP,53/TCP,9153/TCP특별한 시스템이 아니라 그냥 Deployment + Service다. 그래서 똑같이 고장 나고 똑같이 고친다.
flowchart LR
POD["앱 Pod<br/>/etc/resolv.conf 의<br/>nameserver 10.96.0.10"]
POD --> SVC["Service kube-dns<br/>ClusterIP 10.96.0.10"]
SVC --> EP["EndpointSlice"]
EP --> C1["CoreDNS Pod 1"]
EP --> C2["CoreDNS Pod 2"]
C1 --> K["kubernetes 플러그인<br/>Service · Pod 이름 해석"]
C1 --> F["forward 플러그인<br/>그 밖의 도메인 → 노드 리졸버"]
classDef key fill:#dbeafe,stroke:#2563eb,color:#1e3a8a
classDef dns fill:#fef3c7,stroke:#d97706,color:#78350f
classDef mute fill:#f1f5f9,stroke:#94a3b8,color:#334155
class SVC key
class C1,C2 dns
class POD,EP,K,F mute
kube-dns라는 Service가 있다 (이름이 옛날 그대로다)Service의 정식 이름(FQDN)은 이렇게 생겼다.
flowchart LR
A["web-svc"] --> B["prod"] --> C["svc"] --> D["cluster.local"]
A1["서비스 이름"] -.-> A
B1["네임스페이스"] -.-> B
C1["리소스 종류<br/>svc 또는 pod"] -.-> C
D1["클러스터 도메인<br/>기본값"] -.-> D
classDef part fill:#dbeafe,stroke:#2563eb,color:#1e3a8a
classDef label fill:#f1f5f9,stroke:#94a3b8,color:#334155
class A,B,C,D part
class A1,B1,C1,D1 label
| 어디서 부르나 | 쓸 수 있는 이름 |
|---|---|
| 같은 네임스페이스 | web-svc |
| 다른 네임스페이스 | web-svc.prod |
| 어디서나 (완전한 이름) | web-svc.prod.svc.cluster.local |
headless Service의 Pod별 이름
파드이름.서비스이름.네임스페이스.svc.cluster.local
예: db-0.db-headless.default.svc.cluster.local
kubectl exec -it web -- cat /etc/resolv.confnameserver 10.96.0.10search default.svc.cluster.local svc.cluster.local cluster.localoptions ndots:5nameserver — kube-dns Service의 ClusterIPsearch — 짧은 이름을 순서대로 붙여본다ndots:5 — 점이 5개 미만인 이름은 search 도메인을 먼저 시도한다flowchart TD
Q["web-svc 를 조회"] --> N{"점이 5개 이상인가<br/>ndots:5"}
N -->|"아니다 · 점 0개"| S1["① web-svc.default.svc.cluster.local"]
S1 -->|"성공"| OK["해석 완료 ✅"]
S1 -->|실패| S2["② web-svc.svc.cluster.local"]
S2 -->|실패| S3["③ web-svc.cluster.local"]
S3 -->|실패| S4["④ web-svc 그대로 조회"]
S4 -->|실패| NX["NXDOMAIN ❌"]
N -->|"그렇다"| DIRECT["search 를 건너뛰고 바로 조회"]
classDef ok fill:#dcfce7,stroke:#16a34a,color:#14532d
classDef bad fill:#fee2e2,stroke:#dc2626,color:#7f1d1d
classDef step fill:#dbeafe,stroke:#2563eb,color:#1e3a8a
classDef mute fill:#f1f5f9,stroke:#94a3b8,color:#334155
class OK,DIRECT ok
class NX bad
class S1,S2,S3,S4 step
class Q,N mute
spec: dnsPolicy: ClusterFirst # 기본 dnsConfig: nameservers: ["8.8.8.8"] searches: ["custom.local"] options: - name: ndots value: "2"ClusterFirst
클러스터 DNS를 먼저. 기본값이다.
ClusterFirstWithHostNet
hostNetwork: true인 Pod도
클러스터 DNS를 쓰게 한다.
Default
노드의 /etc/resolv.conf를 그대로 쓴다.
이름과 달리 기본값이 아니다.
None
전부 dnsConfig로 직접 지정한다.
flowchart LR
H["hostNetwork: true Pod"] --> D{"dnsPolicy"}
D -->|"ClusterFirst · 기본"| B["노드 DNS 를 쓰게 된다<br/>클러스터 서비스 이름 해석 실패 ❌"]
D -->|ClusterFirstWithHostNet| G["클러스터 DNS 사용<br/>정상 ✅"]
classDef ok fill:#dcfce7,stroke:#16a34a,color:#14532d
classDef bad fill:#fee2e2,stroke:#dc2626,color:#7f1d1d
classDef mute fill:#f1f5f9,stroke:#94a3b8,color:#334155
class G ok
class B bad
class H,D mute
kubectl get cm coredns -n kube-system -o yamlkubectl edit cm coredns -n kube-system.:53 { errors health { lameduck 5s } ready kubernetes cluster.local in-addr.arpa ip6.arpa { pods insecure fallthrough in-addr.arpa ip6.arpa ttl 30 } prometheus :9153 forward . /etc/resolv.conf { max_concurrent 1000 } # 외부 질의는 노드 DNS로 cache 30 loop reload loadbalance}질의 하나가 이 플러그인 체인을 순서대로 통과한다.
flowchart LR
Q["질의"] --> CA["cache<br/>있으면 바로 응답"]
CA -->|"miss"| K{"kubernetes 플러그인<br/>cluster.local 인가"}
K -->|"그렇다"| KR["Service · Pod 이름 해석 → 응답"]
K -->|"아니다 · fallthrough"| FW["forward<br/>노드 /etc/resolv.conf 로"]
FW --> EXT["외부 DNS 응답"]
classDef key fill:#dbeafe,stroke:#2563eb,color:#1e3a8a
classDef ok fill:#dcfce7,stroke:#16a34a,color:#14532d
classDef mute fill:#f1f5f9,stroke:#94a3b8,color:#334155
class K,FW key
class KR,EXT ok
class Q,CA mute
kubernetes 플러그인이 Service/Pod 이름을 담당한다forward 가 그 밖의 도메인을 노드의 리졸버로 넘긴다reload 덕분에 ConfigMap을 고치면 재시작 없이 반영된다 (몇십 초)# Corefile에 스탠자를 추가한다example.internal:53 { errors cache 30 forward . 192.168.10.53}hosts 플러그인으로 정적 매핑도 가능하다hosts { 192.168.10.50 legacy.internal fallthrough}수정 후에는 CoreDNS 로그로 문법 오류가 없는지 확인한다.
kubectl logs -n kube-system -l k8s-app=kube-dns
디버그 Pod에서 조회 — 기준점 테스트부터
kubectl run tmp --image=busybox:1.36 --rm -it --restart=Never -- sh nslookup kubernetes.default # ★ 이게 되면 DNS 는 정상 nslookup web-svc nslookup web-svc.default.svc.cluster.local cat /etc/resolv.confCoreDNS 자체 상태
kubectl get pods -n kube-system -l k8s-app=kube-dnskubectl logs -n kube-system -l k8s-app=kube-dns --tail=50kubectl get svc kube-dns -n kube-systemkubectl get endpoints kube-dns -n kube-system # ★ 비어 있으면 CoreDNS Pod이 없다설정 확인
kubectl get cm coredns -n kube-system -o yamlflowchart TD
S{"무엇이 안 되나"}
S -->|"모든 이름"| A["CoreDNS Pod 다운<br/>kube-dns 엔드포인트가 비어 있다"]
S -->|"클러스터 이름만"| B["Corefile 의 kubernetes 플러그인 이상<br/>또는 dnsPolicy 잘못"]
S -->|"외부 도메인만"| C["forward 대상 문제<br/>노드 /etc/resolv.conf 확인"]
S -->|"특정 네임스페이스만"| D["짧은 이름을 썼다<br/>→ FQDN 이 필요"]
S -->|"이름은 되는데 연결이 안 됨"| E["Service · 엔드포인트 문제 · 9장<br/>또는 NetworkPolicy · 12장"]
S -->|"간헐적으로 느리다"| F["ndots:5 로 인한 불필요한 조회<br/>CoreDNS 레플리카 부족"]
classDef bad fill:#fee2e2,stroke:#dc2626,color:#7f1d1d
classDef warn fill:#fef3c7,stroke:#d97706,color:#78350f
classDef mute fill:#f1f5f9,stroke:#94a3b8,color:#334155
class A bad
class B,C,D,E,F warn
class S mute
| 증상 | 원인 후보 |
|---|---|
| 모든 이름이 안 된다 | CoreDNS Pod 다운, kube-dns 엔드포인트 비어 있음 |
| 클러스터 이름만 안 된다 | Corefile의 kubernetes 플러그인 이상, dnsPolicy 잘못 |
| 외부 도메인만 안 된다 | forward 대상(노드 resolv.conf) 문제 |
| 특정 네임스페이스만 안 된다 | 짧은 이름을 썼다 → FQDN 필요 |
| DNS는 되는데 연결이 안 된다 | Service/엔드포인트 문제 (9장) 또는 NetworkPolicy (12장) |
| 간헐적으로 느리다 | ndots:5로 인한 불필요한 조회, CoreDNS 레플리카 부족 |
kube-dns서비스.네임스페이스.svc.cluster.local/etc/resolv.conf에 네임서버 + search 도메인 + ndots:5 가 들어간다ndots:5가 외부 도메인 조회를 느리게 만든다 — 끝에 점을 찍으면 우회hostNetwork Pod은 ClusterFirstWithHostNet 이 필요하다kube-system의 coredns ConfigMap(Corefile). reload로 자동 반영nslookup kubernetes.default