亲宝软件园·资讯

展开

Linux网络学习笔记(二):域名解析(DNS)——以 CoreDNS 为例

於清樂 人气:0
>个人笔记,观点不一定正确. 适合对 Kubernetes 有一定了解的同学。 ## 前言 最近一直在学习 Kubernetes,但是手头没有个自有域名,要测试 ingress 就比较麻烦,每次都是手动改 hosts 文件。。 今天突然想到——K8s 内部就是用 DNS 做的服务发现,我为啥不自己弄一个 DNS 服务器呢?然后所有节点的 DNS 都配成它,这样有需要时直接改这个 DNS 服务器的配置就行, 一劳永逸。 我首先想到的是 群晖/Windows Server 自带的那种自带图形化界面的 DNS 服务器,但是这俩都是平台特定的。 网上搜一圈没找到类似带 UI 的 DNS 工具,所以决定就用 CoreDNS,刚好熟悉一下它的具体使用。 不过讲 CoreDNS 前,我们还是先来熟悉一下 DNS 吧。 ## 一、DNS 是个啥? DNS,即域名系统(Domain Name System),是一项负责将一个 human readable 的所谓域名,转换成一个 ip 地址的协议。 而域名的好处,有如下几项: 1. 域名对人类更友好,可读的字符串总比一串 ip 数字好记。 1. 一个域名可以对应多个 ip,可实现所谓的负载均衡。 1. 多个域名可以对应同一个 ip,以不同的域名访问该 ip,能访问不同的应用。(通过 nginx 做代理实现) DNS 协议是一个基于 UDP 的应用层协议,它默认使用 53 端口进行通信。 应用程序通常将 DNS 解析委派给操作系统的 DNS Resolver 来执行,程序员对它几乎无感知。 DNS 虽然说一般只用来查个 ip 地址,但是它提供的记录类型还蛮多的,有如下几种: 1. `A` 记录:它记录域名与 IPv4 地址的对应关系。目前用的最多的 DNS 记录就是这个。 1. `AAAA` 记录:它对应的是 IPv6,可以理解成新一代的 `A` 记录。以后会用的越来越多的。 1. `NS` 记录:记录 DNS 域对应的权威服务器**域名**,权威服务器域名必须要有对应的 `A` 记录。 - 通过这个记录,可以将子域名的解析分配给别的 DNS 服务器。 1. `CNAME` 记录: 记录域名与另一个域名的对应关系,用于给域名起别名。这个用得也挺多的。 1. `MX` 记录:记录域名对应的邮件服务器域名,邮件服务器的域名必须要有对应的 `A` 记录。 1. `SRV` 记录:SRV 记录用于提供服务发现,看名字也能知道它和 SERVICE 有关。 - SRV 记录的内容有固定格式:`优先级 权重 端口 目标地址`,例如 `0 5 5060 sipserver.example.com` - 主要用于企业域控(AD)、微服务发现(Kubernetes) 上述的所有 DNS 记录,都是属于将域名解析为 IP 地址,或者另一个域名,这被称做** DNS 正向解析**。 除了这个正向解析外,还有个非常冷门的**反向解析**,基本上只在设置邮件服务器时才会用到。(Kubernetes 可能也有用到) 反向解析主要的记录类型是:**`PTR` 记录**,它提供将 IP 地址反向解析为域名的功能。 而且因为域名是从右往左读的(最右侧是根, `www.baidu.com.`),而 IP 的网段(如 `192.168.0.0/16`)刚好相反,是左边优先。 因此 PTR 记录的“域名”必须将 IP 地址反着写,末尾再加上 `.in-addr.arpa.` 表示这是一个反向解析的域名。(ipv6 使用 `ip6.arpa.`) 拿 baidu.com 的邮件服务器测试一下: ![](https://img2018.cnblogs.com/blog/968138/202002/968138-20200207101308247-1165339332.png) 其他还有些 `TXT`、`CAA` 等奇奇怪怪的记录,就用到的时候自己再查了。 ## 二、域名的分层结构 国际域名系统被分成四层: 1. **根域(Root Zone)**:所有域名的根。 - 根域名服务器负责解析`顶级域名`,给出顶级域名的 DNS 服务器地址。 - 全世界仅有十三组根域名服务器,这些服务器的 ip 地址基本不会变动。 - 它的域名是 "",空字符串。而它的**全限定域名(FQDN)**是 `.`,因为 FQDN 总是以 `.` 结尾。(FQDN 在后面解释,可暂时忽略) 1. **顶级域(Top Level Domains, TLD)**:`.com` `.cn` 等国际、国家级的域名 - 顶级域名服务器负责解析`次级域名`,给出次级域名的 DNS 服务器地址。 - 每个顶级域名都对应各自的服务器,它们之间是完全独立的。`.cn` 的域名解析仅由 `.cn` 顶级域名服务器提供。 1. **次级域(Second Level Domains)**:这个才是个人/企业能够买到的域名,比如 `baidu.com` - 每个次级域名都有一到多个权威 DNS 服务器,这些 DNS 服务器会以 NS 记录的形式保存在对应的顶级域名(TLD)服务器中。 - 权威域名服务器则负责给出最终的解析结果:ip 地址(A 记录 ),另一个域名(CNAME 记录)、另一个 DNS 服务器(NS 记录)等。 1. **子域(Sub Domians)**:`*.baidu.com` 统统都是 `baidu.com` 的子域。 - 每一个子域都可以有自己独立的权威 DNS 服务器,这通过在子域中添加 NS 记录实现。 普通用户通常是通过域名提供商如阿里云购买的次级域名,接下来我们以 `rea.ink` 为例介绍域名的购买到可用的整个流程。 这时阿里云会向该中插入几条 NS 记录,指向阿里云的次级 DNS 服务器(`vip1.alidns.com`)。 1. 你在某域名提供商处购买了一个域名 `rea.ink` 1. 域名提供商向 `.ink` 对应的顶级域名服务器中插入一条以上的 NS 记录,指向它自己的次级 DNS 服务器,如 `dns25.hichina.com.` 1. 你在该域名提供商的 DNS 管理界面中添加 `A` 记录,值为你的服务器 IP。 1. OK 现在 ping 一下 `rea.ink`,就会发现它已经解析到你自己的服务器了。 上述流程中忽略了我大天朝的特殊国情——备案,勿介意。 ## 三、DNS 递归解析器:在浏览器中输入域名后发生了什么? 下面的图片拷贝自 Amazon Aws 文档,它展示了在不考虑任何 DNS 缓存的情况下,一次 Web 请求的经过,详细描绘了 DNS 解析的部分。 ![](https://img2018.cnblogs.com/blog/968138/202002/968138-20200205165225054-57338322.png) 其中的第 3 4 5 步按顺序向前面讲过的根域名服务器、顶级域名服务器、权威域名服务器发起请求,以获得下一个 DNS 服务器的信息。这很清晰。 图中当前还没介绍的部分,是紫色的 `DNS Resolver`(域名解析器),也叫 `Recursive DNS resolver`(**DNS 递归解析器**)。 它本身只负责递归地请求 3 4 5 步中的上游服务器,然后把获取的最终结果返回给客户端,同时将记录缓存到本地以加快解析速度。 这个 DNS 解析器,其实就是所谓的**公共 DNS 服务器**:Google 的 `8.8.8.8`,国内著名的 `114.114.114.114`。 这些公共 DNS 用户量大,缓存了大量的 DNS 记录,有效地降低了上游 DNS 服务器的压力,也加快了网络上的 DNS 查询速度。 接下来使用 `dig +trace baidu.com` 复现一下上述的查询流程(这种情况下 dig 自己就是一个 DNS 递归解析器): ![](https://img2018.cnblogs.com/blog/968138/202002/968138-20200207101817456-1276700345.png) 另外前面有讲过 DNS 的反向解析,也是同样的层级结构,是从根服务器开始往下查询的,下面拿 baidu 的一个邮件服务器进行测试: ![](https://img2018.cnblogs.com/blog/968138/202002/968138-20200207102331250-71048478.png) ### TTL(Time To Live) 上面讲了**公共 DNS 服务器**通过缓存技术,降低了上游 DNS 服务器的压力,也加快了网络上的 DNS 查询速度。 可缓存总得有个过期时间吧!为了精确地控制 DNS 记录的过期时间,每条 DNS 记录都要求设置一个时间属性——TTL,单位为秒。这个时间可以自定义。 任何一条 DNS 缓存,在超过过期时间后都必须丢弃! ## 四、本地 DNS 服务器 这类服务器只在当前局域网内有效,是一个私有的 DNS 服务器,企业常用。一般通过 DHCP 或者手动配置的方式,使内网的服务器都默认使用局域网 DNS 服务器进行解析。 局域网 DNS 服务器的规模与层级,视局域网的大小而定。一般小公司一个就行,要容灾设三个副本也够了。 以 CoreDNS 为例,局域网 DNS 服务器也可以被设置成一个 DNS Resolver,可以设置只转发特定域名的 DNS 解析。这叫将某个域设为转发区域。 著名的 Kubernetes 容器集群系统(它内部使用的是自己的虚拟局域网),就是使用的 CoreDNS 进行局域网的域名解析,以实现服务发现功能。 ## 五、操作系统的 DNS 解析器 应用程序实际上都是调用的操作系统的 DNS Resolver 进行域名解析的。在 Linux 中 DNS Resolver 由 glibc/musl 提供,配置文件为 `/etc/resolv.conf`。 比如 Python 的 DNS 解析,就来自于标准库的 socket 包,这个包只是对底层 c 语言库的一个简单封装。 基本上只有专门用于网络诊断的 DNS 工具包,才会自己实现 DNS 协议。 ### 1. hosts 文件 操作系统中还有一个特殊文件:Linux 中的 `/etc/hosts` 和 Windows 中的 `C:\Windows\System32\drivers\etc\hosts` 系统中的 DNS resolver 会首先查看这个 `hosts` 文件中有没有该域名的记录,如果有就直接返回了。没找到才会去查找本地 DNS 缓存、别的 DNS 服务器。 只有部分专门用于网络诊断的应用程序(e.g. dig)不会依赖 OS 的 DNS 解析器,因此这个 `hosts` 会失效。`hosts` 对于绝大部分程序都有效。 >移动设备上 hosts 可能会失效,部分 app 会绕过系统,使用新兴的 HTTPDNS 协议进行 DNS 解析。 ### 2. HTTPDNS 传统的 DNS 协议因为使用了明文的 UDP 协议,很容易被劫持。顺应移动互联网的兴起,目前一种新型的 DNS 协议——HTTPDNS 应用越来越广泛,国内的阿里云腾讯云都提供了这项功能。 HTTPDNS 通过 HTTP 协议直接向权威 DNS 服务器发起请求,绕过了一堆中间的 DNS 递归解析器。好处有二: 1. 权威 DNS 服务器能直接获取到客户端的真实 IP(而不是某个中间 DNS 递归解析器的 IP),能实现就近调度。 1. 因为是直接与权威 DNS 服务器连接,避免了 DNS 缓存污染的问题。 HTTPDNS 协议需要程序自己引入 SDK,或者直接请求 HTTP API。 ### 3. 默认 DNS 服务器 操作系统的 DNS 解析器通常会允许我们配置多个上游 Name Servers,比如 Linux 就是通过 `/etc/resolv.conf` 配置 DNS 服务器的。 ```conf $ cat /etc/resolv.conf nameserver 8.8.8.8 nameserver 8.8.4.4 search lan ``` >不过现在这个文件基本不会手动修改了,各 Linux 发行版都推出了自己的网络配置工具,由这些工具自动生成 Linux 的各种网络配置,更方便。 比如 Ubuntu 就推荐使用 netplan 工具进行网络设置。 >Kubernetes 就是通过使用容器卷映射的功能,修改 /etc/resolv.conf,使集群的所有容器都使用集群 DNS 服务器(CoreDNS)进行 DNS 解析。 通过重复使用 `nameserver` 字段,可以指定多个 DNS 服务器(Linux 最多三个)。DNS 查询会按配置中的顺序选用 DNS 服务器。 **仅在靠前的 DNS 服务器没有响应(timeout)时,才会使用后续的 DNS 服务器!所以指定的服务器中的 DNS 记录最好完全一致!!!**不要把第一个配内网 DNS,第二个配外网!!! ### 4. DNS 搜索域 上一小节给出的 `/etc/resolv.conf` 文件内容的末尾,有这样一行: `search lan`,它指定的,是所谓的 DNS 搜索域。 讲到 `DNS 搜索域`,就不得不提到一个名词:全限定域名(Full Qulified Domain Name, FQDN),即一个域名的完整名称,`www.baidu.com`。 一个普通的域名,有下列四种可能: 1. `www.baidu.com.`: 末尾的 `.` 表示根域,说明 `www.baidu.com` 是一个 FQDN,因此不会使用搜索域! 1. `www.baidu.com`: 末尾没 `.`,但是域名包含不止一个 `.`。首先当作 FQDN 进行查询,没查找再按顺序在各搜索域中查询。 - `/etc/resolv.conf` 的 `options` 参数中,可以指定域名中包含 `.` 的临界个数,默认是 1. 1. `local`: 不包含 `.`,被当作 `host` 名称,非 FQDN。首先在 `/etc/hosts` 中查找,没找到的话,再按顺序在各搜索域中查找。 >上述搜索顺序可以通过 `host -v ` 进行测试,该命令会输出它尝试过的所有 FQDN。 修改 `/etc/resolv.conf` 中的 `search` 属性并测试,然后查看输出。 就如上面说例举的,在没有 `DNS 搜索域` 这个东西的条件下,我们访问任何域名,都必须输入一个全限定域名 FQDN。 有了搜索域我们就可以稍微偷点懒,省略掉域名的一部分后缀,让 DNS Resolver 自己去在各搜索域中搜索。 在 Kubernetes 中就使用到了搜索域,k8s 中默认的域名 FQDN 是 `service.namespace.svc.cluster.local`, 但是对于 default namespace 中的 service,我们可以直接通过 `service` 名称查询到它的 IP。 对于其他名字空间中的 service,也可以通过 `service.namespace` 查询到它们的 IP,不需要给出 FQDN。 Kubernetes 中 `/etc/resolv.conf` 的示例如下: ```conf nameserver 10.43.0.10 search default.svc.cluster.local svc.cluster.local cluster.local options ndots:5 ``` 可以看到 k8s 设置了一系列的搜索域,并且将 `.` 的临界值设为了 5。 也就是少于 5 个 dots 的域名,都首先当作非 FQDN 看待,优先在搜索域里面查找。 该配置文件的详细描述参见 [manpage - resolv.conf](http://man7.org/linux/man-pages/man5/resolv.conf.5.html),或者在 Linux 中使用 `man resolv.conf` 命令查看。 ## 六、DNS 诊断的命令行工具 ```shell dig +trace baidu.com # 诊断 dns 的主要工具,非常强大 host -a baidu.com # host 基本就是 dig 的弱化版,不过 host 有个有点就是能打印出它测试过的所有 FQDN nslookup baidu.com # 和 host 没啥大差别,多个交互式查询不过一般用不到 whois baidu.com # 查询域名注册信息,内网诊断用不到 ``` 详细的使用请 `man dig` ## 七、CoreDNS 的使用 主流的本地 DNS 服务器中,提供 UI 界面的有 Windows DNS Server 和群晖 DNS Server,很方便,不过这两个都是操作系统绑定的。 开源的 DNS 服务器里边儿,BIND 好像是最有名的,各大 Linux 发行版自带的 `dig/host/nslookup`,最初都是 Bind 提供的命令行工具。 不过为了一举两得(DNS+K8s),咱还是直接学习 CoreDNS 的使用。 CoreDNS 最大的特点是灵活,可以很方便地给它编写插件以提供新功能。功能非常强大,相比传统 DNS 服务器,它非常“现代化”。在 K8s 中它被用于提供服务发现功能。 接下来以 CoreDNS 为例,讲述如何配置一个 DNS 服务器,添加私有的 DNS 记录,并设置转发规则以解析公网域名。 ### 1. 配置文件:Corefile CoreDNS 因为是 Go 语言写的,编译结果是单个可执行文件,它默认以当前文件夹下的 Corefile 为配置文件。以 kubernetes 中的 Corefile 为例: ```corefile .:53 { errors # 启用错误日志 health # 启用健康检查 api ready # 启用 readiness 就绪 api # 启用 kubernetes 集群支持,详见 https://coredns.io/plugins/kubernetes/ # 此插件只处理 cluster.local 域,以及 PTR 解析 kubernetes cluster.local in-addr.arpa ip6.arpa { pods insecure upstream # fallthrough in-addr.arpa ip6.arpa # 向下传递 DNS 反向查询 ttl 30 # 过期时间 } prometheus :9153 # 启用 prometheus metrics 支持 forward . 114.114.114.114 19.29.29.29 # 将非集群域名的 DNS 请求,转发给公网 DNS 服务器。 cache 30 # 启用前端缓存,缓存的 TTL 设为 30 loop # 检测并停止死循环解析 reload # 支持动态更新 Corefile # 随机化 A/AAAA/MX 记录的顺序以实现负载均衡。 # 因为 DNS resolver 通常使用第一条记录,而第一条记录是随机的。这样客户端的请求就能被随机分配到多个后端。 loadbalance } ``` Corefile 首先定义 DNS 域,域后的代码块内定义需要使用的各种插件。**注意这里的插件顺序是没有任何意义的!**插件的调用链是在 CoreDNS 编译时就定义好的,不能在运行时更改。 通过上述配置启动的 CoreDNS 是无状态的,它以 Kubernetes ApiServer 为数据源,CoreDNS 本身只相当于一个查询器/缓存,因此它可以很方便地扩缩容。 ### 2. 将 CoreDNS 设置成一个私有 DNS 服务器 现在清楚了 Corefile 的结构,让我们来设计一个通过文件配置 DNS 条目的 Corefile 配置: ```corefile # 定义可复用 Block (common) { log errors cache loop # 检测并停止死循环解析 } # 本地开发环境的 DNS 解析 dev-env.local:53 { import common # 导入 Block file dev-env.local { # 从文件 `dev-env.local` 中读取 DNS 数据 reload 30s # 每 30s 检查一次配置的 Serial,若该值有变更则重载整个 Zone 的配置。 } } # 本地测试环境 test-env.local:53 { import common file test-env.local { reload 30s } } # 其他 .:53 { forward . 114.114.114.114 # 解析公网域名 log errors cache } ``` 上面的 Corefile 定义了两个本地域名 `dev-env.local` 和 `test-env.local`,它们的 DNS 数据分别保存在 `file` 指定的文件中。 这个 `file` 指定的文件和 `bind9` 一样,都是使用在 [rfc1035](https://tools.ietf.org/html/rfc1035#section-5.3) 中定义的 Master File 格式,`dig` 命令输出的就是这种格式的内容。示例如下: ``` ;; 與整個領域相關性較高的設定包括 NS, A, MX, SOA 等標誌的設定處! $TTL 30 @ IN SOA dev-env.local. devops.dev-env.local. ( 20200202 ; SERIAL,每次修改此文件,都应该同步修改这个“版本号”,可将它设为修改时间。 7200 ; REFRESH 600 ; RETRY 3600000 ; EXPIRE 60) ; MINIMUM @ IN NS dns1.dev-env.local. ; DNS 伺服器名稱 dns1.dev-env.local. IN A 192.168.23.2 ; DNS 伺服器 IP redis.dev-env.local. IN A 192.168.23.21 mysql.dev-env.local. IN A 192.168.23.22 elasticsearch.dev-env.local. IN A 192.168.23.23 ftp IN A 192.168.23.25 ; 這是簡化的寫法! ``` 详细的格式说明参见 [鳥哥的 Linux 私房菜 - DNS 正解資料庫檔案的設定](http://linux.vbird.org/linux_server/0350dns.php#DNS_master_name) `test-env.local` 也是一样的格式,根据上面的模板修改就行。这两个配置文件和 Corefile 放在同一个目录下: ``` root@test-ubuntu:~https://img.qb5200.com/download-x/dns-server# tree . ├── coredns # coredns binary ├── Corefile ├── dev-env.local └── test-env.local ``` 然后通过 `./coredns` 启动 coredns。通过 dig 检验: ![](https://img2018.cnblogs.com/blog/968138/202002/968138-20200211182339141-1135337933.png) 可以看到 `ftp.dev-env.local` 已经被成功解析了。 ### 3. [可选插件(External Plugins)](https://coredns.io/explugins/) CoreDNS 提供的预编译版本,不包含 [External Plugins](https://coredns.io/explugins/) 中列出的部分,如果你需要,可以自行修改 `plugin.cfg`,然后手动编译。 不得不说 Go 语言的编译,比 C 语言是方便太多了。自动拉取依赖,一行命令编译!只要配好 [GOPROXY](https://github.com/goproxy/goproxy.cn),启用可选插件其实相当简单。 ### 4. 设置 DNS 集群 单台 DNS 服务器的性能是有限的,而且存在单点故障问题。因此在要求高可用或者高性能的情况下,就需要设置 DNS 集群。 虽然说 CoreDNS 本身也支持各种 DNS Zone 传输,主从 DNS 服务器等功能,不过我想最简单的,可能还是直接用 K8s。 直接用 ConfigMap 存配置,通过 Deployment 扩容就行,多方便。 要修改起来更方便,还可以启用可选插件:redis,直接把配置以 json 的形式存在 redis 里,通过 redis-desktop-manager 进行查看与修改。 ## 参考 - [DNS 原理入门](http://www.ruanyifeng.com/blog/2016/06https://img.qb5200.com/download-x/dns.html) - [What Is DNS? | How DNS Works - Cloudflare](https://www.cloudflare.com/learninghttps://img.qb5200.com/download-x/dns/what-is-dns/) - [What is DNS? - Amazon AWS](https://aws.amazon.com/cn/route53/what-is-dns/?nc1=h_ls) - [鸟哥的 Linux 私房菜——主機名稱控制者: DNS 伺服器](http://linux.vbird.org/linux_server/0350dns.php#DNS_resolver_whois) - [CoreDNS - Manual](https://coredns.io/manual/toc/) - [Kubernetes - DNS for Services and Pods](https://kubernetes.iohttps://img.qb5200.com/download-x/docs/concepts/services-networkinghttps://img.qb5200.com/download-x/dns-pod-service/) - [Kubernetes - Customizing DNS Service](https://kubernetes.iohttps://img.qb5200.com/download-x/docs/tasks/administer-clusterhttps://img.qb5200.com/download-x/dns-custom-nameservers/)

加载全部内容

相关教程
猜你喜欢
用户评论