Kubernetes 内部 DNS 工作机制
Kubernetes DNS
Kubernetes 通过 CoreDNS(或早期 kube-dns)为集群内的 Pod 和 Service 提供 DNS 解析,实现服务发现和内部通信。简单来说:每个 Service 和 Pod 都有自己的域名,CoreDNS 负责将这些域名解析为对应的 IP 地址。
一、核心组件
Kubernetes DNS 体系由三个组件协同工作:
- CoreDNS,DNS服务器,解析集群内服务域名,默认部署方式是Deployment + Service (kube-dns)
- kubelet,配置 Pod 的 /etc/resolv.conf,每个节点运行一个
- kube-proxy,维护 Service 的 IPTABLES 规则,默认以DaemonSet形式部署
三者协作关系:kubelet 把 CoreDNS 的地址写进每个 Pod → Pod 发起 DNS 查询 → 查询先到 kube-dns Service 的 ClusterIP → kube-proxy 的规则将流量转发到真正的 CoreDNS Pod → CoreDNS 返回解析结果。
二、CoreDNS 详解
- coreDNS的前身是kube-DNS
- coreDNS在kube-system命名空间中以pod的形式运行,由deployment创建多个副本。
- CoreDNS 的可执行文件位于 Pod 容器内,默认镜像为 registry.k8s.io/coredns/coredns。配置文件: /etc/coredns/Corefile
- CoreDNS在启动时,会创建一个Service,默认叫kube-dns。pod的dns服务器地址为该Service的Cluster IP。
- kubelet 配置文件(/var/lib/kubelet/config.yaml)中的 clusterDNS 字段指定的就是该 Service 的 ClusterIP,kubelet 会把它写入每个 Pod 的 /etc/resolv.conf
CoreDNS请求处理流程

