Service 与 Pod 的 DNS

Kubernetes 为 Service 和 Pod 创建 DNS 记录。 你可以使用一致的 DNS 名称而非 IP 地址访问 Service。

Kubernetes 发布有关 Pod 和 Service 的信息,这些信息被用来对 DNS 进行编程。 Kubelet 配置 Pod 的 DNS,以便运行中的容器可以通过名称而不是 IP 来查找服务。

集群中定义的 Service 被赋予 DNS 名称。 默认情况下,客户端 Pod 的 DNS 搜索列表会包含 Pod 自身的名字空间和集群的默认域。

DNS 查询可能因为执行查询的 Pod 所在的名字空间而返回不同的结果。 不指定名字空间的 DNS 查询会被限制在 Pod 所在的名字空间内。 要访问其他名字空间中的 Service,需要在 DNS 查询中指定名字空间。

例如,假定名字空间 中存在一个 Pod,prod 名字空间中存在一个服务 data

Pod 查询 data 时没有返回结果,因为使用的是 Pod 的名字空间 test

Pod 查询 data.prod 时则会返回预期的结果,因为查询中指定了名字空间。

DNS 查询可以使用 Pod 中的 /etc/resolv.conf 展开。 Kubelet 为每个 Pod 配置此文件。 例如,对 data 的查询可能被展开为 data.test.svc.cluster.localsearch 选项的取值会被用来展开查询。要进一步了解 DNS 查询,可参阅 resolv.conf 手册页面

概括起来,名字空间 test 中的 Pod 可以成功地解析 data.prod 或者 data.prod.svc.cluster.local

DNS 记录

哪些对象会获得 DNS 记录呢?

  1. Services
  2. Pods

以下各节详细介绍已支持的 DNS 记录类型和布局。 其它布局、名称或者查询即使碰巧可以工作,也应视为实现细节, 将来很可能被更改而且不会因此发出警告。 有关最新规范请查看 Kubernetes 基于 DNS 的服务发现

A/AAAA 记录

除了无头 Service 之外的 “普通” Service 会被赋予一个形如 my-svc.my-namespace.svc.cluster-domain.example 的 DNS A 和/或 AAAA 记录,取决于 Service 的 IP 协议族(可能有多个)设置。 该名称会解析成对应 Service 的集群 IP。

没有集群 IP 的 也会被赋予一个形如 my-svc.my-namespace.svc.cluster-domain.example 的 DNS A 和/或 AAAA 记录。 与普通 Service 不同,这一记录会被解析成对应 Service 所选择的 Pod IP 的集合。 客户端要能够使用这组 IP,或者使用标准的轮转策略从这组 IP 中进行选择。

SRV 记录

Kubernetes 根据普通 Service 或无头 Service 中的命名端口创建 SRV 记录。每个命名端口, SRV 记录格式为 _port-name._port-protocol.my-svc.my-namespace.svc.cluster-domain.example。 普通 Service,该记录会被解析成端口号和域名:my-svc.my-namespace.svc.cluster-domain.example。 无头 Service,该记录会被解析成多个结果,及该服务的每个后端 Pod 各一个 SRV 记录, 其中包含 Pod 端口号和格式为 hostname.my-svc.my-namespace.svc.cluster-domain.example 的域名。

A/AAAA 记录

一般而言,Pod 会对应如下 DNS 名字解析:

pod-ip-address.my-namespace.pod.cluster-domain.example

例如,对于一个位于 default 名字空间,IP 地址为 172.17.0.3 的 Pod, 如果集群的域名为 cluster.local,则 Pod 会对应 DNS 名称:

172-17-0-3.default.pod.cluster.local

pod-ip-address.service-name.my-namespace.svc.cluster-domain.example

当前,创建 Pod 时其主机名(从 Pod 内部观察)取自 Pod 的 metadata.name 值。

Pod 规约中包含一个可选的 hostname 字段,可以用来指定一个不同的主机名。 当这个字段被设置时,它将优先于 Pod 的名字成为该 Pod 的主机名(同样是从 Pod 内部观察)。 举个例子,给定一个 spec.hostname 设置为 “my-host” 的 Pod, 该 Pod 的主机名将被设置为 “my-host”

Pod 规约还有一个可选的 subdomain 字段,可以用来表明该 Pod 是名字空间的子组的一部分。 举个例子,某 Pod 的 spec.hostname 设置为 “foo”spec.subdomain 设置为 “bar”, 在名字空间 “my-namespace” 中,主机名称被设置成 “foo” 并且对应的完全限定域名(FQDN)为 “foo.bar.my-namespace.svc.cluster-domain.example”(还是从 Pod 内部观察)。

