# DNS原理讲解

## 为什么使用DNS

服务发现，说白了就是服务(应用)之间相互定位的过程。

服务发现需要解决的问题：

1、服务动态性强--容器在k8s中ip变化或迁移

2、更新发布频繁--版本迭代快

3、支持自动伸缩--大促或流量高峰

我们为了解决pod地址变化的问题，我们之前部署了service资源，将pod地址通过service资源暴露的固定地址，来解决以上问题，

那么，如何解决service资源名称和service资源暴露出来的集群网络IP做自动的对应呢，从而达到服务的自动发现呢？

在k8s中，coredns就是为了解决以上问题。

## 问题

### 1.没有手动添加任何解析记录，我们nginx-dp的service资源的IP，已经被解析了，为什么？

当我进入到pod内部以后，我们会发现我们的dns地址是我们的coredns地址，以及搜索域：

![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9pbWcyMDE4LmNuYmxvZ3MuY29tL2Jsb2cvMTAzNDc1OS8yMDE5MTEvMTAzNDc1OS0yMDE5MTEyMzE3NDUxNzYxMi05NzE4MDY3NTcucG5n?x-oss-process=image/format,png)



## 简述

Kubernetes 集群中运行了一组 DNS Pod，配置了对应的 Service，并由 kubelete 将 DNS Service 的 IP 地址配置到节点上的容器中以便解析 DNS names。

集群中的每一个 Service（包括 DNS 服务本身）都将被分配一个 DNS name。默认情况下，客户端 Pod 的 DNS 搜索列表包括 Pod 所在的名称空间以及集群的默认域。例如：

假设名称空间 `bar` 中有一个 Service 名为 `foo`：

* 名称空间 `bar` 中的 Pod 可以通过 `nslookup foo` 查找到该 Service
* 名称空间 `quux` 中的 Pod 可以通过 `nslookup foo.bar` 查找到该 Service

DNS 服务器监视着创建新 `Service` 的 Kubernetes API，从而为每一个 `Service` 创建一组 DNS 记录。如果整个集群的 DNS 一直被启用，那么所有的 `Pod` 应该能够自动对 `Service` 进行名称解析

果整个集群的 DNS 一直被启用，那么所有的 `Pod` 应该能够自动对 `Service` 进行名称解析。

例如，有一个名称为 `"my-service"` 的 `Service`，它在 Kubernetes 集群中名为 `"my-ns"` 的 `Namespace` 中，为 `"my-service.my-ns"` 创建了一条 DNS 记录。

在名称为 `"my-ns"` 的 `Namespace` 中的 `Pod` 应该能够简单地通过名称查询找到 `"my-service"`。在另一个 `Namespace` 中的 `Pod` 必须限定名称为 `"my-service.my-ns"`。这些名称查询的结果是 Cluster IP。

Kubernetes 也支持对端口名称的 DNS SRV（Service）记录。如果名称为 `"my-service.my-ns"` 的 `Service` 有一个名为 `"http"` 的 `TCP` 端口，可以对 `"_http._tcp.my-service.my-ns"` 执行 DNS SRV 查询，得到 `"http"` 的端口号。

Kubernetes DNS 服务器是唯一的一种能够访问 `ExternalName` 类型的 Service 的方式。

### [#](https://kuboard.cn/learning/k8s-intermediate/service/dns.html#services)Services <a href="#services" id="services"></a>

#### [#](https://kuboard.cn/learning/k8s-intermediate/service/dns.html#a-%E8%AE%B0%E5%BD%95)A 记录 <a href="#a-ji-lu" id="a-ji-lu"></a>

* Service（headless Service 除外）将被分配一个 DNS A 记录，格式为 `my-svc.my-namespace.svc.cluster-domain.example`。该 DNS 记录解析到 Service 的 ClusterIP。
* Headless Service（没有 ClusterIP）也将被分配一个 DNS A 记录，格式为 `my-svc.my-namespace.svc.cluster-domain.example`。该 DNS 记录解析到 Service 所选中的一组 Pod 的 IP 地址的集合。调用者应该使用该 IP 地址集合，或者按照轮询（round-robin）的方式从集合中选择一个 IP 地址使用。

#### [#](https://kuboard.cn/learning/k8s-intermediate/service/dns.html#srv-%E8%AE%B0%E5%BD%95)SRV 记录 <a href="#srv-ji-lu" id="srv-ji-lu"></a>

