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请求处理流程

image

三、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-dns Service 的 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 中显式设置 hostnamesubdomain

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-0mysql-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
posted @ 2024-05-10 10:09  立勋  阅读(70)  评论(0)    收藏  举报