如果 Pod 所在的名字空间中存在一个无头服务,其名称与子域相同, 则集群的 DNS 服务器还会为 Pod 的完全限定主机名返回 A 和/或 AAAA 记录。

示例:

  1. apiVersion: v1
  2. kind: Service
  3. metadata:
  4. name: busybox-subdomain
  5. spec:
  6. selector:
  7. name: busybox
  8. clusterIP: None
  9. ports:
  10. - name: foo # 实际上不需要指定端口号
  11. port: 1234
  12. ---
  13. apiVersion: v1
  14. metadata:
  15. name: busybox1
  16. labels:
  17. name: busybox
  18. spec:
  19. subdomain: busybox-subdomain
  20. containers:
  21. - image: busybox:1.28
  22. command:
  23. - sleep
  24. - "3600"
  25. name: busybox
  26. ---
  27. apiVersion: v1
  28. kind: Pod
  29. metadata:
  30. name: busybox2
  31. labels:
  32. name: busybox
  33. spec:
  34. hostname: busybox-2
  35. subdomain: busybox-subdomain
  36. containers:
  37. - image: busybox:1.28
  38. command:
  39. - sleep
  40. - "3600"
  41. name: busybox

鉴于上述服务 “busybox-subdomain” 和将 spec.subdomain 设置为 “busybox-subdomain” 的 Pod, 第一个 Pod 将看到自己的 FQDN 为 “busybox-1.busybox-subdomain.my-namespace.svc.cluster-domain.example”。 DNS 会为此名字提供一个 A 记录和/或 AAAA 记录,指向该 Pod 的 IP。 Pod “busybox1” 和 “busybox2” 都将有自己的地址记录。

EndpointSlice 对象可以为任何端点地址及其 IP 指定 hostname

说明: 由于 A 和 AAAA 记录不是基于 Pod 名称创建,因此需要设置了 hostname 才会生成 Pod 的 A 或 AAAA 记录。 没有设置 hostname 但设置了 subdomain 的 Pod 只会为 无头 Service 创建 A 或 AAAA 记录(busybox-subdomain.my-namespace.svc.cluster-domain.example) 指向 Pod 的 IP 地址。 另外,除非在服务上设置了 publishNotReadyAddresses=True,否则只有 Pod 准备就绪 才会有与之对应的记录。

Pod 的 setHostnameAsFQDN 字段

特性状态: Kubernetes v1.22 [stable]

当 Pod 配置为具有全限定域名 (FQDN) 时,其主机名是短主机名。 例如,如果你有一个具有完全限定域名 busybox-1.busybox-subdomain.my-namespace.svc.cluster-domain.example 的 Pod, 则默认情况下,该 Pod 内的 hostname 命令返回 busybox-1,而 hostname --fqdn 命令返回 FQDN。

当你在 Pod 规约中设置了 setHostnameAsFQDN: true 时,kubelet 会将 Pod 的全限定域名(FQDN)作为该 Pod 的主机名记录到 Pod 所在名字空间。 在这种情况下,hostnamehostname --fqdn 都会返回 Pod 的全限定域名。

说明:

在 Linux 中,内核的主机名字段(struct utsnamenodename 字段)限定最多 64 个字符。

如果 Pod 启用这一特性,而其 FQDN 超出 64 字符,Pod 的启动会失败。 Pod 会一直出于 Pending 状态(通过 kubectl 所看到的 ContainerCreating), 并产生错误事件,例如 “Failed to construct FQDN from Pod hostname and cluster domain, FQDN long-FQDN is too long (64 characters is the max, 70 characters requested).” (无法基于 Pod 主机名和集群域名构造 FQDN,FQDN long-FQDN 过长,至多 64 个字符,请求字符数为 70)。 对于这种场景而言,改善用户体验的一种方式是创建一个 准入 Webhook 控制器, 在用户创建顶层对象(如 Deployment)的时候控制 FQDN 的长度。