Service（含 headless Service）的命名端口（有 name 的端口）将被分配一个 SRV 记录，其格式为 `_my-port-name._my-port-protocol.my-svc.my-namespace.svc.cluster-domain.example`：

* 对于一个普通 Service（非 headless Service），该 SRV 记录解析到其端口号和域名 `my-svc.my-namespace.svc.cluster-domain.example`
* 对于一个 Headless Service，该 SRV 记录解析到多个结果：每一个结果都对应该 Service 的一个后端 Pod，包含其端口号和 Pod 的域名 `auto-generated-pod-name.my-svc.my-namespace.svc.cluster-domain.example`

### [#](https://kuboard.cn/learning/k8s-intermediate/service/dns.html#pods)Pods <a href="#pods" id="pods"></a>

### Pod DNS 的配置方式 <a href="#how_pod_dns_is_configured" id="how_pod_dns_is_configured"></a>

在每个节点上运行的 kubelet 代理会将 Pod 的 `etc/resolv.conf` 配置为使用 `coredns` 服务的 ClusterIP。如下图

![](https://55104664-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LFq8o_9x6ob05ww-sNi%2F-MZBGy0-EM-H2GkLcoaJ%2F-MZC2jYBdtZ471j4i10T%2Fimage.png?alt=media\&token=d9929aa7-7566-4689-a394-bab93df7bedd)



#### Pod 的 hostname / subdomain <a href="#pod-de-hostnamesubdomain" id="pod-de-hostnamesubdomain"></a>

Kubernetes 在创建 Pod 时，将 Pod 定义中的 `metadata.name` 的值作为 Pod 实例的 hostname。

Pod 定义中有一个可选字段 `spec.hostname` 可用来直接指定 Pod 的 hostname。例如，某 Pod 的 `spec.hostname` 字段被设置为 `my-host`，则该 Pod 创建后 hostname 将被设为 `my-host`

Pod 定义中还有一个可选字段 `spec.subdomain` 可用来指定 Pod 的 subdomain。例如，名称空间 `my-namespace` 中，某 Pod 的 hostname 为 `foo`，并且 subdomain 为 `bar`，则该 Pod 的完整域名（FQDN）为 `foo.bar.my-namespace.svc.cluster-domain.example`。

例子：

```
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 Service，其名称与 Pod 的 subdomain 相同，则集群的 KubeDNS 服务器仍将为 Pod 的完整域名（FQDN）返回一个 A 记录。例如，假设一个 Pod 的 hostname 为 `busybox-1` 且其 subdomain 为 `default-subdomain`，同名称空间下有一个 headless Service 的名字为 `default-subdomain`，此时，该 Pod 的完整域名（FQDN）为 `busybox-1.default-subdomain.my-namespace.svc.cluster-domain.example`。DNS 服务将其解析到一个 A 记录，指向 Pod 的 IP 地址。上面 yaml 文件中的 Pod `busybox1` 和 `busybox2` 都将有各自的 A 记录

备注

* A 记录不是根据 Pod name 创建的，而是根据 hostname 创建的。如果一个 Pod 没有 hostname 只有 subdomain，则 Kubernetes 将只为其 headless Service 创建一个 A 记录 `default-subdomain.my-namespace.svc.cluster-domain.example`，该记录指向 Pod 的 IP 地址。
* Pod 必须达到就绪状态才可以拥有 A 记录，除非 Service 的字段 `spec.publishNotReadyAddresses` 被设置为 `True`

#### [#](https://kuboard.cn/learning/k8s-intermediate/service/dns.html#pod-%E7%9A%84-dns-policy)Pod 的 DNS Policy <a href="#pod-de-dnspolicy" id="pod-de-dnspolicy"></a>

可以为每一个 Pod 设置其自己的 DNS Policy。Kubernetes 通过 Pod 定义中的 `spec.dnsPolicy` 字段设置 DNS Policy，可选的值有：

* **Default**： Pod 从其所在的节点继承域名解析配置。更多细节请参考 [Customizing DNS Service (opens new window)](https://kubernetes.io/docs/tasks/administer-cluster/dns-custom-nameservers/#inheriting-dns-from-the-node)
* **ClusterFirst**：任何与集群域名后缀（例如 `www.kubernetes.io`）不匹配的 DNS 查询，都将被转发到 Pod 所在节点的上游 DNS 服务。集群管理员可能配置了额外的 stub-domain 及上游 DNS 服务，更多细节请参考 [Customizing DNS Service (opens new window)](https://kubernetes.io/docs/tasks/administer-cluster/dns-custom-nameservers/#effects-on-pods)
* **ClusterFirstWithHostNet**： 对于运行在节点网络上的 Pod，其 dnsPolicy 必须指定为 `ClusterFirstWithHostNet`
* **None**： 允许 Pod 忽略 Kubernetes 环境中的 DNS 设置。此时，该 Pod 的 DNS 的所有设置必须通过 `spce.dnsConfig` 指定。 参考 [Pod 的 DNS 配置](https://kuboard.cn/learning/k8s-intermediate/service/dns.html#pod-%E7%9A%84-dns-%E9%85%8D%E7%BD%AE)

dnsPolicy的默认值

{% hint style="info" %}
**“Default”** 并非是默认的 DNS Policy。如果 `spec.dnsPolicy` 字段未指定，则 **“ClusterFirst”** 将被默认使用

&#x20;卖座网都为**Default 因为** kubelet将带有`--cluster-dns=<dns-service-ip>`标志的DNS解析器信息传递到每个容器。

kubelet指定了--cluster-dns=10.96.0.10  --cluster-domain=cluster.local \\

coredns中默认为deault&#x20;

&#x20;如果Pod's`dnsPolicy`设置为`default`，它将从Pod运行所在的节点继承名称解析配置

&#x20;如果您不希望这样做，或者想要对Pod使用其他DNS配置，则可以使用kubelet的`--resolv-conf`标志。将此标志设置为“”可以防止Pod继承DNS。将其设置为有效的文件路径，以指定`/etc/resolv.conf`用于DNS继承以外的文件&#x20;
{% endhint %}



下面的例子中的 Pod，其 DNS Policy 必须设置为 **“ClusterFirstWithHostNet”**，因为它的 `hostNetwork` 字段为 `true`

```
apiVersion: v1
kind: Pod
metadata:
  name: busybox
  namespace: default
spec:
  containers:
  - image: busybox:1.28
    command:
      - sleep
      - "3600"
    imagePullPolicy: IfNotPresent
    name: busybox
  restartPolicy: Always
  hostNetwork: true
  dnsPolicy: ClusterFirstWithHostNet
 
        已复制到剪贴板！
```



#### [#](https://kuboard.cn/learning/k8s-intermediate/service/dns.html#pod-%E7%9A%84-dns-%E9%85%8D%E7%BD%AE)Pod 的 DNS 配置 Kuboard v1.0.8.3 <a href="#pod-de-dns-pei-zhi" id="pod-de-dns-pei-zhi"></a>

> Kuboard v1.0.8.3 开始，支持 Pod 的 dnsConfig 字段。配置界面在 `工作负载编辑器 --> 容器组的更多设定`

在 Kubernetes 中，您可以直接配置 Pod 的 DNS 设置。

Pod 定义中的 `spec.dnsConfig` 是可选字段，且可以与任何类型的 `spec.dnsPolicy` 配合使用。如果 `spec.dnsPolicy` 被设置为 **“None”**，则 `spec.dnsConfig` 必须被指定。

`spec.dnsConfig` 中有如下字段可以配置：

* **nameservers**： Pod 的 DNS Server IP 地址列表。最多可以执行 3 个 IP 地址。当 `spec.dnsPolicy` 为 **“None”**，至少需要指定一个 IP 地址，其他情况下该字段是可选的。DNS Server 的 IP 地址列表将会与 DNS Policy 所产生的 DNS Server 地址列表合并（重复的条目被去除）。
* **searches**：Pod 中执行域名查询时搜索域的列表。该字段是可选的。如果指定了该字段，则指定的搜索域列表将与 DNS Policy 所产生的搜索域列表合并（重复的条目被去除）。合并后的列表最多不超过 6 个域。
* **options**：可选数组，其中每个元素由 **name** 字段（必填）和 **value** 字段（选填）组成。该列表中的内容将与 DNS Policy 所产生的 DNS 选项合并（重复的条目被去除）

```
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` 文件如下所示（从 `spec.dnsConfig` 的配置产生），执行命令 `kubectl exec -it dns-example -- cat /etc/resolv.conf` 可查看该文件内容：

```
nameserver 1.2.3.4
search ns1.svc.cluster-domain.example my.dns.search.suffix
options ndots:2 edns0
 
        已复制到剪贴板！
```

如果集群使用的是 IPv6，执行命令 `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.example
options ndots:5

```

#### [#](https://kuboard.cn/learning/k8s-intermediate/service/dns.html#%E7%89%88%E6%9C%AC%E5%85%BC%E5%AE%B9%E6%80%A7)版本兼容性 <a href="#ban-ben-jian-rong-xing" id="ban-ben-jian-rong-xing"></a>

Pod 定义中的 `spec.dnsConfig` 和 `spec.dnsPolicy=None` 的兼容性如下：

| Kubernetes 版本号 | 支持情况       |
| -------------- | ---------- |
| 1.14           | Stable     |
| 1.10           | Beta（默认启用） |
| 1.9            | Alpha      |

## CoreDNS配置

CoreDNS是一个模块化且可插入的DNS服务器，每个插件都为CoreDNS添加了新功能。可以通过维护[Corefile](https://coredns.io/2017/07/23/corefile-explained/)（即CoreDNS配置文件）进行配置。作为集群管理员，您可以修改 [ConfigMap](https://kubernetes.io/docs/concepts/configuration/configmap/) 让CoreDNS Corefile更改该群集的DNS服务发现行为。

在Kubernetes中，使用以下默认Corefile配置安装了CoreDNS：

```
apiVersion: v1
kind: ConfigMap
metadata:
  name: coredns
  namespace: kube-system
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
        }
        prometheus :9153
        forward . /etc/resolv.conf
        cache 30
        loop
        reload
        loadbalance
    }    
