DNS 解析流程

本文档描述 Kubernetes 集群中 CoreDNS 如何解析 Service 到 Pod IP 的完整流程。

概述

Kubernetes DNS 解析涉及以下核心组件:

  • CoreDNS:集群 DNS 服务器
  • kube-dns Service:指向 CoreDNS Pod 的 Service
  • EndpointSlice:记录 CoreDNS Pod 的 IP 地址
  • Pod /etc/resolv.conf:Pod 内的 DNS 配置
flowchart TD
    subgraph Pod["Pod (应用容器)"]
        A["应用发起 DNS 查询"]
        B["读取 /etc/resolv.conf"]
    end

    subgraph CoreDNS["CoreDNS Pod"]
        C["CoreDNS 服务"]
        D["Watch API Server"]
        E["Service/Endpoint 缓存"]
    end

    subgraph APIServer["API Server"]
        F["Service 资源"]
        G["EndpointSlice 资源"]
        H["Pod 资源"]
    end

    A --> B
    B -->|"nameserver 10.96.0.10"| I["kube-dns Service ClusterIP"]
    I --> J["iptables/IPVS 规则"]
    J -->|"DNAT"| C

    D -->|Watch| F
    D -->|Watch| G
    D -->|Watch| H

    C --> E
    E -->|"返回解析结果"| A

    style A fill:#e1f5fe
    style C fill:#c8e6c9
    style F fill:#fff3e0

流程详解

1. Pod DNS 配置生成

Kubelet 为每个 Pod 生成 /etc/resolv.conf 文件,配置 DNS 服务器地址。

代码路径: pkg/kubelet/network/dns/dns.go

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// GetPodDNS 配置 Pod 的 DNS 策略
func (d *dnsConfigurer) GetPodDNS(pod *v1.Pod) (*dns.Config, error) {
switch pod.Spec.DNSPolicy {
case v1.DNSClusterFirstWithHostNet:
return d.dnsConfigForHostNetworkPod(pod)
case v1.DNSClusterFirst:
if !kubecontainer.IsHostNetworkPod(pod) {
return d.dnsConfig(pod)
}
// hostNetwork Pod 默认使用节点 DNS
fallthrough
case v1.DNSDefault:
return d.hostDNS(pod)
case v1.DNSNone:
return nil, nil
}
}

DNS 配置示例:

1
2
3
nameserver 10.96.0.10
search default.svc.cluster.local svc.cluster.local cluster.local
options ndots:5

2. kube-dns Service

kube-dns Service 是集群 DNS 的入口点:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
apiVersion: v1
kind: Service
metadata:
name: kube-dns
namespace: kube-system
spec:
clusterIP: 10.96.0.10 # 固定 ClusterIP
ports:
- name: dns
port: 53
protocol: UDP
- name: dns-tcp
port: 53
protocol: TCP
selector:
k8s-app: kube-dns # 选择 CoreDNS Pod

3. CoreDNS 工作原理

CoreDNS 通过 Watch API Server 获取 Service 和 EndpointSlice 信息:

sequenceDiagram
    participant API as API Server
    participant Coredns as CoreDNS
    participant Cache as 本地缓存
    participant Client as 客户端 Pod

    API->>Coredns: Watch Service/EndpointSlice 事件
    Coredns->>Cache: 更新本地缓存

    Client->>Coredns: DNS 查询 my-svc.default.svc.cluster.local
    Coredns->>Cache: 查找 Service 记录
    Cache->>Coredns: 返回 ClusterIP 或 Endpoint IPs
    Coredns->>Client: 返回 DNS 响应

4. Service DNS 记录类型

4.1 ClusterIP Service

1
2
3
4
5
# 服务名格式
<service-name>.<namespace>.svc.cluster.local

# 示例
my-service.default.svc.cluster.local -> 10.96.100.1

4.2 Headless Service(无 ClusterIP)

Headless Service(clusterIP: None)直接返回后端 Pod IP:

1
2
3
4
5
6
7
8
apiVersion: v1
kind: Service
metadata:
name: headless-svc
spec:
clusterIP: None # Headless
selector:
app: myapp

DNS 解析返回所有 Ready Pod 的 IP:

1
headless-svc.default.svc.cluster.local -> 10.244.1.5, 10.244.2.3

4.3 ExternalName Service

1
2
3
4
5
6
7
apiVersion: v1
kind: Service
metadata:
name: external-svc
spec:
type: ExternalName
externalName: external.example.com

DNS 返回 CNAME 记录:

1
external-svc.default.svc.cluster.local -> CNAME external.example.com

5. Pod DNS 记录(Headless Service 场景)

当 Pod 配置了 hostnamesubdomain

1
2
3
4
5
6
7
8
9
10
apiVersion: v1
kind: Pod
metadata:
name: my-pod
spec:
hostname: my-hostname
subdomain: my-subdomain # 必须匹配 Headless Service 名称
containers:
- name: app
image: nginx

DNS 记录格式:

1
2
3
4
<pod-hostname>.<subdomain>.<namespace>.svc.cluster.local

# 示例
my-hostname.my-subdomain.default.svc.cluster.local -> 10.244.1.5