三、Pod 如何获得 DNS 配置
理解了 CoreDNS 是"谁",接下来看 DNS 配置是如何从 CoreDNS 传递到每个 Pod 的。
3.1 kubelet 的配置
kubelet 的配置文件中指定了集群的 DNS 地址和域名后缀:
# cat /var/lib/kubelet/config.yaml
clusterDNS:
- 10.0.0.1
clusterDomain: cluster.local
kubelet 将 clusterDNS 的值写入每个 Pod 的 /etc/resolv.conf 作为 nameserver。因此,Pod 内所有 DNS 查询都会发往 CoreDNS。
3.2 resolv.conf 结构
# cat /etc/resolv.conf
nameserver 10.96.0.10
search default.svc.cluster.local svc.cluster.local cluster.local
options ndots:5
- nameserver:DNS 服务器地址,即
kube-dnsService 的 ClusterIP - search:搜索域列表,用于将短域名补全为 FQDN
- options ndots:5:控制何时使用 search 域补齐
3.3 search 域的工作原理
当 Pod 内发起 DNS 查询(如 curl web-service),系统按顺序尝试用 search 域拼接:
| 尝试顺序 | 拼接后的域名 | 何时命中 |
|---|---|---|
| 1 | web-service.default.svc.cluster.local |
Service 在同一命名空间 |
| 2 | web-service.svc.cluster.local |
不会命中(缺少命名空间) |
| 3 | web-service.cluster.local |
几乎不会命中 |
这就是为什么同命名空间内只需写 Service 名就能访问的原因。
3.4 ndots:5 的含义
查询的域名中,如果点号数量 < 5,先尝试 search 域补齐后再查询;如果点号数量 >= 5,则视为完整域名直接查询,不补 search 域。
| 查询的域名 | 点号数 | 行为 |
|---|---|---|
web-service |
0 | 先补 search 域:web-service.default.svc.cluster.local |
web-service.apps |
1 | 先补 search 域 |
web-service.apps.svc.cluster.local |
4 | 先补 search 域 |
www.baidu.com |
2 | 先补 search 域(可能产生多余查询) |
注意: search 域中不包含
.pod.cluster.local,所以访问 Pod 必须使用 FQDN。这个细节在下一节"域名格式"中会体现。
四、Service 和 Pod 的域名格式
知道了 DNS 配置如何投递、search 域如何补齐后,接下来看集群中 Service 和 Pod 的域名长什么样。
4.1 Service DNS
同命名空间内访问 Service 的三种写法(假设 Service 名为 web-service,命名空间 apps,Pod 也在 apps):
web-service # 最短,依赖 resolv.conf 的 search 域
web-service.apps # 中等
web-service.apps.svc.cluster.local # 完全限定域名(FQDN)
跨命名空间访问(从 default 命名空间访问 apps 命名空间的 Service):
web-service.apps # 最短(必须带命名空间)
web-service.apps.svc.cluster.local # FQDN
FQDN 格式:
<service-name>.<namespace>.svc.cluster.local
4.2 Pod DNS
Pod 有两种 DNS 记录格式:
1. IP-based DNS(默认开启)
将 Pod IP 中的点(.)替换为短横线(-):
# 格式
<pod-ip-with-dashes>.<namespace>.pod.cluster.local
# 示例:Pod IP 10.244.2.5,命名空间 apps
10-244-2-5.apps.pod.cluster.local
2. Hostname-based DNS
需要在 Pod spec 中显式设置 hostname 和 subdomain:
apiVersion: v1
kind: Pod
metadata:
name: my-app
spec:
hostname: my-pod
subdomain: my-subdomain # Kubernetes 会在同命名空间找名为 my-subdomain 的 Service
# 找到后将此 Pod 的 IP 作为 A 记录写入该 Service 的域名下
containers:
- name: app
image: nginx
此时 DNS 记录为:
my-pod.my-subdomain.<namespace>.svc.cluster.local
注意: 后缀是
.svc.cluster.local而不是.pod.cluster.local。因为subdomain字段的含义是"将 Pod 注册到同名 Service 的域名下",这条记录本质上是 Service 域名树的子条目。且subdomain必须匹配一个同名的 Headless Service(普通 Service 有 ClusterIP 代理,Pod 级记录无意义)。
4.3 StatefulSet 示例
StatefulSet 是 hostname-based DNS 的典型应用场景——每个 Pod 拥有稳定、可预测的域名,便于直接寻址:
apiVersion: v1
kind: Service
metadata:
name: mysql
namespace: db
spec:
clusterIP: None # Headless
selector:
app: mysql
ports:
- port: 3306
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql
namespace: db
spec:
serviceName: mysql # 即 subdomain
replicas: 2
template:
spec:
subdomain: mysql
containers:
- name: mysql
image: mysql:8.0
StatefulSet 自动为每个 Pod 命名为 mysql-0、mysql-1。CoreDNS 生成:
# Headless Service 的 SRV 记录
mysql.db.svc.cluster.local SRV 0 33 3306 mysql-0.mysql.db.svc.cluster.local
mysql.db.svc.cluster.local SRV 0 33 3306 mysql-1.mysql.db.svc.cluster.local
# 每个 Pod 的 A 记录
mysql-0.mysql.db.svc.cluster.local A 10.244.1.10
mysql-1.mysql.db.svc.cluster.local A 10.244.1.11
4.4 速查表
| 目标 | 同命名空间最短写法 | FQDN |
|---|---|---|
| Service | service-name |
service-name.ns.svc.cluster.local |
| Pod (IP-based) | 无短名,必须 FQDN | 10-244-2-5.ns.pod.cluster.local |
| Pod (hostname) | 无短名,必须 FQDN | pod-name.subdomain.ns.svc.cluster.local |
| 跨命名空间 Service | service-name.ns |
service-name.ns.svc.cluster.local |
核心记忆点:Service 在 search 路径里所以有短名,Pod 不在所以没有。
五、DNS 记录类型
前面了解了域名格式,接下来深入看 CoreDNS 为这些域名生成了哪些类型的 DNS 记录。
5.1 A 记录(Address Record)
A 记录用于将域名映射到 IPv4 地址。在 Kubernetes 中,A 记录通常用于将 Service 的 DNS 名称解析为 ClusterIP。
# 普通 Service:域名 → Cluster IP
{service-name}.{namespace}.svc.{domain} → Cluster IP
# Headless Service:域名 → 后端 Pod IP 列表
{service-name}.{namespace}.svc.{domain} → 后端 Pod IP 列表
# Pod 的 IP-based 记录(将 IP 中的点替换为短横线)
{pod-ip-with-dashes}.{namespace}.pod.{domain} → Pod IP
# Pod 的 hostname 记录(需设置 hostname + subdomain,详见 4.2 节)
# 后缀是 .svc.cluster.local,因为是 Service 域名树的子条目
{hostname}.{subdomain}.{namespace}.svc.cluster.local → Pod IP
5.2 AAAA 记录(Quad-A Record)
将域名映射到 IPv6 地址。CoreDNS 根据 Service ClusterIP 类型自动选择:
| ClusterIP 类型 | 创建的记录 |
|---|---|
| IPv4 | A 记录 |
| IPv6 | AAAA 记录 |
| Dual-stack | A + AAAA 两条都有 |
web-service.apps.svc.cluster.local → AAAA → fd00:10:96::1:5
5.3 SRV 记录(Service Record)
SRV 记录用于发现服务的主机名、端口号和协议,尤其适用于需要暴露多个端口的服务(如数据库、消息队列等)。
格式:
_{port-name}._{protocol}.{service-name}.{namespace}.svc.cluster.local
多端口 Service 示例:
apiVersion: v1
kind: Service
metadata:
name: my-service
namespace: default
spec:
selector:
app: my-app
ports:
- name: http
port: 80
targetPort: 8080
protocol: TCP
- name: metrics
port: 9090
targetPort: 9090
protocol: TCP
生成以下 SRV 记录:
_http._tcp.my-service.default.svc.cluster.local
_metrics._tcp.my-service.default.svc.cluster.local
查询示例:
nslookup -type=SRV _http._tcp.my-service.default.svc.cluster.local
# 返回结果:
_http._tcp.my-service.default.svc.cluster.local service = 10 50 80 my-service.default.svc.cluster.local.
字段含义:
10:优先级(Kubernetes 中固定为 10)50:权重(Kubernetes 中固定为 50)80:端口号my-service.default.svc.cluster.local:目标主机名
Headless Service 的 SRV 记录:
apiVersion: v1
kind: Service
metadata:
name: my-headless
spec:
clusterIP: None # Headless Service
selector:
app: my-app
ports:
- port: 80
name: http
protocol: TCP
假设该 Service 有 3 个 Pod,CoreDNS 会为每个 Pod 生成独立的 SRV 记录:
_http._tcp.my-headless.default.svc.cluster.local service = 10 50 80 172-17-0-3.my-headless.default.svc.cluster.local.
_http._tcp.my-headless.default.svc.cluster.local service = 10 50 80 172-17-0-4.my-headless.default.svc.cluster.local.
_http._tcp.my-headless.default.svc.cluster.local service = 10 50 80 172-17-0-5.my-headless.default.svc.cluster.local.
典型应用场景 — 数据库集群发现:
apiVersion: v1
kind: Service
metadata:
name: mysql
spec:
clusterIP: None
selector:
app: mysql
ports:
- port: 3306
name: mysql
protocol: TCP
应用程序通过 SRV 记录发现所有 MySQL Pod:
nslookup -type=SRV _mysql._tcp.mysql.default.svc.cluster.local
5.4 CNAME 记录(Canonical Name Record)
CNAME 记录用于将域名别名指向另一个域名(而非直接指向 IP)。CoreDNS 通过 rewrite 插件实现类似功能:
# Corefile 配置示例
.:53 {
rewrite name alias.service.local target.service.default.svc.cluster.local
kubernetes cluster.local
forward . /etc/resolv.conf
}
效果:所有对 alias.service.local 的查询被重定向到 target.service.default.svc.cluster.local。
六、域名解析策略(dnsPolicy)
前面几节讲的都是默认情况下的 DNS 行为。Kubernetes 还允许通过 spec.dnsPolicy 为每个 Pod 定制域名解析策略,支持以下四种:
Default
Pod 继承所在节点的 /etc/resolv.conf 配置。这种方式下 Pod 使用宿主机的 DNS 设置,不使用集群 DNS。适用于不需要集群内服务发现的场景。
None
忽略 Kubernetes 环境的 DNS 设置,使用 spec.dnsConfig 字段提供的完全自定义 DNS 配置:
apiVersion: v1
kind: Pod
metadata:
name: custom-dns-pod
namespace: default
spec:
dnsPolicy: None
dnsConfig:
nameservers:
- 8.8.8.8
- 8.8.4.4
searches:
- example.com
options:
- name: ndots
value: "2"
containers:
- name: my-container
image: busybox
command: ["/bin/sh", "-c", "while true; do echo Hello World; sleep 3600; done"]
ClusterFirst(默认)
优先使用集群内部 DNS 进行域名解析,无法解析的查询转发到上游 DNS。这是大多数 Pod 的默认行为。
ClusterFirstWithHostNet
用于 hostNetwork: true 的 Pod。hostNetwork 设置为 true 时,Pod 容器直接使用宿主机的网络接口和 IP 地址,默认会继承宿主机 DNS;设置 ClusterFirstWithHostNet 可以让这类 Pod 仍然使用集群 DNS。
apiVersion: v1
kind: Pod
metadata:
name: busybox
namespace: default
spec:
containers:
- name: busybox
image: busybox:latest
command: ["/bin/sh", "-c", "while true; do echo Hello World; sleep 3600; done"]
restartPolicy: Always
hostNetwork: true
dnsPolicy: ClusterFirstWithHostNet

浙公网安备 33010602011771号