Kubernetes - Services 和 Pods 的 DNS

Table of Contents

Kubernetes 会为 services 和 pods 创建 DNS 记录。你可以通过统一的 DNS 名字代替 IP 地访问 services。

1. 介绍

Kubernetes DNS 集群上的调度 DNS Pod 和 Service,并配置 kubelet 告诉各个容器使用 DNS 的服务 IP 来解析 DNS 名字。

集群中定义的每一个 Service 都会赋予一个 DNS 名字。默认情况下,客户端 Pod 的 DNS 搜索列表包含 Pod 的命名空间和集群默认域。

1.1. Services 的命名空间

DNS 的查询结果可能因为 Pod 空间而返回不同。未指定命名空间的 DNS 查询仅限于 Pod 所在空间。在 DNS 查询中指定命名空间来访问其他空间的服务。

比如说,一个 Pod 在 test 空间。 data service 在 prod 空间。查询 data 不会返回结果,因为他调用的是 test 空间的 Pod。

改成 data.prod 会返回预期的结果。

DNS 查询会在 Pod 的 /etc/resolv.conf 中扩展。kubelet 会为每一个 Pod 设置这个文件。比如,查询 data 会被扩展成 data.test.cluster.localsearch 选项的值用来扩展查询。学习更多的 DNS 查询,请查看 resolv.conf 手册

nameserver 10.32.0.10
search <namespace>.svc.cluster.local svc.cluster.local cluster.local
options ndots:5

总之, test 空间中的 Pod 可以使用 data.prod 或者 data.prod.svc.cluster.local 来成功解析。

1.2. DNS 记录

哪些对象会获得 DNS 记录?

  1. Services
  2. Pods

下面各节详细介绍了受支持的记录类型和布局。其它可行的布局、名字或者查询都视为实现细节,可能会在没有警告的情况下更改。 有关最新的规范,查看 Kubernetes 基于 DNS 的服务发现

2. Services

2.1. A/AAAA 记录

“正常的”(不是 headless)Service 会被分配一个 DNS A 或者 AAAA 记录,值取决于 service 的 IP 集合。名字的格式为: my-svc.my-namespace.svc.cluster-domain.example ,会被解析为 Service 的集群 IP。

“Headless”(没有集群 IP )Services 也会被分配一个 DNS A 或者 AAAA 记录,值取决于 service 的 IP 集合,名字格式为: my-svc.my-namespace.svc.cluster-domain.example ,与正常 Service 不同在于,他解析的结果是 Service 的 IP 集合。 客户端要从负责从集合中选择 IP(可能使用轮询策略)。

也就是说,正常 service DNS 解析的结果是一个 IP(由 service 的策略来控制如何从一堆的 endpoint 中选择一个),而 headless service 是把集合返回给客户端,客户端需要自己选择。

2.2. SRV 记录

SRC 记录是为正常或者 headless service 一部分的命名端口创建的。为每一个命名端口,SRV 记录记录的格式为: my-port-name.my-port-protocol.my-svc.my-namespace.svc.cluster-domain.example 。对于一个正常的 service,解析的端口号和域名: my-svc.my-namespace.svc.cluster-domain.example

对于 headless service,会被解析成多个答案,每个支持的 service 都会有一个,并且包含端口号和 Pod 域名的格式为: auto-generated-name.my-svc.my-namespace.svc.cluster-domain.example

3. Pods

3.1. A/AAAA 记录

通常情况下一个 Pod 有如下的 DNS 解析: pod-ip-address.my-namespace.pod.cluster-domain.example

比如,如果一个 pod 在 default 空间下, IP 地址是 172.17.0.3,集群域名为 cluster.local ,那么 Pod 的 DNS 名为 172-17-0-3.default.pod.cluster.local

3.2. Pod 的主机名和子域字段

当 Pod 被创建的时候, hostname 由 Pod 的 metadata.name 的值来决定。

Pod spec 中有一个可选的 hostname 字段,用于指定 Pod 的主机名。指定之后,他的优先级要比 Pod 的名字要高。

