# 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。如下图

![](/files/-MZC2jYBdtZ471j4i10T)

#### 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`选项是为了与*kube-dns*向后兼容。您可以使用此`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中配置

```

![](/files/-MZHH_-yL6r7cO5_G2yi)

* 普通的 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://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>

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)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://darren.gitbook.io/project/gai-nian-yu-yuan-li/fu-wu-fa-xian-zi-yuan-dui-xiang/dns-yuan-li-jiang-jie.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