DNS 策略可以逐个 Pod 来设定。目前 Kubernetes 支持以下特定 Pod 的 DNS 策略。 这些策略可以在 Pod 规约中的 dnsPolicy 字段设置:

  • ClusterFirst“: 与配置的集群域后缀不匹配的任何 DNS 查询(例如 “www.kubernetes.io”) 都会由 DNS 服务器转发到上游名称服务器。集群管理员可能配置了额外的存根域和上游 DNS 服务器。 参阅相关讨论 了解在这些场景中如何处理 DNS 查询的信息。
  • ““: 对于以 hostNetwork 方式运行的 Pod,应将其 DNS 策略显式设置为 “ClusterFirstWithHostNet“。否则,以 hostNetwork 方式和 "ClusterFirst" 策略运行的 Pod 将会做出回退至 "Default" 策略的行为。
    • 注意:这在 Windows 上不支持。 有关详细信息,请参见。
  • None“: 此设置允许 Pod 忽略 Kubernetes 环境中的 DNS 设置。Pod 会使用其 dnsConfig 字段所提供的 DNS 设置。 参见 Pod 的 DNS 配置节。

说明:

“Default” 不是默认的 DNS 策略。如果未明确指定 dnsPolicy,则使用 “ClusterFirst”。

Pod 的 DNS 配置

特性状态: Kubernetes v1.14 [stable]

Pod 的 DNS 配置可让用户对 Pod 的 DNS 设置进行更多控制。

dnsConfig 字段是可选的,它可以与任何 dnsPolicy 设置一起使用。 但是,当 Pod 的 dnsPolicy 设置为 “None“ 时,必须指定 dnsConfig 字段。

用户可以在 dnsConfig 字段中指定以下属性:

  • nameservers:将用作于 Pod 的 DNS 服务器的 IP 地址列表。 最多可以指定 3 个 IP 地址。当 Pod 的 dnsPolicy 设置为 “None“ 时, 列表必须至少包含一个 IP 地址,否则此属性是可选的。 所列出的服务器将合并到从指定的 DNS 策略生成的基本名称服务器,并删除重复的地址。

  • searches:用于在 Pod 中查找主机名的 DNS 搜索域的列表。此属性是可选的。 指定此属性时,所提供的列表将合并到根据所选 DNS 策略生成的基本搜索域名中。 重复的域名将被删除。Kubernetes 最多允许 6 个搜索域。

  • options:可选的对象列表,其中每个对象可能具有 name 属性(必需)和 value 属性(可选)。 此属性中的内容将合并到从指定的 DNS 策略生成的选项。 重复的条目将被删除。

以下是具有自定义 DNS 设置的 Pod 示例:

service/networking/custom-dns.yaml

  1. apiVersion: v1
  2. kind: Pod
  3. metadata:
  4. namespace: default
  5. name: dns-example
  6. spec:
  7. containers:
  8. - name: test
  9. image: nginx
  10. dnsPolicy: "None"
  11. dnsConfig:
  12. nameservers:
  13. - 192.0.2.1 # 这是一个示例
  14. searches:
  15. - ns1.svc.cluster-domain.example
  16. - my.dns.search.suffix
  17. options:
  18. - name: ndots
  19. value: "2"
  20. - name: edns0

创建上面的 Pod 后,容器 test 会在其 /etc/resolv.conf 文件中获取以下内容:

对于 IPv6 设置,搜索路径和名称服务器应按以下方式设置:

  1. kubectl exec -it dns-example -- cat /etc/resolv.conf

输出类似于:

特性状态: Kubernetes 1.26 [beta]

Kubernetes 本身不限制 DNS 配置,最多可支持 32 个搜索域列表,所有搜索域的总长度不超过 2048。 此限制分别适用于节点的解析器配置文件、Pod 的 DNS 配置和合并的 DNS 配置。

说明:

早期版本的某些容器运行时可能对 DNS 搜索域的数量有自己的限制。 根据容器运行环境,那些具有大量 DNS 搜索域的 Pod 可能会卡在 Pending 状态。

众所周知 containerd v1.5.5 或更早版本和 CRI-O v1.21 或更早版本都有这个问题。

  • 在 Windows 节点上运行的 Pod 不支持 ClusterFirstWithHostNet。 Windows 将所有带有 . 的名称视为全限定域名(FQDN)并跳过全限定域名(FQDN)解析。
  • 在 Linux 上,有一个 DNS 后缀列表,当解析全名失败时可以使用。 在 Windows 上,你只能有一个 DNS 后缀, 即与该 Pod 的命名空间相关联的 DNS 后缀(例如:mydns.svc.cluster.local)。 Windows 可以解析全限定域名(FQDN),和使用了该 DNS 后缀的 Services 或者网络名称。 例如,在 default 命名空间中生成一个 Pod,该 Pod 会获得的 DNS 后缀为 default.svc.cluster.local。 在 Windows 的 Pod 中,你可以解析 kubernetes.default.svc.cluster.localkubernetes, 但是不能解析部分限定名称( 和 kubernetes.default.svc)。

有关管理 DNS 配置的指导, 请查看配置 DNS 服务