Pod 还有一个可选的 subdomain 字段,用来指定他的子域。比如说, Pod hostnamefoosubdomainbar , 所在空间为 my-namespace ,他的 FQDN 为: foo.bar.my-namespace.svc.cluster-domain.example

如果是 headless service,Pod 的域名会带上序号。比如 busybox-1 busybox-2

3.3. Pod 的 setHostnameAsFQDN 字段

FEATURE STATE: Kubernetes v1.22 [stable]

TODO

3.4. Pod 的 DNS 策略

可以对每一个 Pod 指定 DNS 策略。目前 Kubernetes 支持如下的 pod-specific DNS 策略。策略由 Pod 的 dnsPolicy 字段来指定。

  • default 从 Pod 运行节点继承解析配置,从 关联的讨论 查看更多;
  • ClusterFirst 与集群配置不匹配的 DNS 查询,比如 www.kubernetes.io 会转发到节点继承的上游 nameserver。 集群管理员可能配置了额外的存根域和上游 DNS 服务器。关于 DNS 查询如何处理这些场景,查看 相关讨论
  • ClusterFirstWithHostNet 对于使用 hostNetwork 的运行中 Pod,你需要显式的指定 DNS 策略为 ClusterFirstWithHostNet
  • None 他允许忽略从 Kubernetes 环境中的 DNS 设置。所有的 DNS 设置都应该使用 Pod Spec 中的 dnsConfig 来提供;

举例:

apiVersion: v1
kind: Service
metadata:
  name: default-subdomain
spec:
  selector:
    name: busybox
  clusterIP: None
  ports:
  - name: foo # Actually, no port is needed.
    port: 1234
    targetPort: 1234
---
apiVersion: v1
kind: Pod
metadata:
  name: busybox1
  labels:
    name: busybox
spec:
  hostname: busybox-1
  subdomain: default-subdomain
  containers:
  - image: busybox:1.28
    command:
      - sleep
      - "3600"
    name: busybox
---
apiVersion: v1
kind: Pod
metadata:
  name: busybox2
  labels:
    name: busybox
spec:
  hostname: busybox-2
  subdomain: default-subdomain
  containers:
  - image: busybox:1.28
    command:
      - sleep
      - "3600"
    name: busybox

如果在于 Pod 相同的命名空间中存在 headless 服务并且与子域具有相同的名称。集群的 DNS 服务器也为 Pod 的完全限定域名返回 A 或 AAAA 记录。比如说,给定的 Pod 的 hostname 设置成为 busybox-1 子域设置成为 default-subdomain ,相同的空间下 headless service 命名为 default-subdomain ,Pod 会看到自己的 FQDN 是 busybox-1.default-subdomain.my-namespace.svc.cluster-domain.example DNS 以该名称提供 A 或者 AAAA 记录,指向 Pod 的 IP。 busybox1busybox2 有独立的 A 或者 AAAA 记录。

Endpoints 对象可以制定任何 endpoint 地址的 hostname ,跟他的 IP 一起。

注意: 由于 A 和 AAAA 不会为 Pod 名字创建, 所以在创建 A 或者 AAAA 记录时 hostname 是必要的。Pod 没有 hostname 但是有 subdomain 会只会为 headless service 创建 A 或者 AAAA 记录(default-subdomain.my-namespace.svc.cluster-domain.example), 指向 Pod 的 IP 地址。此外,除非在 Service 上设置了 publishNotReadyAddresses=True ,否则 Pod 需要就绪之后才能有记录。

3.5. Pod 的 DNS 配置

FEATURE STATE: Kubernetes v1.14 [stable]

Pod 的 DNS 配置允许用户更多的控制 Pod 的 DNS 设置。