```

Corefile配置包括以下CoreDNS[插件](https://coredns.io/plugins/)：

* [错误](https://coredns.io/plugins/errors/)：将错误记录到stdout。
* [健康](https://coredns.io/plugins/health/)：CoreDNS的运行状况报告给`http://localhost:8080/health`。使用这种扩展语法，`lameduck`将使进程运行不正常，然后等待5秒钟，然后再关闭进程。
* [ready](https://coredns.io/plugins/ready/)：当所有能够发出信号准备就绪的插件都这样做时，端口8181上的HTTP端点将返回200 OK。
* [kubernetes](https://coredns.io/plugins/kubernetes/)：CoreDNS将基于Kubernetes的服务和Pod的IP答复DNS查询。您可以在CoreDNS网站上找到有关该插件的[更多详细信息](https://coredns.io/plugins/kubernetes/)。`ttl`允许您为响应设置自定义TTL。默认值为5秒。允许的最小TTL为0秒，最大为3600秒。将TTL设置为0将防止记录被缓存。提供\
  该`pods insecure`选项是为了&#x4E0E;_&#x6B;ube-dn&#x73;_&#x5411;后兼容。您可以使用此`pods verified`选项，仅当在相同名称空间中存在具有匹配IP的容器时，该选项才返回A记录。`pods disabled`如果您不使用pod记录，则可以使用该选项。
* [普罗米修斯](https://coredns.io/plugins/metrics/)：CoreDNS的度量可在`http://localhost:9153/metrics`在[普罗米修斯](https://prometheus.io/)格式（也称为OpenMetrics）。
* [forward](https://coredns.io/plugins/forward/)：任何不在Kubernetes集群域内的查询都将转发到预定义的解析器（/etc/resolv.conf）。
* [cache](https://coredns.io/plugins/cache/)：这将启用前端缓存。
* [loop](https://coredns.io/plugins/loop/)：检测简单的转发循环，如果发现循环，则暂停CoreDNS进程。
* [reload](https://coredns.io/plugins/reload)：允许自动重新加载已更改的Corefile。编辑ConfigMap配置后，请等待两分钟，以使更改生效。
* [loadbalance](https://coredns.io/plugins/loadbalance)：这是一个轮询DNS负载均衡器，用于随机分配答案中A，AAAA和MX记录的顺序。

您可以通过修改ConfigMap来修改默认的CoreDNS行为。

## darrendu对coredns的理解

```
1.CoreDNS通过维护 Corefile（即CoreDNS配置文件）进行配置
2.可以通过修改 ConfigMap让CoreDNS Corefile更改该群集的DNS服务发现行为
3.在configmap的配置文件中可以可以看到里面有关于k8s的配置

 kubernetes cluster.local 10.96.0.0/12 {
          pods insecure 是为了与kube-dns向后兼容
          upstream
          fallthrough in-addr.arpa ip6.arpa 查询该插件具有权威性的区域中的记录
        }

  CoreDNS将基于Kubernetes的服务和Pod的IP答复DNS查询 
  
4.可查看https://coredns.io/plugins/kubernetes/，有介绍具体的连接
    在k8s插件中，endpoint指定远程k8s API端点的URL。如果省略，它将使用群集服务帐户连接到k8s群集内。
    
    所以卖座网，有添加clusterroles权限的账号 system:coredns，给予了对（- endpoints，services，pods，namespaces）list和watch
    
5.dns中的记录
coredns来解析之前在etcd里面创建的记录：/skydns/com/dynamic/coredns '{"host":"10.10.13.5", "port":8082}'。

 
说明：1。由于插件中k8s pods POD-MODE设置用于处理基于IP的pod A记录的模式 disabled： 默认。不处理pod请求   
  
```

## 总结

```
可查看https://www.qikqiak.com/post/service-found-dns/
dns的功能就是把名称转换成ip
在早期kube-dns中，包含kube-dns、dnsmasq-nanny、sidecar 这3个容器分别实现了什么功能?

kubedns: kubedns 基于 SkyDNS 库，通过 apiserver 监听 Service 和 Endpoints 的变更事件同时也同步到本地 Cache，实现了一个实时的 Kubernetes 集群内 Service 和 Pod 的 DNS服务发现
dnsmasq: dsnmasq 容器则实现了 DNS 的缓存功能(在内存中预留一块默认大小为 1G 的地方，保存当前最常用的 DNS 查询记录，如果缓存中没有要查找的记录，它会到 kubedns 中查询，并把结果缓存起来)，通过监听 ConfigMap 来动态生成配置
sider: sidecar 容器实现了可配置的 DNS 探测，并采集对应的监控指标暴露出来供 prometheus 使用

DNS Pod 具有静态 IP 并作为 Kubernetes 服务暴露出来。
 该静态 IP 被分配后，kubelet 会将使用 --cluster-dns = <dns-service-ip>参数配置的 DNS 传递给每个容器。
 DNS 名称也需要域名，本地域可以使用参数--cluster-domain = <default-local-domain>在kubelet中配置

```

![](https://55104664-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LFq8o_9x6ob05ww-sNi%2F-MZHDHzag54_G4mh3vrj%2F-MZHH_-yL6r7cO5_G2yi%2Fimage.png?alt=media\&token=02c3c284-b59c-42d7-b009-c46875cc8843)

* 普通的 Service：会生成`servicename.namespace.svc.cluster.local`的域名，会解析到 Service 对应的 ClusterIP 上，在 Pod 之间的调用可以简写成 `servicename.namespace`，如果处于同一个命名空间下面，甚至可以只写成 servicename 即可访问
* Headless Service：无头服务，就是把 clusterIP 设置为 None 的，会被解析为指定 Pod 的 IP 列表，同样还可以通过`podname.servicename.namespace.svc.cluster.local`访问到具体的某一个 Pod。

> CoreDNS 实现的功能和 KubeDNS 是一致的，不过`CoreDNS`的所有功能都集成在了同一个容器中，在最新版的1.11.0版本中官方已经推荐使用 CoreDNS了
>
> [https://zhuanlan.zhihu.com/p/54036077](https://zhuanlan.zhihu.com/p/54036077)

## 参考文章：

官方文档 [https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/](https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/)

kuboard [https://kuboard.cn/learning/k8s-intermediate/service/dns.html#pod-%E7%9A%84-dns-%E9%85%8D%E7%BD%AE](https://kuboard.cn/learning/k8s-intermediate/service/dns.html#pod-%E7%9A%84-dns-%E9%85%8D%E7%BD%AE)

dns讲解：[**https://kubernetes.io/zh/docs/tasks/administer-cluster/dns-custom-nameservers/**](https://kubernetes.io/zh/docs/tasks/administer-cluster/dns-custom-nameservers/)

**明阳的博客：**[**https://www.qikqiak.com/post/service-found-dns/**](https://www.qikqiak.com/post/service-found-dns/)

**coredns原理：**[**https://draveness.me/dns-coredns/**](https://draveness.me/dns-coredns/)

**原理：**[**https://www.cnblogs.com/kazihuo/p/13071242.html**](https://www.cnblogs.com/kazihuo/p/13071242.html)

