一、什么是DNS?

域名系统(Domain Name System)是整个互联网的电话簿,它能够将可被人理解的域名翻译成可被机器理解 IP 地址,使得互联网的使用者不再需要直接接触很难阅读和理解的 IP 地址

域名系统在现在的互联网中非常重要,因为服务器的 IP 地址可能会经常变动,如果没有了 DNS,那么可能 IP 地址一旦发生了更改,当前服务器的客户端就没有办法连接到目标的服务器了,如果我们为 IP 地址提供一个『别名』并在其发生变动时修改别名和 IP 地址的关系,那么我们就可以保证集群对外提供的服务能够相对稳定地被其他客户端访问。

image-20230920174519689

DNS 其实就是一个分布式的树状命名系统,它就像一个去中心化的分布式数据库,存储着从域名到 IP 地址的映射。

1.1、工作原理

在我们对 DNS 有了简单的了解之后,接下来我们就可以进入 DNS 工作原理的部分了,作为用户访问互联网的第一站,当一台主机想要通过域名访问某个服务的内容时,需要先通过当前域名获取对应的 IP 地址。这时就需要通过一个 DNS 解析器负责域名的解析,下面的图片展示了 DNS 查询的执行过程:

image-20230920174550216

  • 本地的 DNS 客户端向 DNS 解析器发出解析 draveness.me 域名的请求;
  • DNS 解析器首先会向就近的根 DNS 服务器 . 请求顶级域名 DNS 服务的地址;
  • 拿到顶级域名 DNS 服务 me. 的地址之后会向顶级域名服务请求负责 dravenss.me. 域名解析的命名服务;
  • 得到授权的 DNS 命名服务时,就可以根据请求的具体的主机记录直接向该服务请求域名对应的 IP 地址;

DNS 客户端接受到 IP 地址之后,整个 DNS 解析的过程就结束了,客户端接下来就会通过当前的 IP 地址直接向服务器发送请求。

对于 DNS 解析器,这里使用的 DNS 查询方式是迭代查询,每个 DNS 服务并不会直接返回 DNS 信息,而是会返回另一台 DNS 服务器的位置,由客户端依次询问不同级别的 DNS 服务直到查询得到了预期的结果;另一种查询方式叫做递归查询,也就是 DNS 服务器收到客户端的请求之后会直接返回准确的结果,如果当前服务器没有存储 DNS 信息,就会访问其他的服务器并将结果返回给客户端。

1.2、域名层级

域名层级是一个层级的树形结构,树的最顶层是根域名,一般使用 . 来表示,这篇文章所在的域名一般写作 draveness.me,但是这里的写法其实省略了最后的 .,也就是全称域名(FQDN)dravenss.me.。

image-20230920174642976

根域名下面的就是 com、net 和 me 等顶级域名以及次级域名 draveness.me,我们一般在各个域名网站中购买和使用的都是次级域名、子域名和主机名了。

1.3、域名服务器

既然域名的命名空间是树形的,那么用于处理域名解析的 DNS 服务器也是树形的,只是在树的组织和每一层的职责上有一些不同。DNS 解析器从根域名服务器查找到顶级域名服务器的 IP 地址,又从顶级域名服务器查找到权威域名服务器的 IP 地址,最终从权威域名服务器查出了对应服务的 IP 地址。

 dig -t A jiujiu.tj.cn +trace

我们可以使用 dig 命令追踪 draveness.me 域名对应 IP 地址是如何被解析出来的,首先会向预置的 13 组根域名服务器发出请求获取顶级域名的地址:

; <<>> DiG 9.11.4-P2-RedHat-9.11.4-26.P2.el7 <<>> -t A jiujiu.tj.cn +trace
;; global options: +cmd
.                       733     IN      NS      a.root-servers.net.
.                       733     IN      NS      b.root-servers.net.
.                       733     IN      NS      c.root-servers.net.
.                       733     IN      NS      d.root-servers.net.
.                       733     IN      NS      e.root-servers.net.
.                       733     IN      NS      f.root-servers.net.
.                       733     IN      NS      g.root-servers.net.
.                       733     IN      NS      h.root-servers.net.
.                       733     IN      NS      i.root-servers.net.
.                       733     IN      NS      j.root-servers.net.
.                       733     IN      NS      k.root-servers.net.
.                       733     IN      NS      l.root-servers.net.
.                       733     IN      NS      m.root-servers.net.
;; Received 239 bytes from 223.5.5.5#53(223.5.5.5) in 10 ms

image-20230920180208155

根域名服务器是 DNS 中最高级别的域名服务器,这些服务器负责返回顶级域的权威域名服务器地址,这些域名服务器的数量总共有 13 组,域名的格式从上面返回的结果可以看到是 .root-servers.net,每个根域名服务器中只存储了顶级域服务器的 IP 地址,大小其实也只有 2MB 左右,虽然域名服务器总共只有 13 组,但是每一组服务器都通过提供了镜像服务,全球大概也有几百台的根域名服务器在运行。

在这里,我们获取到了以下的 5 条 NS 记录,也就是 5 台 cn. 定义域名 DNS 服务器:

image-20230920175923960

当 DNS 解析器从根域名服务器中查询到了顶级域名 .cn 服务器的地址之后,就可以访问这些顶级域名服务器其中的一台 b.dns.cn 获取权威 DNS 的服务器的地址了:

image-20230920175948361

这里的权威 DNS 服务是作者在域名提供商进行配置的,当有客户端请求 jiujiu.tj.cn 域名对应的 IP 地址时,其实会从作者使用的 DNS 服务商 DNSPod 处请求服务的 IP 地址:

image-20230920180141524

最终,DNS 解析器从zebra.dnspod.net服务中获取了当前博客的 IP 地址 124.221.11.65,浏览器或者其他设备就能够通过 IP 向服务器获取请求的内容了。

从整个解析过程,我们可以看出 DNS 域名服务器大体分成三类,根域名服务、顶级域名服务以及权威域名服务三种,获取域名对应的 IP 地址时,也会像遍历一棵树一样按照从顶层到底层的顺序依次请求不同的服务器。

1.4、胶水记录

在通过服务器解析域名的过程中,我们看到当请求 .cn 顶级域名服务器的时候,其实返回了 b.dns.cn等域名:

image-20230920175923960

就像我们最开始说的,在互联网中想要请求服务,最终一定需要获取 IP 提供服务的服务器的 IP 地址;同理,作为 b.dns.cn作为一个 DNS 服务器,我也必须获取它的 IP 地址才能获得次级域名的 DNS 信息,但是这里就陷入了一种循环:

  • 如果想要获取 jiujiu.tj.cn 的 IP 地址,就需要访问 cn 顶级域名服务器 b.dns.cn
  • 如果想要获取 b.dns.cn 的 IP 地址,就需要访问 cn 顶级域名服务器 b.dns.cn
  • 如果想要获取 b.dns.cn 的 IP 地址,就需要访问 cn 顶级域名服务器 b.dns.cn

为了解决这一个问题,我们引入了胶水记录(Glue Record)这一概念,也就是在出现循环依赖时,直接在上一级作用域返回 DNS 服务器的 IP 地址:

dig +trace +additional jiujiu.tj.cn

image-20230920180941770

也就是同时返回 NS 记录和 A(或 AAAA) 记录,这样就能够解决域名解析出现的循环依赖问题。

1.5、服务发现

讲到现在,我们其实能够发现 DNS 就是一种最早的服务发现的手段,通过虽然服务器的 IP 地址可能会经常变动,但是通过相对不会变动的域名,我们总是可以找到提供对应服务的服务器。

在微服务架构中,服务注册的方式其实大体上也只有两种,一种是使用 Zookeeper 和 etcd 等配置管理中心,另一种是使用 DNS 服务,比如说 Kubernetes 中的 CoreDNS 服务。