dnsConfig 字段是可选的,他可以和任何的 dnsPolicy 设置。然而,当 Pod 的 dnsPolicy 被设置为 NonednsConfig 字段必须要指定。

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

  • nameservers : Pod 使用的 DNS 服务器列表。最多可以指定 3 个 IP 地址。当 Pod 的 dnsPolicy 设置为 None ,至少需要指定一个 IP 地址,否则属性是可选的。 列出的服务器将与从指定 DNS 策略生成的基本名称服务器合并,并删除重复地址;
  • searches : 用于在 Pod 中查找主机名的 DNS 搜索域列表。这个属性是可选的。当被指定时,提供的列表会被与基础的搜索域合并。重复的域名会被删除。Kubernetes 最多允许 6 个搜索域;
  • options : 一个可选的对象列表,其中每个对象可能有一个 name 属性(必需)和一个 value 属性(可选)。同样会跟已有的 option 合并,重复的会被移除;

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

apiVersion: v1
kind: Pod
metadata:
  namespace: default
  name: dns-example
spec:
  containers:
    - name: test
      image: nginx
  dnsPolicy: "None"
  dnsConfig:
    nameservers:
      - 1.2.3.4
    searches:
      - ns1.svc.cluster-domain.example
      - my.dns.search.suffix
    options:
      - name: ndots
        value: "2"
      - name: edns0

当上面的 Pod 被创建时, test 容器的 /etc/resolv.conf 文件会得到下面的内容:

nameserver 1.2.3.4
search ns1.svc.cluster-domain.example my.dns.search.suffix
options ndots:2 edns0

对于 IPv6 设置,搜索路径和 nameserver 可能会被设置为:

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

输出类似:

nameserver fd00:79:30::a
search default.svc.cluster-domain.example svc.cluster-domain.example cluster-domain.exampleoptions ndots:5

3.6. 扩展 DNS 配置

FEATURE STATE: Kubernetes 1.22 [alpha]

默认情况下,Pod 的 DNS 配置,Kubernetes 允许最多 6 个搜索域并且最多 256 个字符。

如果为 kube-apiserver 和 kubelet 开启了 ExpandedDNSConfig 特性,它会允许最多 32 个搜索域和最多 2048 个字符。

3.7. 功能可用性

Pod 的 DNS 配置和 DNS 策略 None 可用性如下所示:

K8s version 功能支持
1.14 Stable
1.10 Beta(on by default)
1.9 Alpha

4. 实践

4.1. /etc/resolv.conf 中的 ndots

如上,每个集群中 Pod 的 /etc/resolv.conf 中的内容大概为

nameserver 10.32.0.10
search <namespace>.svc.cluster.local svc.cluster.local cluster.local
options ndots:5

其中 nameserver 和 search 分别表示 DNS 地址和搜索域。

比较关键的是 ndots 的含义。他表示请求域名的中的 . 的数量,当域名的 . 数量小于 5 时,会从 search 从依次填充后 缀再从 DNS 服务器中查找。当大于等于 5 时,会直接对域名本身进行解析,然后再在搜索域列表中搜索。什么意思呢?

假设你要访问 api.github.com ,点数小于 5, 那他的查找顺序为

  1. api.github.com.<namespace>.svc.cluster.local
  2. api.github.com.svc.cluster.local
  3. api.github.com.cluster.local
  4. api.github.com

在这种情况下,访问外部域名会有大量的无效查询导致 dnslookup 变慢,也会增加 CoreDNS 的压力。

优化的办法是改小 ndots 的值, ndots:2 可能是一个好的选择。为什么呢?search 域在 K8s 集群中是为了做服务发现,上面文章内 容里有说明原理。在实际使用集群时,会用到的基本就两种情况:

  • 本空间短域名访问 svc:port
  • 跨空间短域名访问 svc.namespace:port

所以 ndots 为 2 时,足够覆盖这两种场景。如何实现自动修改呢?

  1. pod 的 yaml 中 dnsConfig 可以修改 resolv 的内容;
  2. K8s 提供了 adminssion controller,用于在 Pod 的创建中做一些额外的事情。可以利用 adminession controller 的机制, 来自动化完成 dns 的注入。具体可见 自定义 Kubernetes 准入控制器

更多:https://pracucci.com/kubernetes-dns-resolution-ndots-options-and-why-it-may-affect-application-performances.html

First created: 2021-02-09 16:50:29
Last updated: 2022-12-11 Sun 12:49
Power by Emacs 29.0.91 (Org mode 9.6.6)