Please enable Javascript to view the contents

Kubernetes 中的 DNS 服务

 ·  ☕ 4 分钟

1. 关于 DNS

1.1 DNS 服务的用途

DNS 提供的是域名到 IP 的映射服务。例如,在浏览器输入 https://www.chenshaowen.com 访问页面,但数据链路是基于 IP 的通信,无法识别 www.chenshaowen.com 。这时就需要进行 DNS 查询,输入参数是 www.chenshaowen.com ,返回结果是 IP 地址。

可以看到 DNS 提供了一种助记方法,我们不必关注 IP 地址以及其变动,而只需记住一段英文字符串,就能够找到服务。

一种常见的服务发现机制是配置管理中心,存储 Key/Value 值,例如 Consul、ZooKeeper、Etcd 等。而 DNS 提供的功能,也可以满足微服务架构中服务发现的需求,Kubernetes 就是采用的这种方式。

Kubernetes 从 1.11 版本开始,使用 CoreDNS 替代 KubeDNS 成为了内置的 DNS 服务。

1.2 resolv.conf

/etc/resolv.conf 是 DNS 客户端的配置文件,主要有四部分组成:

  • nameserver ,DNS 服务器的 IP 地址
  • domain ,本地域名的后缀
  • search ,搜索的域名后缀
  • sortlist ,对查询结果进行特定排序

当遇到无法解析的域名时,解析器才会用到 domain 、search 。例如,访问 http://abc/index.html, 解析器无法解析 abc ,就会拼接 domain 或 search 的配置作为后缀继续解析。当配置了 search 时,domain 失效。

2. CoreDNS

2.1 简介

CoreDNS 是 CNCF 正式毕业的项目。它是一个基于 Caddy 实现,模块化且可插拔的 DNS 服务器。

每个插件都遵循一个特定的接口协议。在 Corefile 文件中采用 DSL 定义 DNS 服务,可以很方便地开启各种插件,定制 DNS 服务。下面是一个示例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
example.org:8000 {
    file example.org
    prometheus
    errors  
    log
}
.:8000 {
    kubernetes
    proxy . 8.8.8.8
    log
    errors
    cache
}

这个配置暴露了一个 DNS Server ,监听在 8000 端口,根据不同的域名匹配不同的处理逻辑。每个逻辑,加载指定的插件处理。

2.2 相关插件

常用的插件包括:

  • hosts,配置集群全局可解析的 hosts ,需要注意的是域名后缀需要与 search 保持一致,例如,cluster.local ,否则 nodelocaldns 无法上报 coredns 解析
  • errors, 错误记录到 stdout
  • health,提供健康报告接口
  • kubernetes,解析为 Kubernetes 集群服务的 IP 地址
  • prometheus,提供 Prometheus 的 Metrics 接口
  • proxy,不在集群域内的查询转到指定解析器
  • cache,启用缓存
  • loop,检测死循环,并中断
  • reload,自动加载 Corefile,热更新
  • loadbalance,DNS 负载均衡器

2.3 集群 ConfigMap 配置

  • 查看 CoreDNS 的服务:
1
2
3
kubectl -n kube-system get svc coredns

coredns                            ClusterIP   10.233.0.3      <none>        53/UDP,53/TCP,9153/TCP   5h
  • 查看 CoreDNS 的配置
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
kubectl -n kube-system get cm  coredns -o yaml

apiVersion: v1
data:
  Corefile: |
    .:53 {
        errors
        health
        ready
        kubernetes cluster.local in-addr.arpa ip6.arpa {
          pods insecure
          upstream /etc/resolv.conf
          fallthrough in-addr.arpa ip6.arpa
        }
        prometheus :9153
        forward . /etc/resolv.conf {
          prefer_udp
        }
        cache 30
        loop
        reload
        loadbalance
    }
...

DNS 的默认端口是 53 。cluster.local 、 in-addr.arpa 、 ip6.arpa 格式的域名将被解析为 Kubernetes 的内部 IP 地址。

3. NodelocalDNS

3.1 简介

为了避免 Pod 进行 DNS 解析时,频繁查询 CoreDNS ,NodelocalDNS 在每个节点上都以 DaemonSet 运行 DNS 缓存以提高集群性能。

NodelocalDNS 的原理是,运行一个 hostNetwork 网络模式的 Pod,创建一个网卡绑定本地 DNS 的 IP 地址。节点上的 Pod 请求 DNS 解析时,将被拦截到 NodelocaDNS 。NodelocalDNS 通过取缓存或向上游请求 DNS ,完成解析过程。

