参考:
- 官方网站,CoreDNS: DNS and Service Discovery
- CoreDNS安装,
- CoreDNS使用手册,CoreDNS: DNS and Service Discovery
- CoreDNS源码,CoreDNS · GitHub
- CoreDNS配置,
一.Kubernetes DNS服务发展史
从Kubernetes 1.11开始,可使用CoreDNS作为Kubernetes的DNS插件进入GA状态,Kubernetes推荐使用CoreDNS作为集群内的DNS服务。 我们先看一下Kubernetes DNS服务的发展历程。
1.1 Kubernetes 1.3之前的版本 – skyDNS
Kubernetes 1.3之前的版本使用skyDNS作为DNS服务,这个有点久远了。Kubernetes的DNS服务由kube2sky、skyDNS、etcd组成。 kube2sky通过kube-apiserver监听集群中Service的变化,将生成的DNS记录信息更新到etcd中,而skyDNS将从etcd中获取数据对外提供DNS的查询服务。
1.2 Kubernetes 1.3版本开始 – kubeDNS
Kubernetes 1.3开始使用kubeDNS和dnsmasq替换了原来的kube2sky和skyDNS,不再使用etcd,而是将DNS记录直接存放在内存中,通过dnsmasq的缓存功能提高DNS的查询效率。下图是描述了Kubernetes使用kubeDNS实现服务发现的整体架构:
1.3 Kubernetes 1.11版本开始 – CoreDNS进入GA
从Kubernetes 1.11开始,可使用CoreDNS作为Kubernetes的DNS插件进入GA状态,Kubernetes推荐使用CoreDNS作为集群内的DNS服务。 CoreDNS从2017年初就成为了CNCF的的孵化项目,CoreDNS的特点就是十分灵活和可扩展的插件机制,
各种插件实现:
不同的功能,如重定向、定制DNS记录、记录日志等等。下图描述了CoreDNS的整体架构:
二、CoreDNS简介
Kubernetes包括用于服务发现的DNS服务器Kube-DNS。 该DNS服务器利用SkyDNS的库来为Kubernetes pod和服务提供DNS请求。SkyDNS2的作者,Miek Gieben,创建了一个新的DNS服务器,CoreDNS,它采用更模块化,可扩展的框架构建。 Infoblox已经与Miek合作,将此DNS服务器作为Kube-DNS的替代品。
。虽然它一开始作为Web服务器,但是Caddy并不是专门针对HTTP协议的,而是构建了一个基于CoreDNS的理想框架。
在这种灵活的模型中添加对Kubernetes的支持,相当于创建了一个Kubernetes中间件。该中间件使用Kubernetes API来满足针对特定Kubernetes pod或服务的DNS请求。而且由于Kube-DNS作为Kubernetes的另一项服务,kubelet和Kube-DNS之间没有紧密的绑定。您只需要将DNS服务的IP地址和域名传递给kubelet,而Kubernetes并不关心谁在实际处理该IP请求。
1、CoreDNS支持行为
1.0.0版本主要遵循Kube-DNS的当前行为。 CoreDNS的005及更高版本实现了完整的规范和更多功能。
- A记录(正常的Service分配了一个名为my-svc.my-namespace.svc.cluster.local的DNS A记录。 这解决了服务的集群IP)
- “headless”(没有集群IP)的Service也分配了一个名为my-svc.my-namespace.svc.cluster.local的DNS A记录。 与普通服务不同,这解决了Service选择了pods的一组IP。 客户预计将从这ip集合中消耗集合或使用标准循环选择。
- 针对名为正常或无头服务的端口创建的SRV记录,对于每个命名的端口,SRV记录的格式为_my-port-name._my-port-protocol.my-svc.my-namespace.svc.cluster.local。对于常规服务,这将解析为端口号和CNAME:my-svc.my-namespace.svc.cluster.local;对于无头服务,这解决了多个答案,一个用于支持服务的每个pod,并包含端口号还有格式为auto-generated-name.my-svc.my-namespace.svc.cluster.local 的pod的CNAME 。SRV记录包含它们中的“svc”段,对于省略“svc”段的旧式CNAME不支持。
- 作为Service一部分的endpoints的A记录(比如“pets”的记录)
- pod的Spec中描述的A记录
- 还有就是用来发现正在使用的DNS模式版本的TXT记录
所有群集中不需要pod A记录支持,默认情况下禁用。 此外,CoreDNS对此用例的支持超出了在Kube-DNS中找到的标准行为。
在Kube-DNS中,这些记录不反映集群的状态,例如,对w-x-y-z.namespace.pod.cluster.local的任何查询将返回带有w.x.y.z(ip)的A记录,即使该IP不属于指定的命名空间,甚至不属于集群地址空间。最初的想法是启用对* .namespace.pod.cluster.local这样的域使用通配符SSL证书。
CoreDNS集成了提供pod验证的选项,验证返回的IP地址w.x.y.z实际上是指定命名空间中的pod的IP。他防止在命名空间中欺骗DNS名称。 然而,它确实会大大增加CoreDNS实例的内存占用,因为现在它需要观察所有的pod,而不仅仅是服务端点。
2、架构
整个 CoreDNS 服务都建立在一个使用 Go 编写的 HTTP/2 Web 服务器 Caddy · GitHub 上,CoreDNS 整个项目可以作为一个 Caddy 的教科书用法。
CoreDNS 的大多数功能都是由插件来实现的,插件和服务本身都使用了 Caddy 提供的一些功能,所以项目本身也不是特别的复杂。
3、插件
作为基于 Caddy 的 Web 服务器,CoreDNS 实现了一个插件链的架构,将很多 DNS 相关的逻辑都抽象成了一层一层的插件,包括 Kubernetes 等功能,每一个插件都是一个遵循如下协议的结构体:
type (
Plugin func(Handler) Handler
Handler interface {
ServeDNS(context.Context, dns.ResponseWriter, *dns.Msg) (int, error)
Name() string
}
)
所以只需要为插件实现 ServeDNS
以及 Name
这两个接口并且写一些用于配置的代码就可以将插件集成到 CoreDNS 中。
4、Corefile
另一个 CoreDNS 的特点就是它能够通过简单易懂的 DSL 定义 DNS 服务,在 Corefile 中就可以组合多个插件对外提供服务:
coredns.io:5300 {
file db.coredns.io
}
example.io:53 {
log
errors
file db.example.io
}
example.net:53 {
file db.example.net
}
.:53 {
kubernetes
proxy . 8.8.8.8
log
errors
cache
}
对于以上的配置文件,CoreDNS 会根据每一个代码块前面的区和端点对外暴露两个端点提供服务:
该配置文件对外暴露了两个 DNS 服务,其中一个监听在 5300 端口,另一个在 53 端口,请求这两个服务时会根据不同的域名选择不同区中的插件进行处理。
原理
CoreDNS 可以通过四种方式对外直接提供 DNS 服务,分别是 UDP、gRPC、HTTPS 和 TLS:
但是无论哪种类型的 DNS 服务,最终队会调用以下的 ServeDNS
方法,为服务的调用者提供 DNS 服务:
func (s *Server) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) {
m, _ := edns.Version(r)
ctx, _ := incrementDepthAndCheck(ctx)
b := r.Question[0].Name
var off int
var end bool
var dshandler *Config
w = request.NewScrubWriter(r, w)
for {
if h, ok := s.zones[string(b[:l])]; ok {
ctx = context.WithValue(ctx, plugin.ServerCtx{}, s.Addr)
if r.Question[0].Qtype != dns.TypeDS {
rcode, _ := h.pluginChain.ServeDNS(ctx, w, r)
dshandler = h
}
off, end = dns.NextLabel(q, off)
if end {
break
}
}
if r.Question[0].Qtype == dns.TypeDS && dshandler != nil && dshandler.pluginChain != nil {
rcode, _ := dshandler.pluginChain.ServeDNS(ctx, w, r)
plugin.ClientWrite(rcode)
return
}
if h, ok := s.zones["."]; ok && h.pluginChain != nil {
ctx = context.WithValue(ctx, plugin.ServerCtx{}, s.Addr)
rcode, _ := h.pluginChain.ServeDNS(ctx, w, r)
plugin.ClientWrite(rcode)
return
}
}
在上述这个已经被简化的复杂函数中,最重要的就是调用了『插件链』的 ServeDNS
方法,将来源的请求交给一系列插件进行处理,如果我们使用以下的文件作为 Corefile:
example.org {
file /usr/local/etc/coredns/example.org
prometheus # enable metrics
errors # show errors
log # enable query logs
}
那么在 CoreDNS 服务启动时,对于当前的 example.org
这个组,它会依次加载 file
、log
、errors
和 prometheus
几个插件,这里的顺序是由 zdirectives.go 文件定义的,启动的顺序是从下到上:
var Directives = []string{
// ...
"prometheus",
"errors",
"log",
// ...
"file",
// ...
"whoami",
"on",
}
因为启动的时候会按照从下到上的顺序依次『包装』每一个插件,所以在真正调用时就是从上到下执行的,这就是因为 NewServer
方法中对插件进行了组合:
func NewServer(addr string, group []*Config) (*Server, error) {
s := &Server{
Addr: addr,
zones: make(map[string]*Config),
connTimeout: 5 * time.Second,
}
for _, site := range group {
s.zones[site.Zone] = site
if site.registry != nil {
for name := range enableChaos {
if _, ok := site.registry[name]; ok {
s.classChaos = true
break
}
}
}
var stack plugin.Handler
for i := len(site.Plugin) - 1; i >= 0; i-- {
stack = site.Plugin[i](stack)
site.registerHandler(stack)
}
site.pluginChain = stack
}
return s, nil
}
对于 Corefile 里面的每一个配置组,NewServer
都会讲配置组中提及的插件按照一定的顺序组合起来,原理跟 Rack Middleware 的机制非常相似,插件 Plugin
其实就是一个出入参数都是 Handler
的函数:
type (
Plugin func(Handler) Handler
Handler interface {
ServeDNS(context.Context, dns.ResponseWriter, *dns.Msg) (int, error)
Name() string
}
)
所以我们可以将它们叠成堆栈的方式对它们进行操作,这样在最后就会形成一个插件的调用链,在每个插件执行方法时都可以通过 NextOrFailure
函数调用下一个插件的 ServerDNS
方法:
func NextOrFailure(name string, next Handler, ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
if next != nil {
if span := ot.SpanFromContext(ctx); span != nil {
child := span.Tracer().StartSpan(next.Name(), ot.ChildOf(span.Context()))
defer child.Finish()
ctx = ot.ContextWithSpan(ctx, child)
}
return next.ServeDNS(ctx, w, r)
}
return dns.RcodeServerFailure, Error(name, errors.New("no next plugin found"))
}
除了通过 ServeDNS
调用下一个插件之外,我们也可以调用 WriteMsg
方法并结束整个调用链。
从插件的堆叠到顺序调用以及错误处理,我们对 CoreDNS 的工作原理已经非常清楚了,接下来我们可以简单介绍几个插件的作用。
三、在kubernetes中部署coredns
1、下载coredns部署包并说明
https://github.com/coredns/deployment/tree/master/kubernetes
注意和k8s的版本对应关系:https://github.com/coredns/deployment/blob/master/kubernetes/CoreDNS-k8s_version.md
主要有几个文件:
deploy.sh是一个便捷的脚本,用于生成用于在当前运行标准kube-dns的集群上运行CoreDNS的清单。使用coredns.yaml.sed文件作为模板,它创建一个ConfigMap和一个CoreDNS deployment,然后更新 Kube-DNS service selector以使用CoreDNS deployment。 通过重新使用现有服务,服务请求不会中断。
脚本不会删除kube-dns的deployment或replication controller - 您必须手动执行:
kubectl delete --namespace=kube-system deployment kube-dns
要使用它,只需将它们放在同一目录中,然后运行deploy.sh脚本,将其传递给您的服务CIDR(10.3.0.0/24)。 这将生成具有必要Corefile的ConfigMap。 它还将查找现有的kube-dns服务的集群IP。
[root@k8s-master conf.d]# etcdctl ls /k8s/network/subnets
/k8s/network/subnets/10.0.24.0-24
/k8s/network/subnets/10.0.86.0-24
/k8s/network/subnets/10.0.35.0-24
(注意:以上原始脚本只适用于当前kubernetes集群含有kube-dns的情况,如果没有需要修改下脚本
#!/bin/bash
# Deploys CoreDNS to a cluster currently running Kube-DNS.
SERVICE_CIDR=$1
CLUSTER_DOMAIN=${2:-cluster.local}
YAML_TEMPLATE=${3:-`pwd`/coredns.yaml.sed}
YAML=${4:-`pwd`/coredns.yaml}
if [[ -z $SERVICE_CIDR ]]; then
echo "Usage: $0 SERVICE-CIDR [ CLUSTER-DOMAIN ] [ YAML-TEMPLATE ] [ YAML ]"
exit 1
fi
#CLUSTER_DNS_IP=$(kubectl get service --namespace kube-system kube-dns -o jsonpath="{.spec.clusterIP}")
CLUSTER_DNS_IP=10.0.24.200
默认情况下CLUSTER_DNS_IP是自动获取kube-dns的集群ip的,但是由于没有部署kube-dns所以只能手动指定一个集群ip了。
执行: ./deploy.sh 10.0.0.0/24 cluster.local
以上脚本执行后可以看到预览的效果。
仔细观察上面的Corefile部分,这是一个在端口53上运行CoreDNS并为Kubernetes提供cluster.local域的示例
.:53 { errors log stdout health kubernetes cluster.local 10.3.0.0/24 proxy . /etc/resolv.conf cache 30 }
1)errors官方没有明确解释,后面研究
2)log stdout:日志中间件配置为将日志写入STDOUT
3)health:健康检查,提供了指定端口(默认为8080)上的HTTP端点,如果实例是健康的,则返回“OK”。
4)cluster.local:CoreDNS为kubernetes提供的域,10.3.0.0/24这告诉Kubernetes中间件它负责为反向区域提供PTR请求0.0.3.10.in-addr.arpa ..换句话说,这是允许反向DNS解析服务(我们经常使用到得DNS服务器里面有两个区域,即“正向查找区域”和“反向查找区域”,正向查找区域就是我们通常所说的域名解析,反向查找区域即是这里所说的IP反向解析,它的作用就是通过查询IP地址的PTR记录来得到该IP地址指向的域名,当然,要成功得到域名就必需要有该IP地址的PTR记录。PTR记录是邮件交换记录的一种,邮件交换记录中有A记录和PTR记录,A记录解析名字到地址,而PTR记录解析地址到名字。地址是指一个客户端的IP地址,名字是指一个客户的完全合格域名。通过对PTR记录的查询,达到反查的目的。)
5)proxy:这可以配置多个upstream 域名服务器,也可以用于延迟查找 /etc/resolv.conf 中定义的域名服务器
6)cache:这允许缓存两个响应结果,一个是肯定结果(即,查询返回一个结果)和否定结果(查询返回“没有这样的域”),具有单独的高速缓存大小和TTLs。
2、现在安装coredns到kubernetes中
# ./deploy.sh -r 10.0.0.0/16 -i 192.168.10.90 -d cluster.local -t coredns.yaml.sed -s > coredns.yaml
# .kubectl apply -f coredns.yaml
或者直接执行:
# ./deploy.sh -r 10.0.0.0/16 -i 192.168.10.90 -d cluster.local -t coredns.yaml.sed -s | kubectl apply -f -
查看service
[root@k8s-master coredns]# kubectl get service -l k8s-app=kube-dns --namespace=kube-system
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kube-dns ClusterIP 192.168.10.90 <none> 53/UDP,53/TCP,9153/TCP 15d
查看coredns的Pod,确认所有Pod都处于Running状态:
kubectl get pods -n kube-system -l k8s-app=kube-dns
查看日志:# kubectl logs -f coredns-6cc7bf59f4-4rfq8 --namespace=kube-system
3、修改cluster-dns
方式一kubelet统一修改:
kubelet的启动参数添加--cluster-dns配置:修改master节点和所有node节点--cluster-dns配置,修改内容如红色所注,与上面的Corefile中的值对应。
方式二:在pod启动添加
apiVersion: v1
kind: Pod
metadata:
name: springbootweb
spec:
containers:
- name: springbootweb
image: registry.xxxxx.com/springboot:latest
ports:
- containerPort: 9081
imagePullSecrets:
- name: registry-key-secret
dnsConfig:
nameservers:
- 172.16.1.21
searches:
- ns1.svc.cluster.local
- my.dns.search.suffix
options:
- name: ndots
value: "2"
- name: edns0
通过上述配置创建 Pod
之后,执行 kubectl exec dockerId cat /etc/resolv.conf
命令即可看到额外的配置项目
即在 nameservers
中多了 172.16.1.21、 search
中多了 ns1.svc.cluster.local
和 my.dns.search.suffix
两项值,及多了 options ndots:2 edns0
配置。
因此,直接通过 dnsConfig
进行配置后,默认是追加到当前默认配置中。
4、测试CoreDNS
现在我们来创建一个wepapp的pod和service,测试一下coredns是否起作用
[root@k8s-master conf.d]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE
webapp-nrz4t 1/1 Running 0 1d 10.0.35.3 192.168.10.39
webapp-zp69q 1/1 Running 0 1d 10.0.24.4 192.168.10.50
[root@k8s-master ~]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 192.168.0.1 <none> 443/TCP 28d
webapp ClusterIP 192.168.14.242 <none> 9081/TCP 17d
webapp2 ClusterIP 192.168.22.2 <none> 9082/TCP 17d
[root@k8s-master ~]# curl 192.168.22.2:9082
Hello world
1、检查集群的pod /etc/resolv.conf是否生效:
首先进入这个集群内的另一个pod
kubectl exec -it webapp-nrz4t /bin/sh 或者 docker exec -it 0d0874df9e15 /bin/sh
$ cat /etc/resolv.conf
sh-4.2# cat /etc/resolv.conf
nameserver 192.168.10.90
search default.svc.cluster.local svc.cluster.local cluster.local
options ndots:5
2、curl测试服务:
curl webapp.default.svc.cluster.local:9081
3、在master的主机上修改 /etc/resolv.conf,增加一行:nameserver 192.168.10.90后执行
curl webapp.default.svc.cluster.local:9081
通过域名访问成功!
三、DNS 策略
我们在kubelet的启动参数添加--cluster-dns配置--cluster-dns=172.16.10.254。在Kubelet配置中通过使用–cluster-dns=10.10.10.2参数指定的coredns地址会被写入容器的/etc/resolv.conf配置中。
在 kubernetes 中还提供了 dnsPolicy
决定 Pod
内预设4种DNS策略:
None,无任何策略:表示空的DNS设置,这种方式一般用于想要自定义 DNS 配置的场景,往往需要和 dnsConfig 配合一起使用达到自定义 DNS 的目的。
Default,默认: 此种方式是让kubelet来决定使用何种DNS策略。而kubelet默认的方式,就是使用宿主机的/etc/resolv.conf文件。同时,kubelet也可以配置指定的DNS策略文件,使用kubelet参数即可,如:–resolv-conf=/etc/resolv.conf
ClusterFirst, 集群 DNS 优先:此种方式是使用kubernets集群内部中的kubedns或coredns服务进行域名解析。若解析不成功,才会使用宿主机的DNS配置来进行解析。
ClusterFistWithHostNet,集群 DNS 优先,并伴随着使用宿主机网络:在某些场景下,我们的 POD 是用 HOST 模式启动的(HOST模式,是共享宿主机网络的),一旦用 HOST 模式,表示这个 POD 中的所有容器,都要使用宿主机的 /etc/resolv.conf 配置进行DNS查询,但如果你想使用了 HOST 模式,还继续使用 Kubernetes 的DNS服务,那就将 dnsPolicy 设置为 ClusterFirstWithHostNet。
1、无策略 (None)
清除 Pod 预设 DNS 配置,当 dnsPolicy 设置成为这个值之后, kubernetes 不会为 Pod 预先加载任何逻辑用于判定得到 DNS 的配置。因此若将 dnsPolicy 设置为 None , 为了避免 Pod 里面没有 DNS 配置,最好通过 dnsConfig 来描述自定义的 DNS 参数。如下所示:
apiVersion: v1
kind: Pod
metadata:
name: springbootweb
spec:
containers:
- name: springbootweb
image: registry.xxxx.com/springboot:latest
ports:
- containerPort: 9081
imagePullSecrets:
- name: registry-key-secret
dnsConfig:
nameservers:
- 172.16.1.21
searches:
- ns1.svc.cluster.local
- my.dns.search.suffix
options:
- name: ndots
value: "2"
- name: edns0
通过上述配置创建 Pod
之后,执行 kubectl exec demo cat /etc/resolv.conf
命令即可看到额外的配置项目,如下:
2、默认预设 (Default)
Pod 里面的 DNS 配置继承了宿主机上的 DNS 配置。即,该 Pod 的 DNS 配置与宿主机完全一致。
apiVersion: v1
kind: Pod
metadata:
name: springbootweb
spec:
containers:
- name: springbootweb
image: registry.xxxx.com/springboot:latest
ports:
- containerPort: 9081
imagePullSecrets:
- name: registry-key-secret
dnsPolicy: Default
查看到宿主机上的配置如下: cat /etc/resolv.conf
通过上述配置创建 Pod
之后即可看到额外的配置项目,如下:
注意:这个会覆盖--cluster-dns配置
3、集群优先 (ClusterFirst)
与 Default 相反,会预先使用 kube-dns
(或 CoreDNS
) 的信息当预设置参数写入到该 Pod 内的DNS配置。
apiVersion: v1
kind: Pod
metadata:
name: springbootweb
spec:
containers:
- name: springbootweb
image: registry.xxxxx.com/springboot:latest
ports:
- containerPort: 9081
imagePullSecrets:
- name: registry-key-secret
dnsPolicy: ClusterFirst
通过上述配置创建 Pod
之后,执行 kubectl exec
springbootweb cat /etc/resolv.conf
命令即可看到额外的配置项目,如下:
nameserver 172.16.10.254
search default.svc.cluster.local svc.cluster.local cluster.local
options ndots:5
注
如设置了hostNetwork = true
时,ClusterFirst
会被强制转化为Default
。如下:
apiVersion: v1
kind: Pod
metadata:
name: springbootweb
spec:
containers:
- name: springbootweb
image: registry.xxxxx.com/springboot:latest
ports:
- containerPort: 9081
imagePullSecrets:
- name: registry-key-secret
hostNetwork: true
dnsPolicy: ClusterFirst
通过上述配置创建 Pod
之后,执行 kubectl exec springbootweb cat /etc/resolv.conf
命令即可看到额外的配置项目,如下:
nameserver 172.16.1.21
nameserver 119.29.29.29
注
设置 hostNetwork = true
之后,会让 Pod 与该节点公用相同的网络空间(网卡/路由等)
4、宿主机与 Kubernetes 共存 ( ClusterFirstWithHostNet )
同时使用 hostNetwork
与 kube-dns
作为 Pod 预设 DNS 配置。
apiVersion: v1
kind: Pod
metadata:
name: springbootweb
spec:
containers:
- name: springbootweb
image: registry.xxxx.com/springboot:latest
ports:
- containerPort: 9081
imagePullSecrets:
- name: registry-key-secret
hostNetwork: true
dnsPolicy: ClusterFirstWithHostNet
通过上述配置创建 Pod
之后,执行 kubectl exec demo cat /etc/resolv.conf
命令即可看到额外的配置项目,如下:
四、coredns 自定义解析域名
1、CoreDNS ConfigMap选项
CoreDNS是一个模块化和可插拔的DNS服务器,每个插件都为CoreDNS添加了新功能。这可以通过维护Corefile来配置,Corefile是CoreDNS配置文件。集群管理员可以修改CoreDNS Corefile的ConfigMap以更改服务发现的工作方式。
在Kubernetes中,CoreDNS安装了以下默认的Corefile配置。先来看看默认的CoreDns的配置文件:
kubectl edit configmap coredns -n kube-system, 红框部分是新加的:
- error: 错误记录到stdout
- health:CoreDNS的运行状况报告为http:// localhost:8080 / health
- kubernetes:CoreDNS将根据Kubernetes服务和pod的IP回复DNS查询
- prometheus:CoreDNS的度量标准可以在http://localhost:9153/Prometheus格式的指标中找到
- forward:任何不在Kubernetes集群域内的查询都将转发到预定义的解析器(/etc/resolv.conf)
- cache:启用前端缓存
- loop:检测简单的转发循环,如果找到循环则停止CoreDNS进程
- reload:允许自动重新加载已更改的Corefile。编辑ConfigMap配置后,请等待两分钟以使更改生效
- loadbalance:这是一个循环DNS负载均衡器,可以在答案中随机化A,AAAA和MX记录的顺序
注意:CoreDNS 1.5.0以后不在使用proxy关键字,而是使用forward。
我们强制所有非集群 DNS 查找通过特定的域名服务器(位于172.16.1.21)来解析,将forward 指向域名服务器,而不是 /etc/resolv.conf。
如果域名后缀都为.xxxapi.com,并通过172.16.1.21域名服务器来解析,
在Corefile
配置如下:
xxxapi.com:53 {
errors
cache 30
forward . 172.16.1.21
}
即上面截图。
然后可以访问进入容器:docker exec -it 7d9ba3b6acdf /bin/sh 可以ping通了:
问题排查技巧
如果执行 nslookup 命令失败,检查如下内容:
1、先检查本地 DNS 配置
查看配置文件 resolv.conf。
按照如下方法(注意搜索路径可能会因为云提供商不同而变化)验证搜索路径和 Name Server 的建立:
nameserver 192.168.10.90
search default.svc.cluster.local svc.cluster.local cluster.local
options ndots:5
2、快速诊断
出现类似如下指示的错误,说明 kube-dns 插件或相关 Service 存在问题:
检查service是否正常运行:
kubectl get svc --namespace=kube-system
或者检查是否 DNS Pod 正在运行
使用 kubectl get pods
命令验证 DNS Pod 正在运行:
kubectl get pods --namespace=kube-system -l k8s-app=kube-dns
应该能够看到类似如下信息:
如果看到没有 Pod 运行,或 Pod 失败/结束,DNS 插件不能默认部署到当前的环境,必须手动部署。
或者查看service的Endpoint是否正常:
kubectl get ep kube-dns --namespace=kube-system
如果没有看到 Endpoint,查看 调试 Service 文档 中的 Endpoint 段内容。
3、检查 DNS Pod 中的错误信息
使用 kubectl logs
命令查看 DNS 后台进程的日志:
kubectl logs coredns-6cc7bf59f4-vj7cc -n kube-system
看到错误信息:
eflector.go:134] pkg/mod/k8s.io/client-go@v10.0.0+incompatible/tools/cache/reflector.go:95: Failed to list *v1.Namespace: Get https://192.168.0.1:443/api/v1/namespace
2.168.0.1:443/api/v1/endpoints?limit=500&resourceVersion=0: x509: certificate is valid for 192.168.10.50, 10.0.0.1, not 192.168.0.1
E0710 16:00:58.986103 1 reflector.go:134] pkg/mod/k8s.io/client-go@v10.0.0+incompatible/tools/cache/reflector.go:95: Failed to list *v1.Namespace: Get https://192.168.0.1:443/api/v1/namespaces?limit=500&resourceVersion=0: x509: certificate is valid for 192.168.10.50, 10.0.0.1, not 192.168.0.1
E0710 16:00:58.989633 1 reflector.go:134] pkg/mod/k8s.io/client-go@v10.0.0+incompatible/tools/cache/reflector.go:95: Failed to list *v1.Service: Get https://192.168.0.1:443/api/v1/services?limit=500&resourceVersion=0: x509: certificate is valid for 192.168.10.50, 10.0.0.1, not 192.168.0.1
E0710 16:00:58.991655 1 reflector.go:134] pkg/mod/k8s.io/client-go@v10.0.0+incompatible/tools/cache/reflector.go:95: Failed to list *v1.Endpoints: Get https://192.168.0.1:443/api/v1/endpoints?limit=500&resourceVersion=0: x509: certificate is valid for 192.168.10.50, 10.0.0.1, not 192.168.0.1
E0710 16:00:59.990732 1 reflector.go:134] pkg/mod/k8s.io/client-go@v10.0.0+inco
原因:192.168.0.1不在证书里面
解决:需要重新设置:apiserver证书
masterssl.cnf文件的示例如下:IP.3 = 192.168.0.1
[req]
req_extensions = v3_req
distinguished_name = req_distinguished_name
[req_distinguished_name]
[ v3_req ]
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
subjectAltName = @alt_names
[alt_names]
DNS.1 = kubernetes
DNS.2 = kubernetes.default
DNS.3 = kubernetes.default.svc
DNS.4 = kubernetes.default.svc.cluster.local
IP.1 = ${K8S_SERVICE_IP}
IP.2 = ${MASTER_IPV4}IP.3 = 192.168.0.1
错误信息:
E0712 02:25:45.326123 1 reflector.go:134] pkg/mod/k8s.io/client-go@v10.0.0+incompatible/tools/cache/reflector.go:95: Failed to list *v1.Service: Unauthorized
E0712 02:25:45.327038 1 reflector.go:134] pkg/mod/k8s.io/client-go@v10.0.0+incompatible/tools/cache/reflector.go:95: Failed to list *v1.Endpoints: Unauthorized
E0712 02:25:45.328029 1 reflector.go:134] pkg/mod/k8s.io/client-go@v10.0.0+incompatible/tools/cache/reflector.go:95: Failed to list *v1.Namespace: Unauthorized
E0712 02:25:46.327153 1 reflector.go:134] pkg/mod/k8s.io/client-go@v10.0.0+incompatible/tools/cache/reflector.go:95: Failed to list *v1.Service: Unauthorized
E0712 02:25:46.328210 1 reflector.go:134] pkg/mod/k8s.io/client-go@v10.0.0+incompatible/tools/cache/reflector.go:95: Failed to list *v1.Endpoints: Unauthorized
E0712 02:25:46.329160 1 reflector.go:134] pkg/mod/k8s.io/client-go@v10.0.0+incompatible/tools/cache/reflector.go:95: Failed to list *v1.Namespace: Unauthorized
E0712 02:25:47.328243 1 reflector.go:134] pkg/mod/k8s.io/client-go@v10.0.0+incompatible/tools/cache/reflector.go:95: Failed to list *v1.Service: Unauthorized
E0712 02:25:47.329143 1 reflector.go:134] pkg/mod/k8s.io/client-go@v10.0.0+incompatible/tools/cache/reflector.go:95: Failed to list *v1.Endpoints: Unauthorized
E0712 02:25:47.330086 1 reflector.go:134] pkg/mod/k8s.io/client-go@v10.0.0+incompatible/tools/cache/reflector.go:95: Failed to list *v1.Namespace: Unauthorized
原因:service account 的token认知不通过,是因为重新生成证书后,需要删除旧的token。
解决:
kubectl get secret -n kube-system
NAME TYPE DATA AGE
coredns-token-cdn9x kubernetes.io/service-account-token 3 3d
default-token-lht2v kubernetes.io/service-account-token 3 3d kubectl delete secret coredns-token-cdn9x -n kube-system
secret "coredns-token-cdn9x" deleted