欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 新闻 > 资讯 > kubernetes-循序渐进了解coredns

kubernetes-循序渐进了解coredns

2025/1/7 17:38:49 来源:https://blog.csdn.net/weixin_43230594/article/details/144941483  浏览:    关键词:kubernetes-循序渐进了解coredns

文章目录

  • 概要
  • 基础知识
  • Kubernetes 集群中对对象名称的 DNS 流量
  • 解析 Kubernetes 集群外的名称的 DNS 流量
  • CoreDNS 如何确定向哪个本地 DNS 请求解析?
  • 修改 CoreDNS 的配置

概要

CoreDNS 是 Kubernetes 的核心组件之一。只有在 Kubernetes 集群中安装了 容器网络接口(CNI) 后,CoreDNS 的 Pod 才会启动并运行。CoreDNS 的作用是为 Kubernetes 集群中的对象将名称解析为 IP 地址。

基础知识

CoreDNS 是 Kubernetes 集群中的核心组件,作为一个 Pod 运行在集群中的某个节点上。这个节点可以是主节点,也可以是工作节点。默认情况下,有两个 CoreDNS Pod 分布在两个不同的节点上。这种设置为集群中的名称到 IP 的功能提供了冗余和负载均衡。CoreDNS 是通过一个 Deployment 对象部署的,因此可以根据需要进行扩展或缩减。CoreDNS Pod 是通过一个服务(Service)访问的,这个服务拥有一个虚拟 IP 地址,用于在这些 Pod 之间平衡流量负载。

CoreDNS Pod 属于 kube-system 命名空间,其配置存储在同一命名空间中的一个名为 coredns 的 ConfigMap 中。默认配置类似于以下内容:

kubeuser@k8smaster:~$ kubectl get cm coredns -n kube-system -o yaml
apiVersion: v1
data:Corefile: |.:53 {log #注意,默认的configMap中,没有log,记得加上,以便于后面的调试errorshealth {lameduck 5s}readykubernetes cluster.local in-addr.arpa ip6.arpa {pods insecurefallthrough in-addr.arpa ip6.arpattl 30}prometheus :9153forward . /etc/resolv.conf {max_concurrent 1000}cache 30loopreloadloadbalance}
kind: ConfigMap
metadata:creationTimestamp: "2024-11-18T09:56:27Z"name: corednsnamespace: kube-systemresourceVersion: "255"uid: 38653b7a-05c2-4f83-bbc4-b7b1e4397f17

为了更好地理解 CoreDNS 的行为并便于故障排查,可以在配置中添加 log 选项。这样,CoreDNS 进行的所有解析操作都会被记录到日志中。随后,通过执行 kubectl logs 命令,可以查看这些记录,从而更直观地分析和排查问题。

Kubernetes 集群中对对象名称的 DNS 流量

CoreDNS 是 Kubernetes 集群的默认 DNS 解析器,其服务通常监听在 UDP 端口 53。如果需要捕获 CoreDNS Pod 的所有流入和流出流量,建议将 CoreDNS 的副本数缩减为 1 个 Pod,从而确保所有流量都通过该实例。

接下来,我们通过一个示例来演示如何捕获 DNS 流量。在示例中,一个 Pod 将发送 DNS 请求,同时我们利用 Cilium 捕获相关流量以便更好地分析和理解。这里使用的 Pod 是 netshoot,它集成了多种网络工具(在本例中,我们将使用 nslookup 工具):

kubeuser@k8smaster:~$ kubectl run netshoot --image=nicolaka/netshoot -it -- bash
netshoot:~# nslookup nginx-service.default.svc.cluster.local
Server:         10.96.0.10
Address:        10.96.0.10#53Name:   nginx-service.default.svc.cluster.local
Address: 10.98.167.203

在 netshoot Pod 中,我们将集群中某个服务(nginx-service)的名称解析为其对应的 IP 地址:10.98.167.203,而 CoreDNS 服务的 IP 地址为 10.96.0.10。

与此同时,在另一个终端中,我们通过 Cilium 捕获了这段流量。为了监控该流量,需要确保使用运行在与 CoreDNS Pod 同一节点上的 Cilium 代理。捕获结果如下所示:

kubeuser@k8smaster:~$  kubectl exec -it cilium-kbwq7 -n kube-system -- cilium monitor |grep :53
Defaulted container "cilium-agent" out of: cilium-agent, config (init), mount-cgroup (init), apply-sysctl-overwrites (init), mount-bpf-fs (init), clean-cilium-state (init), install-cni-binaries (init)
-> endpoint 118 flow 0x5a3b1331 , identity 27258->53291 state new ifindex lxc29d980acbf33 orig-ip 10.0.1.127: 10.0.1.127:51805 -> 10.0.1.47:53 udp
-> endpoint 139 flow 0x0 , identity 53291->27258 state reply ifindex lxc78a71191cd5e orig-ip 10.0.1.47: 10.96.0.10:53 -> 10.0.1.127:51805 udp

netshoot Pod 的 IP 地址为 10.0.1.127,而 CoreDNS Pod 的 IP 地址为 10.0.1.47。我们可以观察到,DNS 请求通过端口 53 发送到 CoreDNS Pod,随后 CoreDNS 将解析结果返回给 netshoot Pod。

需要注意的是,即使该服务名称不存在,数据流看起来依然相同。这是因为 CoreDNS 不会进一步尝试解析此类 DNS 请求,其原因在于 CoreDNS 对域名 svc.cluster.local 具有权威性。如果 CoreDNS 无法解析,那么其他任何 DNS 解析器也无法解析。此外,执行 nslookup nginx-service.default 时,CoreDNS 会自动尝试解析其管理范围内的多个后缀。

为了完整地展示整个过程,以下是与该 DNS 流量相关的 CoreDNS 日志内容:

kubectl logs --namespace=kube-system -l k8s-app=kube-dns
...
[INFO] 10.0.1.127:54548 - 57210 "A IN nginx-service.default.svc.cluster.local. udp 57 false 512" NOERROR qr,aa,rd 112 0.000152805s
...

我们可以看到 netshoot Pod 的 IP 地址、需要解析的名称以及解析结果。NOERROR 表示解析成功完成,最后还显示了解析所花费的时间。如果解析失败,将看到 NXDOMAIN 替代 NOERROR。

解析 Kubernetes 集群外的名称的 DNS 流量

当一个 Pod 尝试解析 CoreDNS 无权限管理的域名时,会发生不同的处理方式。这些域名可能包括 Ingress 规则中指定的任意 URL,或者集群的 API Server 名称。这类名称通常需要通过集群外的 DNS 服务器进行解析,以便流量能够正确路由到 Kubernetes 集群外。

在某些架构中可能存在类似场景:例如,一个 Pod 尝试连接到 Kubernetes 集群外的 Oracle 数据库。在这种情况下,数据库的名称解析将由位于本地网络中的外部 DNS 服务器完成,而非 CoreDNS。

接下来,让我们再次使用 netshoot Pod 来进一步探索这种情况并观察具体行为:

netshoot:~# nslookup pcicvs.mydomain.com
Server:         10.96.0.10
Address:        10.96.0.10#53
Name:   pcicvs.mydomain.com
Address: 172.19.2.5

名称已经被解析,但从 Pod 的视角看不到太多有关解析过程的细节。Pod 只知道 CoreDNS 服务的 IP 地址是 10.96.0.10。如果查看该 netshoot Pod 内部的 DNS 配置,会看到如下内容:

netshoot:~# cat /etc/resolv.conf
search default.svc.cluster.local svc.cluster.local cluster.local pci.co.id
nameserver 10.96.0.10
options ndots:5

你可以看到 CoreDNS 有权管理的域名和 CoreDNS 服务的 IP 地址,仅此而已!接下来,我们来看看 Cilium 能够看到的关于这类流量的信息:

kubectl exec -it -n kube-system cilium-kbwq7 -- cilium monitor|grep ":53"
-> endpoint 1202 flow 0x226176d0 , identity 27258->53291 state new ifindex lxc0c634b736227 orig-ip 10.0.1.127: 10.0.1.127:39445 -> 10.0.1.47:53 udp
-> stack flow 0x350dacf7 , identity 53291->world state new ifindex 0 orig-ip 0.0.0.0: 10.0.1.47:60021 -> 172.19.5.5:53 udp
-> endpoint 1202 flow 0x0 , identity world->53291 state reply ifindex lxc0c634b736227 orig-ip 172.19.5.5: 172.19.5.5:53 -> 10.0.1.47:60021 udp
-> endpoint 1320 flow 0x0 , identity 53291->27258 state reply ifindex lxc15973d8c3d02 orig-ip 10.0.1.47: 10.96.0.10:53 -> 10.0.1.127:39445 udp

在这个案例中,你可以看到,由于 CoreDNS 并没有管理 mydomain.com 的权限,它会向它已知的 DNS 请求解析操作。在这个例子中,它使用了 kubernetes cluster外部的LAN DNS(172.19.5.5)来完成解析,同时为了匿名化我们本地网络中的真实 DNS IP 地址,使用了这个公共 DNS。因此,DNS 流量的路径如下所示:
Netshoot Pod (10.0.1.127) -> CoreDNS Pod (10.0.1.47) -> 本地 DNS (172.19.5.5),然后沿同一路径返回。
需要注意的是,这是 Kubernetes 集群中 Cilium 代理所观察到的流量路径,但实际上我们的本地 DNS 可能还会向其他 DNS 请求解析操作(这就是标准的 DNS 递归解析机制)。
在 CoreDNS 的日志中,可以看到这个名称解析操作已经成功完成,并且解析所花费的时间比集群内部解析稍长。这是因为流量需要经过本地 DNS,多了一些处理步骤。

 kubectl logs --namespace=kube-system -l k8s-app=kube-dns -f
...
[INFO] 10.0.1.127:45895 - 16502 "A IN pcicvs.mydomain.com. udp 34 false 512" NOERROR qr,aa,rd,ra 66 0.000089403s
...

CoreDNS 如何确定向哪个本地 DNS 请求解析?

CoreDNS 知道向哪个本地 DNS 请求解析的答案来自其配置文件中的 ConfigMap,例如其中的 forward 插件配置:forward . /etc/resolv.conf。这是本文开头提到的配置。
你是否可以查看 /etc/resolv.conf 文件的内容及其来源?
Pods 的设计初衷是提供特定功能,因此其容器镜像通常仅包含实现该功能所需的最小工具集。这意味着容器中可能没有像 shell 这样的工具,从而减少了潜在攻击面。例如,CoreDNS 镜像就不包含 shell。尽管如此,你仍可以通过一些方法“模拟”出一个 shell 来探索容器中的文件:

kubectl debug -it coredns-58f6d65b75-zgfw6 -n kube-system --image=nicolaka/netshoot --target=coredns  --image-pull-policy=IfNotPresent
coredns-58f6d65b75-zgfw6 ~ cat /proc/1/root/etc/resolv.conf

通过 kubectl debug,你可以使用你选择的其他镜像进入 Pod,同时仍然可以访问原始 CoreDNS 容器的内容。在目标 Pod coredns-58f6d65b75-zgfw6中,CoreDNS 容器的名称是 coredns。然后,你可以查看 /proc/1/root/etc/resolv.conf 的内容,这正是我们要找的文件。
顺便说一下,CoreDNS 的 ConfigMap 也作为文件存储在这个容器中,路径是 /proc/1/root/etc/coredns/Corefile。
问题是,这个 resolv.conf 文件是从哪里来的?其实很简单:它来自于托管 CoreDNS Pod 的节点的 /etc/resolv.conf 文件!我做了一个测试,往节点的 /etc/resolv.conf 文件中添加了一条记录,然后重新部署了 CoreDNS,结果确实如预期那样,添加的记录也出现在了 CoreDNS 容器的 /etc/resolv.conf 中!记住,CoreDNS Pod 可以托管在集群中的任何节点上。因此,如果你想做这个测试,你可以强制 Pod 调度到特定的节点,或者修改所有节点的 /etc/resolv.conf 文件。在实验室中,使用几台节点来测试应该会非常容易。

修改 CoreDNS 的配置

到目前为止,已经了解了 CoreDNS 的工作原理,但有没有办法可以从集群内部控制名称解析呢?
可能的架构中,Pod 需要使用由集群的 Ingress 控制器管理的 URL。在这种情况下,如前所述,名称解析将不会由 CoreDNS 完成,而是由你的本地 DNS 处理。在你的组织中,这些本地 DNS可能由其他团队管理,你无法对它们进行控制。如果由于某些原因这些本地 DNS 无法访问,那么集群中的应用程序将无法正常工作。
如果希望集群中的 Pod 能够独立解析这些 URL 名称,那么你可以通过使用 CoreDNS 的 hosts 插件在 CoreDNS 中静态映射这些名称。
只需编辑 CoreDNS 的 ConfigMap,将 hosts 插件添加到 forward 插件之前即可(在 CoreDNS 的术语中,这些块或指令被称为插件),如下所示:
hosts插件示范内容

        hosts {172.19.2.5 pcicvs.mydomain.comfallthrough}

coredns congfigMap完整内容

kubeuser@k8smaster:/etc$  kubectl get cm coredns -n kube-system -o yaml
apiVersion: v1
data:Corefile: |.:53 {logerrorshealth {lameduck 5s}readykubernetes cluster.local in-addr.arpa ip6.arpa {pods insecurefallthrough in-addr.arpa ip6.arpattl 30}prometheus :9153hosts {172.19.2.5 pcicvs.mydomain.comfallthrough}forward . /etc/resolv.conf {max_concurrent 1000}cache 30loopreloadloadbalance}
kind: ConfigMap
metadata:creationTimestamp: "2024-11-18T09:56:27Z"name: corednsnamespace: kube-systemresourceVersion: "5271081"uid: 38653b7a-05c2-4f83-bbc4-b7b1e4397f17

不久后,如果你从 netshoot Pod 再次尝试解析pcicvs.mydomain.com 的名称,会发现它返回在 hosts 插件中设置的 IP 地址。
因此,CoreDNS 能够直接解析该 URL,而无需向 /etc/resolv.conf 中配置的本地 DNS 发送请求。参数 fallthrough 的作用是,如果 hosts 插件中没有静态映射,则会像之前一样将 DNS 请求转发给本地 DNS 进行解析。

这一更改是动态生效的(无需重启),并且通过 fallthrough 参数,如果没有找到匹配的映射,解析仍然会交由本地 DNS 处理,从而能够解析外部名称或 URL。通过这种方式,你可以根据需求逐步添加由 CoreDNS 处理的域名映射。这不仅能够加快名称解析的速度,还可以让你对解析过程保持更好的控制。然而,这样做的缺点是,当新的应用(例如使用 Ingress 规则的应用)部署到集群时,你需要及时更新这些映射。

需要注意的是,在 CoreDNS 配置中,fallthrough 参数用于决定当一个插件无法处理 DNS 请求时,是否将请求传递给下一个插件。
因此将 hosts 插件放在 forward 插件之后没有意义,因为 forward 插件没有 fallthrough 参数,无法将请求传递回 hosts 插件。因此,hosts 插件的映射就永远不会被访问到。为了确保 hosts 插件能够生效,它应该放在 forward 插件之前。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com