3.2 集群 ConfigMap 配置

  • 查看 NodelocalDNS 服务
1
2
3
kubectl -n kube-system get ds nodelocaldns

nodelocaldns   1         1         1       1            1           <none>                        5h
  • 查看 NodelocalDNS 配置
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
kubectl -n kube-system get cm nodelocaldns -o yaml

apiVersion: v1
data:
  Corefile: |
    cluster.local:53 {
        errors
        cache {
            success 9984 30
            denial 9984 5
        }
        reload
        loop
        bind 169.254.25.10
        forward . 10.233.0.3 {
            force_tcp
        }
        prometheus :9253
        health 169.254.25.10:9254
    }
    in-addr.arpa:53 {
        errors
        cache 30
        reload
        loop
        bind 169.254.25.10
        forward . 10.233.0.3 {
            force_tcp
        }
        prometheus :9253
    }
    ip6.arpa:53 {
        errors
        cache 30
        reload
        loop
        bind 169.254.25.10
        forward . 10.233.0.3 {
            force_tcp
        }
        prometheus :9253
    }
    .:53 {
        errors
        cache 30
        reload
        loop
        bind 169.254.25.10
        forward . /etc/resolv.conf
        prometheus :9253
    }
...

同样使用 53 端口提供 DNS 服务,但是根据不同的域名,NodelocalDNS 提供了不同的解析策略。 cluster.local、in-addr.arpa、ip6.arpa 格式的域名从 CoreDNS 解析后缓存到本地,其他则使用节点的 DNS 解析后缓存。

4. Kubernetes Pod 中的 DNS 解析

  • 创建测试用的 Pod
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
cat > busybox.yaml <<-EOF
apiVersion: v1
kind: Pod
metadata:
  name: busybox
  namespace: default
spec:
  containers:
  - name: busybox
    image: busybox:1.28.4
    command:
      - sleep
      - "3600"
    imagePullPolicy: IfNotPresent
  restartPolicy: Always
EOF
1
kubectl apply -f busybox.yaml
  • 查看 DNS 解析配置
1
2
3
4
5
kubectl exec busybox cat /etc/resolv.conf

nameserver 169.254.25.10
search default.svc.cluster.local svc.cluster.local cluster.local
options ndots:5
  • 解析正常的内部服务
1
2
3
4
5
6
7
kubectl exec -ti busybox -- nslookup kubernetes.default

Server:    169.254.25.10
Address 1: 169.254.25.10

Name:      kubernetes.default
Address 1: 10.233.0.1 kubernetes.default.svc.cluster.local

解析链路:nodelocaldns -> 缓存 -> coredns -> 返回 IP

其中涉及 search 添加域名后缀的逻辑,在此不再表述。

  • 解析不存在的服务
1
2
3
4
5
6
kubectl exec -ti busybox -- nslookup a.b

Server:    169.254.25.10
Address 1: 169.254.25.10

nslookup: can't resolve 'a.b'

解析链路:nodelocaldns -> 节点配置的 DNS -> 未找到

  • 解析正常的外部服务
1
2
3
4
5
6
7
kubectl exec -ti busybox -- nslookup www.chenshaowen.com

Server:    169.254.25.10
Address 1: 169.254.25.10

Name:      www.chenshaowen.com
Address 1: 163.181.33.208

解析逻辑:nodelocaldns -> 节点配置的 DNS -> 返回 IP

5. ExternalName - CNAME 解析

ExternalName Service 是 Service 的一个特例,没有选择器,可以用于给外部服务取一个内部别名。

  • 创建 ExternalName Service
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
cat > externalname.yaml <<-EOF
apiVersion: v1
kind: Service
metadata:
  name: chenshaowen
  namespace: default
spec:
  type: ExternalName
  externalName: www.chenshaowen.com
EOF
1
kubectl apply -f externalname.yaml
  • 测试访问内部服务
1
2
3
4
5
6
7
kubectl exec -ti busybox -- nslookup chenshaowen.default

Server:    169.254.25.10
Address 1: 169.254.25.10

Name:      chenshaowen.default
Address 1: 58.215.145.110

chenshaowen.default 将会被映射到 www.chenshaowen.com ,这是通过 DNS 的 CNAME 记录实现的。

6. 参考


微信公众号
作者
微信公众号