使用 DNS 在集群中做服务发现其实是一件比较容易的事情,这主要是因为绝大多数的计算机上都会安装 DNS 服务,所以这其实就是一种内置的、默认的服务发现方式,不过使用 DNS 做服务发现也会有一些问题,因为在默认情况下 DNS 记录的失效时间是 600s,这对于集群来讲其实并不是一个可以接受的时间,在实践中我们往往会启动单独的 DNS 服务满足服务发现的需求。

二、CoreDNS

从 Kubernetes v1.12 开始,CoreDNS 是推荐的 DNS 服务器,取代了 kube-dns。 如果 你的集群原来使用 kube-dns,你可能部署的仍然是 kube-dns 而不是 CoreDNS。

说明: CoreDNS 和 kube-dns 的 Service 都在其 metadata.name 字段使用名字 kube-dns。这是为了能够与依靠传统 kube-dns 服务名称来解析集群内部地址的工作负载具有更好的互操作性。 使用 kube-dns作为服务名称可以抽离共有名称之后运行的是哪个 DNS 提供程序这一实现细节。

如果你在使用 Deployment 运行 CoreDNS,则该 Deployment 通常会向外暴露为一个具有 静态 IP 地址 Kubernetes 服务。 kubelet 使用 --cluster-dns=<DNS 服务 IP> 标志将 DNS 解析器的信息传递给每个容器。
DNS 名称也需要域名。 你可在 kubelet 中使用 --cluster-domain=<默认本地域名> 标志配置本地域名。
DNS 服务器支持正向查找(A 和 AAAA 记录)、端口发现(SRV 记录)、反向 IP 地址发现(PTR 记录)等。 更多信息,请参见Pod 和 服务的 DNS。

如果 Pod 的 dnsPolicy 设置为 “default”,则它将从 Pod 运行所在节点继承名称解析配置。 Pod 的 DNS 解析行为应该与节点相同。 但请参阅已知问题。
如果你不想这样做,或者想要为 Pod 使用其他 DNS 配置,则可以 使用 kubelet 的 --resolv-conf 标志。 将此标志设置为 “” 可以避免 Pod 继承 DNS。 将其设置为有别于 /etc/resolv.conf 的有效文件路径可以设定 DNS 继承不同的配置。

image-20230920174427852

2.1、安装部署

#导出coredns.yaml文件
kubectl get configmap coredns -n kube-system -o yaml > coredns-config.yaml
#添加dns规则    //192.16.0.1为物理机真实DNS,example.com为你想解析的网站域名, 
hosts {
    192.168.0.1 example.com
    fallthrough
}
#修改完成如下
apiVersion: v1
data:
  Corefile: |
    .:53 {
        errors
        health {
           lameduck 5s
        }
        ready
        kubernetes cluster.local in-addr.arpa ip6.arpa {
           pods insecure
           fallthrough in-addr.arpa ip6.arpa
           ttl 30
        }
        hosts {
          192.168.0.1 www.baidu.com
          fallthrough
        }

        prometheus :9153
        forward . /etc/resolv.conf {
           max_concurrent 1000
        }
        cache 30
        loop
        reload
        loadbalance
    }
kind: ConfigMap
metadata:
  creationTimestamp: "2023-09-18T11:50:51Z"
  managedFields:
  - apiVersion: v1
    fieldsType: FieldsV1
    fieldsV1:
      f:data:
        .: {}
        f:Corefile: {}
    manager: kubeadm
    operation: Update
    time: "2023-09-18T11:50:51Z"
  name: coredns
  namespace: kube-system
  resourceVersion: "216"
  selfLink: /api/v1/namespaces/kube-system/configmaps/coredns
  uid: bb798c92-0df5-41bf-9b97-9fdb3be40197

更新 ConfigMap,将修改后的配置应用到 CoreDNS:

kubectl apply -f coredns-config.yaml

kubectl port-forward -n kube-system svc/coredns 192.168.0.1:53