个人笔记,观点不一定正确. 适合对 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 地址的协议。
而域名的好处,有如下几项:
DNS 协议是一个基于 UDP 的应用层协议,它默认使用 53 端口进行通信。
应用程序通常将 DNS 解析委派给操作系统的 DNS Resolver 来执行,程序员对它几乎无感知。
DNS 虽然说一般只用来查个 ip 地址,但是它提供的记录类型还蛮多的,有如下几种:
A
记录:它记录域名与 IPv4 地址的对应关系。目前用的最多的 DNS 记录就是这个。AAAA
记录:它对应的是 IPv6,可以理解成新一代的 A
记录。以后会用的越来越多的。NS
记录:记录 DNS 域对应的权威服务器域名,权威服务器域名必须要有对应的 A
记录。
CNAME
记录: 记录域名与另一个域名的对应关系,用于给域名起别名。这个用得也挺多的。MX
记录:记录域名对应的邮件服务器域名,邮件服务器的域名必须要有对应的 A
记录。SRV
记录:SRV 记录用于提供服务发现,看名字也能知道它和 SERVICE 有关。
优先级 权重 端口 目标地址
,例如 0 5 5060 sipserver.example.com
上述的所有 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 的邮件服务器测试一下:
其他还有些 TXT
、CAA
等奇奇怪怪的记录,就用到的时候自己再查了。
国际域名系统被分成四层:
顶级域名
,给出顶级域名的 DNS 服务器地址。.
,因为 FQDN 总是以 .
结尾。(FQDN 在后面解释,可暂时忽略).com
.cn
等国际、国家级的域名
次级域名
,给出次级域名的 DNS 服务器地址。.cn
的域名解析仅由 .cn
顶级域名服务器提供。baidu.com
*.baidu.com
统统都是 baidu.com
的子域。
普通用户通常是通过域名提供商如阿里云购买的次级域名,接下来我们以 rea.ink
为例介绍域名的购买到可用的整个流程。
这时阿里云会向该中插入几条 NS 记录,指向阿里云的次级 DNS 服务器(vip1.alidns.com
)。
rea.ink
.ink
对应的顶级域名服务器中插入一条以上的 NS 记录,指向它自己的次级 DNS 服务器,如 dns25.hichina.com.
A
记录,值为你的服务器 IP。rea.ink
,就会发现它已经解析到你自己的服务器了。上述流程中忽略了我大天朝的特殊国情——备案,勿介意。
下面的图片拷贝自 Amazon Aws 文档,它展示了在不考虑任何 DNS 缓存的情况下,一次 Web 请求的经过,详细描绘了 DNS 解析的部分。
其中的第 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 递归解析器):
另外前面有讲过 DNS 的反向解析,也是同样的层级结构,是从根服务器开始往下查询的,下面拿 baidu 的一个邮件服务器进行测试:
上面讲了公共 DNS 服务器通过缓存技术,降低了上游 DNS 服务器的压力,也加快了网络上的 DNS 查询速度。
可缓存总得有个过期时间吧!为了精确地控制 DNS 记录的过期时间,每条 DNS 记录都要求设置一个时间属性——TTL,单位为秒。这个时间可以自定义。
任何一条 DNS 缓存,在超过过期时间后都必须丢弃!
这类服务器只在当前局域网内有效,是一个私有的 DNS 服务器,企业常用。一般通过 DHCP 或者手动配置的方式,使内网的服务器都默认使用局域网 DNS 服务器进行解析。
局域网 DNS 服务器的规模与层级,视局域网的大小而定。一般小公司一个就行,要容灾设三个副本也够了。
以 CoreDNS 为例,局域网 DNS 服务器也可以被设置成一个 DNS Resolver,可以设置只转发特定域名的 DNS 解析。这叫将某个域设为转发区域。
著名的 Kubernetes 容器集群系统(它内部使用的是自己的虚拟局域网),就是使用的 CoreDNS 进行局域网的域名解析,以实现服务发现功能。
应用程序实际上都是调用的操作系统的 DNS Resolver 进行域名解析的。在 Linux 中 DNS Resolver 由 glibc/musl 提供,配置文件为 /etc/resolv.conf
。
比如 Python 的 DNS 解析,就来自于标准库的 socket 包,这个包只是对底层 c 语言库的一个简单封装。
基本上只有专门用于网络诊断的 DNS 工具包,才会自己实现 DNS 协议。
操作系统中还有一个特殊文件: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 解析。
传统的 DNS 协议因为使用了明文的 UDP 协议,很容易被劫持。顺应移动互联网的兴起,目前一种新型的 DNS 协议——HTTPDNS 应用越来越广泛,国内的阿里云腾讯云都提供了这项功能。
HTTPDNS 通过 HTTP 协议直接向权威 DNS 服务器发起请求,绕过了一堆中间的 DNS 递归解析器。好处有二:
HTTPDNS 协议需要程序自己引入 SDK,或者直接请求 HTTP API。
操作系统的 DNS 解析器通常会允许我们配置多个上游 Name Servers,比如 Linux 就是通过 /etc/resolv.conf
配置 DNS 服务器的。
$ 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,第二个配外网!!!
上一小节给出的 /etc/resolv.conf
文件内容的末尾,有这样一行: search lan
,它指定的,是所谓的 DNS 搜索域。
讲到 DNS 搜索域
,就不得不提到一个名词:全限定域名(Full Qulified Domain Name, FQDN),即一个域名的完整名称,www.baidu.com
。
一个普通的域名,有下列四种可能:
www.baidu.com.
: 末尾的 .
表示根域,说明 www.baidu.com
是一个 FQDN,因此不会使用搜索域!www.baidu.com
: 末尾没 .
,但是域名包含不止一个 .
。首先当作 FQDN 进行查询,没查找再按顺序在各搜索域中查询。
/etc/resolv.conf
的 options
参数中,可以指定域名中包含 .
的临界个数,默认是 1.local
: 不包含 .
,被当作 host
名称,非 FQDN。首先在 /etc/hosts
中查找,没找到的话,再按顺序在各搜索域中查找。上述搜索顺序可以通过
host -v <domain-name>
进行测试,该命令会输出它尝试过的所有 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
的示例如下:
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,或者在 Linux 中使用 man resolv.conf
命令查看。
dig +trace baidu.com # 诊断 dns 的主要工具,非常强大
host -a baidu.com # host 基本就是 dig 的弱化版,不过 host 有个有点就是能打印出它测试过的所有 FQDN
nslookup baidu.com # 和 host 没啥大差别,多个交互式查询不过一般用不到
whois baidu.com # 查询域名注册信息,内网诊断用不到
详细的使用请 man dig
主流的本地 DNS 服务器中,提供 UI 界面的有 Windows DNS Server 和群晖 DNS Server,很方便,不过这两个都是操作系统绑定的。
开源的 DNS 服务器里边儿,BIND 好像是最有名的,各大 Linux 发行版自带的 dig/host/nslookup
,最初都是 Bind 提供的命令行工具。
不过为了一举两得(DNS+K8s),咱还是直接学习 CoreDNS 的使用。
CoreDNS 最大的特点是灵活,可以很方便地给它编写插件以提供新功能。功能非常强大,相比传统 DNS 服务器,它非常“现代化”。在 K8s 中它被用于提供服务发现功能。
接下来以 CoreDNS 为例,讲述如何配置一个 DNS 服务器,添加私有的 DNS 记录,并设置转发规则以解析公网域名。
CoreDNS 因为是 Go 语言写的,编译结果是单个可执行文件,它默认以当前文件夹下的 Corefile 为配置文件。以 kubernetes 中的 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 本身只相当于一个查询器/缓存,因此它可以很方便地扩缩容。
现在清楚了 Corefile 的结构,让我们来设计一个通过文件配置 DNS 条目的 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 中定义的 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 正解資料庫檔案的設定
test-env.local
也是一样的格式,根据上面的模板修改就行。这两个配置文件和 Corefile 放在同一个目录下:
root@test-ubuntu:~/dns-server# tree
.
├── coredns # coredns binary
├── Corefile
├── dev-env.local
└── test-env.local
然后通过 ./coredns
启动 coredns。通过 dig 检验:
可以看到 ftp.dev-env.local
已经被成功解析了。
CoreDNS 提供的预编译版本,不包含 External Plugins 中列出的部分,如果你需要,可以自行修改 plugin.cfg
,然后手动编译。
不得不说 Go 语言的编译,比 C 语言是方便太多了。自动拉取依赖,一行命令编译!只要配好 GOPROXY,启用可选插件其实相当简单。
单台 DNS 服务器的性能是有限的,而且存在单点故障问题。因此在要求高可用或者高性能的情况下,就需要设置 DNS 集群。
虽然说 CoreDNS 本身也支持各种 DNS Zone 传输,主从 DNS 服务器等功能,不过我想最简单的,可能还是直接用 K8s。
直接用 ConfigMap 存配置,通过 Deployment 扩容就行,多方便。
要修改起来更方便,还可以启用可选插件:redis,直接把配置以 json 的形式存在 redis 里,通过 redis-desktop-manager 进行查看与修改。
Linux网络学习笔记(二):域名解析(DNS)——以 CoreDNS 为例
原文:https://www.cnblogs.com/kirito-c/p/12076274.html