代码路径: staging/src/k8s.io/api/core/v1/types.go

1
2
3
4
// Pod DNS 名称格式
// <hostname>.<subdomain>.<namespace>.svc.<cluster-domain>
Hostname string `json:"hostname,omitempty"`
Subdomain string `json:"subdomain,omitempty"`

6. CoreDNS 插件配置

CoreDNS 使用 Corefile 配置插件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
.: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
}
cache 30
loop
reload
loadbalance
}

关键插件说明:

  • kubernetes: 处理 Service/Pod DNS 记录
  • forward: 转发外部域名到上游 DNS
  • cache: 缓存 DNS 响应
  • loadbalance: 负载均衡 DNS 响应

关键代码锚点

功能 文件路径
Kubelet DNS 配置 pkg/kubelet/network/dns/dns.go
HostNetwork 判断 pkg/kubelet/container/helpers.go:IsHostNetworkPod
Service DNS 名称格式 staging/src/k8s.io/api/core/v1/types.go
EndpointSlice 工具 staging/src/k8s.io/endpointslice/util/controller_utils.go

DNS 查询流程总结

  1. 应用发起 DNS 查询 → 读取 /etc/resolv.conf
  2. 查询发送到 kube-dns ClusterIP → iptables/IPVS 转发
  3. CoreDNS 接收查询 → 查找本地缓存
  4. 返回结果:
    • ClusterIP Service → 返回 Service ClusterIP
    • Headless Service → 返回 Pod IP 列表
    • ExternalName → 返回 CNAME
    • 外部域名 → 转发到上游 DNS

常见问题

DNS 解析慢

  • 检查 CoreDNS Pod 状态
  • 检查 ndots 配置(过多会导致多次查询)
  • 考虑使用 NodeLocal DNSCache

Service 无法解析

  • 检查 Service 是否存在
  • 检查 EndpointSlice 是否正常
  • 检查 CoreDNS 日志

高频面试题

Q1: Kubernetes 中 Service 的 DNS 解析流程是怎样的?

参考答案:

  1. Pod 内应用发起 DNS 查询,读取 /etc/resolv.conf 获取 DNS 服务器地址
  2. 查询发送到 kube-dns Service 的 ClusterIP(通常是 10.96.0.10)
  3. iptables/IPVS 将请求 DNAT 到后端 CoreDNS Pod
  4. CoreDNS 查找本地缓存(通过 Watch API Server 获取 Service/EndpointSlice 信息)
  5. 返回解析结果:
    • ClusterIP Service → 返回 Service 的 ClusterIP
    • Headless Service → 返回所有 Ready Pod 的 IP 列表
    • ExternalName Service → 返回 CNAME 记录

Q2: Headless Service 和普通 Service 的 DNS 解析有什么区别?

参考答案:

特性 普通 ClusterIP Service Headless Service
DNS 返回 返回 Service 的 ClusterIP 返回所有后端 Pod IP 列表
负载均衡 由 kube-proxy/iptables 完成 由客户端 DNS 轮询或应用层处理
适用场景 普通服务访问 StatefulSet、直接 Pod 寻址
配置 clusterIP: 10.96.x.x clusterIP: None

Q3: Pod 的 DNSPolicy 有哪几种?分别是什么作用?

参考答案:

1
2
3
4
5
6
7
8
9
10
11
12
# 1. ClusterFirst(默认)
# - 使用集群 DNS(CoreDNS)
# - hostNetwork 的 Pod 会回退到 Default

# 2. ClusterFirstWithHostNet
# - 即使是 hostNetwork 的 Pod 也使用集群 DNS

# 3. Default
# - 使用节点 DNS 配置(继承宿主机 /etc/resolv.conf)

# 4. None
# - 使用自定义 DNS 配置,必须指定 dnsConfig

Q4: CoreDNS 是如何获取 Service 和 Pod 信息的?

参考答案:

  1. CoreDNS 通过 ServiceAccount 获得 RBAC 权限,可以 list/watch Service、Pod、EndpointSlice、Namespace 资源
  2. 使用 client-go 的 Informer 机制 Watch API Server
  3. 当资源变化时,Informer 触发回调,更新 CoreDNS 本地缓存
  4. kubernetes 插件根据缓存数据生成 DNS 记录

Q5: 如何排查 Pod DNS 解析失败的问题?

参考答案:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 1. 检查 Pod 的 DNS 配置
kubectl exec -it <pod> -- cat /etc/resolv.conf

# 2. 测试 DNS 解析
kubectl exec -it <pod> -- nslookup kubernetes.default

# 3. 检查 CoreDNS Pod 状态
kubectl get pods -n kube-system -l k8s-app=kube-dns

# 4. 查看 CoreDNS 日志
kubectl logs -n kube-system -l k8s-app=kube-dns

# 5. 检查 kube-dns Service 和 EndpointSlice
kubectl get svc kube-dns -n kube-system
kubectl get endpointslice -n kube-system -l kubernetes.io/service-name=kube-dns

# 6. 常见原因:
# - CoreDNS Pod 未正常运行
# - ndots 配置导致查询超时
# - ndots:5 使得短域名需要多次查询
# - 节点 DNS 配置问题

延伸阅读