CoreDNS 是一个 DNS 服务器。基于 Go 语言开发。由于其灵活性,可以在多种不同的环境中使用。CoreDNS 已在 Apache 2 许可证版本获得许可,并且完全开源。其已成为 Kubernetes 1.13 以后版本的默认 DNS 服务。如今,当我们使用托管 Kubernetes 集群或为应用程序工作负载自行管理集群时,通常只需要关注应用程序本身,而无须过多关注 Kubernetes 提供的服务或如何利用它们。DNS 解析是任何应用程序的基本要求,因此我们需要确保它正常工作。
本文的将不深入探讨 CoreDNS,而是解释 DNS 如何在 Kubernetes 集群中工作,CoreDNS 包含什么以及 Corefile 如何使用插件。
在讨论 CoreDNS 之前,我们先来看一下 Kubernetes 是如何在集群中实现 DNS 交互的。假设有一个 Pod,即 Service A 想要与另一个 Pod Service B 进行通信。通常情况下,我们可以通过在 /etc/hosts 文件中将对方的地址信息更新上去来实现这一点,如下图所示。
但在实际的业务场景中,我们所面临的并不是少量的服务交互。如果我们处理的是每分钟都在创建和销毁的数百个甚至上万个 Pod ,并且 Pod 之间也不停止的互访,那该怎么办?
在这种情况下,我们不在 /etc/hosts 中创建条目(这不是合适的解决方案),而是将这些条目移动到集中式 DNS 服务器,即 10.10.0.10,如下图所示。现在,我们需要在 Pod 中的某个位置将这个 IP 指定为 nameserver,该位置恰好位于 /etc/resolv.conf 文件中。
每次创建新 Pod 时,K8s 都会在 DNS 服务器 中更新新加入的 Pod 地址信息,并在新 Pod 的 /etc/resolv.conf 文件中更新相应的条目,当然,这些清单列表指向 DNS 服务器的 IP 地址,如下图所示:
正如我上面所述,我们将 /etc/hosts 的条目更改为集中式 DNS 服务器。嗯,但有一部分是正确的。毕竟,DNS 不会像我们在 Pods 中编辑 /etc/hosts 文件那样输入 Pods(格式:<podu name><IP>)。相反,它通过将 Pod 的 IP 地址中的点替换为破折号来创建新的主机名,如主机名 “10-10-10-1(其格式为:<hostname><IP>)”。详情如下图所示:
基于上述的解析,我们对 Pod 之间的交互有了简单的认知。然而,在实际的业务场景中,Pod 通过 K8s 集群中的服务进行通信,CoreDNS 为这些服务设置记录(默认情况下,Pod 条目被禁用,但我们可以在 CoreDNS 的 Ccorefile 中启用它们)。
虽然 CoreDNS 和 Kube DNS 最终执行相同的任务,但在实现中存在一些影响资源消耗和性能的关键差异。我们可以在 coreDNS 官方文档中详细了解这一点。
CoreDNS 自 1.9 版开始在 Kubernetes 中可用。它是一个快速灵活的 DNS 服务器。因此,意味着大家可以自由使用 DNS 数据,可以使用一系列插件来使用这些数据。如果某些功能不是现成的,我们可以通过编写插件来实现,毕竟,它是基于 Go 语言写的。
我们将 CoreDNS 部署为集群中 Kube 系统命名空间中的一个部署对象,该集群中有一个名为 “kube dns” 的服务。它需要一个配置文件,我们将位于 /etc/coredns/corefile 的文件称之为 “Corefile”。
其实,从本质上来讲,Corefile 由多种不同的插件组成,其往往主要用于错误处理、报告运行状况、监控指标、缓存等等。
使 coreDNS 与 Kubernetes 协同工作的插件是 Kubernetes 插件。在 Kubernetes 插件中,设置了 Kubernetes 集群的顶级域(cluster.local)。此外,默认情况下,它会监视新服务。对于 Pod,我们需要通过在集群中创建 “pods Pod Mode” 条目,在 Kubernetes 插件下的 Corefile 中启用 “Pod Mode”。如果创建了一个新对象,它会在 coreDNS 服务器中添加服务记录或 Pod。
Pods 的下一步是通过在 resolv.conf 文件中指定 nameserver来指向用于 DNS 解析的 coreDNS IP 地址。但是,应该是什么地址呢?
其实,我们不需要关心这个,因为 DNS 条目已经由 Kubelet 组件处理。
当我们在集群中安装 coreDNS 时,我们将其作为服务公开,因此 Kubelet 将该服务的 IP 地址配置为 Pods 中的名称服务器。
现在我们的问题是 Kubelet 是如何知道这一点的?
当然,我们可以在 Kubelet 配置文件中看到 coreDNS 服务器的条目,如上图所示。
除此之外,我们还可以配置 Kubelet 并作为服务运行,并在该服务文件中传递 ClusterDns IP 信息。
我们基于 Minikube 工具,来进行简要的阐述,其相关操作如下:
代码语言:javascript复制[administrator@JavaLangOutOfMemory ~] % minikube ssh
[administrator@JavaLangOutOfMemory ~] % cat /var/lib/kubelet/config.yaml
我们来看一下完整的 config.yaml 文件信息,具体如下所示:
代码语言:javascript复制[administrator@JavaLangOutOfMemory ~] % cat /var/lib/kubelet/config.yaml
apiVersion: kubelet.config.k8s.io/v1beta1
authentication:
anonymous:
enabled: false
webhook:
cacheTTL: 0S
enabled: true
x509:
clientCAFile: /var/lib/minikube/certs/ca.crt
authorization:
mode: Webhook
webhook:
cacheAuthorizedTTL: 0S
cacheUnAuthorizedTTL: 0S
clusterDNS:
- 10.10.0.10
clusterDomain: cluster.local
cpuManagerReconcilePeriod: 0s
evictionPressureTransitionPeriod: 0s
fileCheckFrequency: 0s
healthzBindAddress: 127.0.0.1
healthzPort: 10248
httpCheckFrequency: 0s
imageMinimunGCAge: 0s
kind: KubeletConfiguration
nodeStatusReportFrequency: 0s
nodeStatusUpdateFrequency: 0s
rotateCertificates: ture
runtimeRequestTimeout: 0s
staticPodPath: /etc/kubernetes/manifests
streamingConnectionIdleTimeout: 0s
syncFrequency: 0s
volumeStatsAggPeriod: 0s
然而,在我们实际的项目中,很多企业仍未使用托管 Kubernetes 服务,因此,在此我将讨论自建的 Kubernetes 集群,我们可以通过对任何 K8s 节点执行 ssh 来检查 Kubelet 服务中的 clusterDns 条目。下面是我们在 K8s 集群中使用的服务文件,如下所示:
代码语言:javascript复制[Unit]
Description=Kubernetes Kubelet
Documentation=https://github.com/GoogleCloudPlatform/kubernetes
After=docker.service
Requires=docker.service
[Service]
ExecStart=/usr/bin/kubelet
--allow-privileged=true
--cloud-provider=
--cluster-dns=10.10.0.10
--cluster-domain=cluster.local
--container-runtime=docker
--docker-endpoint=unix:///var/run/docker.sock
--network-plugin=cni
--cni-bin-dir=/opt/cni/bin
--cni-conf-dir=/etc/cni/net.d
--kubeconfig=/var/lib/kubelet/kubeconfig
--serialize-image-pulls=true
--tls-cert-file=/var/lib/kubernetes/kubernetes.pem
--tls-private-key-file=/var/lib/kubernetes/kubernetes-key.pem
--system-reserved=memory=19227Mi
--fail-swap-on=true
--runtime-cgroups=/systemd/system.slice
--kubelet-cgroups=/systemd/system.slice
--pod-infra-container-image=<dockerregistry/imagename:tag
--log-dir=/var/log/kubernetes
--logtostderr=false
--v=2
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
接下来,我们再来看一下 Kubernetes DNS 记录的相关内容情况,具体如下所示。基于其格式规范:
对于服务:svcname.namespace.type.rootDomain
对于 Pod:hostname.namespace.type.rootDomain
这里,我们举个简单的例子,如下所示:
对于服务:test-service.default.svc.cluster.local
对于 Pod: 10-10-10-1.default.pod.cluster.local
在 Corefile 中,我们在集群中将 Corefile 作为配置映射传递,以便它与 coreDNS 的部署对象保持解耦。我们可以在 “https://coredns.io/plugins/kubernetes/” 获得插件链列表。
代码语言:javascript复制.:53 {
errors
log
health {
lameduck 5s
}
ready
kubernetes cluster.local in-addr.arpa ip6.arpa {
pods insecure
fallthrough in-addr.arpa ip6.arpa
ttl 30
}
prometheus :9153
forward . /etc/resolv.conf
cache 30
loop
reload
loadbalance
}
让我们再来看下 Kubernetes 插件,基于 Kubernetes 插件,CoreDNS 将会从 Kubernetes 集群中读取区域数据。它实现了为 Kubernetes 基于 DNS 的服务发现定义的规范。其格式为以下:
代码语言:javascript复制Kubernetes ZONE {
pods POD-MODE
fallthrough ZONE
ttl time_in_sec
Kubernetes Plugin 块示例,具体可参考如下所示:
代码语言:javascript复制kubernetes cluster.local in-addr.arpa ip6.arpa {
pods insecure
fallthrough in-addr.arpa ip6.arpa
ttl 30
在 Kubernetes 插件中,我们可以在 https://coredns.io/plugins/kubernetes/ 查看更多的选项以供选择。
让我们再简要解析一下在上面的 Corefile 文件中核心参数含义及使用规范,具体如下所示:
pods POD-MODE:设置处理基于 IP 的 Pod A 记录的模式,例如10-10-10-1.default.POD.cluster.local。在 A 10.10.10.1中,提供此选项是为了在直接连接到 Pod 时方便使用 SSL 证书。
insecure:我们所使用的 Pod 模式的值,其往往会返回一个 Pod 记录。
fallthrough[ZONES…]:如果插件授权的区域中的查询返回结果,或者返回查询的 NXDOMAIN。当 DNS 没有所请求域的列表时,将创建 NXDOMAIN 响应。如果启用了 fallthrough,则插件不会在未找到记录时返回 NXDOMAIN ,而是将请求向下传递到插件链,该插件链可以包含另一个插件来处理查询。
ttl:允许我们为响应设置自定义 ttl 。默认值为 5 秒。允许的最小 TTL 为 0 秒,最大 TTL 为 3600 秒。将 TTL 设置为 0 将阻止缓存记录。
综上所述,我们阐述了 DNS 是如何在 Kubernetes 中发挥重要作用的。CoreDNS 通过利用 Kubernetes 插件与 Kubernetes 进行协作。基于各种插件组合,我们可以根据自己的实际业务场景进行定制。基于上述所述,使得大家能够尽可能去接触到 CoreDNS 世界奥妙,以助于大家在云原生生态中能够稳健翱翔。