Kubernetes(k8s)DNS(CoreDNS)介绍
一、DNS服务概述
service发现是k8s中的一个重要机制,其基本功能为:在集群内通过服务名对服务进行访问,即需要完成从服务名到ClusterIP的解析。
k8s主要有两种service发现机制:环境变量和DNS。没有DNS服务的时候,k8s会采用环境变量的形式,但一旦有多个service,环境变量会变复杂,为解决该问题,我们使用DNS服务。
DNS服务在kubernetes中经历了三个阶段(SkyDNS-》KubeDNS-》CoreDNS):
- 【第一阶段】在kubernetes 1.2版本时,dns服务使用的是由SkyDNS提供的,由4个容器组成:kube2sky、skydns、etcd和healthz。etcd存储dns记录;kube2sky监控service变化,生成dns记录;skydns读取服务,提供查询服务;healthz提供健康检查。
- 【第二阶段】在kubernetes 1.4版本开始使用KubeDNS,有3个容器组成:kubedns、dnsmasq和sidecar。kubedns监控service变化,并记录到内存(存到内存提高性能)中;dnsmasq获取dns记录,提供dns缓存,提供dns查询服务;sidecar提供健康检查。
- 【第三阶段】从kubernetes >=1.11版本开始,dns服务有CoreDNS提供,coredns支持自定义dns记录及配置upstream dns server,可以统一管理内部dns和物理dns。coredns只有一个coredns容器。下面是coredns的架构。
二、CoreDNS配置解析
下面是coredns的配置模板
apiVersion: v1
kind: ConfigMap
metadata:
name: coredns
namespace: namespace-test
data:
Corefile: |
.:53 {
errors
health
ready
kubernetes cluster.local 10.200.0.0/16 {
pods insecure
upstream 114.114.114.114
fallthrough in-addr.arpa ip6.arpa
namespaces namespace-test
}
prometheus :9153
forward . /etc/resolv.conf
cache 30
loop
reload
loadbalance
}
CoreDNS的主要功能是通过插件系统实现的。它实现了一种链式插件的结构,将dns的逻辑抽象成了一个个插件。常见的插件如下:
- loadbalance:提供基于dns的负载均衡功能
- loop:检测在dns解析过程中出现的简单循环问题
- cache:提供前端缓存功能
- health:对Endpoint进行健康检查
- kubernetes:从kubernetes中读取zone数据
- etcd:从etcd读取zone数据,可以用于自定义域名记录
- file:从文件中读取zone数据
- hosts:使用/etc/hosts文件或者其他文件读取zone数据,可以用于自定义域名记录
- auto:从磁盘中自动加载区域文件
- reload:定时自动重新加载Corefile配置文件的内容
- forward:转发域名查询到上游dns服务器
- proxy:转发特定的域名查询到多个其他dns服务器,同时提供到多个dns服务器的负载均衡功能
- prometheus:为prometheus系统提供采集性能指标数据的URL
- pprof:在URL路径/debug/pprof下提供运行是的西能数据
- log:对dns查询进行日志记录
- errors:对错误信息镜像日志记录
三、Pod的dns策略
1)Pod dns策略
- Default: 继承Pod所在宿主机的DNS设置
- ClusterFirst:优先使用kubernetes环境的dns服务,将无法解析的域名转发到从宿主机继承的dns服务器
- ClusterFirstWithHostNet:和ClusterFirst相同,对于以hostNetwork模式运行的Pod应明确知道使用该策略
None: 忽略kubernetes环境的dns配置,通过spec.dnsConfig自定义DNS配置 - 自定义Dns配置可以通过spec.dnsConfig字段进行设置,可以设置如下信息
- nameservers:一组dns服务器的列表,最多可设置3个
- searchs:一组用于域名搜索的dns域名后缀,最多6个
- options:配置其他可选参数,例如ndots、timeout等
例如:
spec:
dnsPolicy: "None"
dnsConfig:
nameservers:
- 1.2.3.4
searchs:
- xx.ns1.svc.cluster.local
- xx.daemon.com
options:
- name: ndots
values: "2"
pod被创建后,容器内的/etc/resolv.conf会根据这个信息进行配置。
2)测试解析结果
之前安装k8s集群的时候就已经安装过CoreDNS,所以这里就不重复讲解安装了,不清楚的,看这里
创建nslookup服务
$ cat >busybox.yaml<<EOF
apiVersion: v1
kind: Pod
metadata:
name: busybox
namespace: default
spec:
containers:
- name: busybox
image: busybox:1.28
command:
- sleep
- "3600"
imagePullPolicy: IfNotPresent
restartPolicy: Always
EOF
查看并验证
$ kubectl create -f busybox.yaml
$ kubectl get pods busybox
$ kubectl exec busybox -- cat /etc/resolv.conf
容器内 resolv 文件的配置
nameserver 10.1.0.10
search default.svc.cluster.local svc.cluster.local cluster.local
options ndots:5
ndots:5:如果查询的域名包含的点 “.” 不到 5 个,那么进行 DNS 查找,将使用非完全限定名称(或者叫绝对域名),如果你查询的域名包含点数大于等于 5,那么 DNS 查询,默认会使用绝对域名进行查询。
Kubernetes 域名的全称,必须是 service-name.namespace.svc.cluster.local 这种模式,服务名。
$ kubectl exec -ti busybox -- nslookup kubernetes.default
四、测试CoreDNS
1)pod验证
现在我们来创建一个busybox的pod,测试一下pod内是否可以解析
$ cat >busybox.yaml<<EOF
apiVersion: v1
kind: Pod
metadata:
name: busybox
namespace: default
spec:
containers:
- name: busybox
image: busybox:1.28
command:
- sleep
- "3600"
imagePullPolicy: IfNotPresent
restartPolicy: Always
EOF
创建并测试解析kubernetes.default
$ kubectl apply -f busybox.yaml
$ kubectl get pods busybox
$ kubectl exec busybox -- cat /etc/resolv.conf
$ kubectl exec -ti busybox -- nslookup kubernetes.default
$ kubectl exec -ti busybox -- nslookup kubernetes.default.svc.cluster.local
2)创建service和Deployment来验证
cat >dns-Service-Deployment-test001.yaml<<EOF
apiVersion: v1
kind: Service
metadata:
name: nginx-svc-old
labels:
app: nginx-svc
spec:
selector:
app: nginx
ports:
- protocol: TCP
port: 80
targetPort: 80
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-old
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
EOF
创建
$ kubectl apply -f dns-Service-Deployment-test001.yaml
$ kubectl get pod|grep nginx-old
$ kubectl get svc nginx-svc-old
直接在宿主机上验证
$ nslookup nginx-svc-old.default.svc
发现直接在宿主机上是不能解析域名的。然后用以下yaml创建了一个busybox作为调试工具:
$ cat >dns-Deployment-test002.yaml<<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
name: busybox-deployment
spec:
replicas: 1
selector:
matchLabels:
app: busybox
template:
metadata:
labels:
app: busybox
spec:
restartPolicy: Always
containers:
- name: busybox
command:
- sleep
- "3600"
image: busybox
EOF
这里用的是截止2021/10/08,busybox的最新镜像。创建好之后,exec进入容器,执行测试命令。
$ kubectl apply -f dns-Deployment-test002.yaml
$ kubectl get pod|grep busybox-deployment
$ kubectl exec -ti busybox-deployment-5bc85cc8d9-gkjgj -- sh
# 访问上面service的域名
$ nslookup nginx-svc-old.default.svc
发现也是无法解析。
根据coredns解析集群内域名原理可知:
服务 a 访问服务 b,对于同一个 Namespace下,可以直接在 pod 中,通过 curl b 来访问。对于跨 Namespace 的情况,服务名后边对应 Namespace即可,比如 curl b.default。DNS 如何解析,依赖容器内 resolv 文件的配置。
查看busybox容器内的resolve.conf文件:
$ kubectl exec -ti busybox-deployment-5bc85cc8d9-gkjgj -- sh
$ nslookup nginx-svc-old.default.svc
$ cat /etc/resolv.conf
这个文件中,配置的 DNS Server,一般就是 K8S 中,coredns的 Service 的 ClusterIP,这个IP是虚拟IP,无法ping,但可以访问。
在容器内发请求时,会根据 /etc/resolv.conf 进行解析流程。选择 nameserver 10.1.0.10 进行解析,然后用nginx-svc-old ,依次带入 /etc/resolve.conf 中的 search 域,进行DNS查找,分别是:
search 内容类似如下(不同的pod,第一个域会有所不同)
search default.svc.cluster.local svc.cluster.local cluster.local
nginx-svc-old.default.svc.cluster.local -> nginx-svc-old.svc.cluster.local -> nginx-svc-old.cluster.local
$ kubectl exec -ti busybox-deployment-5bc85cc8d9-gkjgj -- sh
$ ping nginx-svc-old
$ ping nginx-svc-old.default
直到找到为止。所以,我们执行 ping nginx-svc-old,或者执行 ping nginx-svc-old.default,都可以完成DNS请求,这2个不同的操作,会分别进行不同的DNS查找步骤。
根据以上原理,查看到busybox内的域名/etc/resolv.conf没有问题,nameserver指向正确的kube-dns的service clusterIP。
这下更加怀疑core-dns有问题了。
但查看coredns日志,可以看到并没有报错:
$ kubectl get pod -n kube-system|grep dns
$ kubectl logs -f coredns-7f6cbbb7b8-cn44w -n kube-system
$ kubectl logs -f coredns-7f6cbbb7b8-gf82k -n kube-system
那就说明不是coredns问题了。。
经过一番查找,发现是busybox版本问题。busybox<=1.28.4
发现都说是busybox镜像的问题,从1.28.4以后的镜像都存在这问题。把镜像换成1.28.4试试?修改yaml版本号:
$ kubectl apply -f dns-Deployment-test002.yaml
$ kubectl get pod|grep busybox-deployment
$ kubectl exec -ti busybox-deployment-564c775bbd-g6228 -- sh
$ nslookup nginx-svc-old.default.svc
# exit
$ kubectl get svc
确实可以成功解析域名了。所以这样看还真是busybox版本问题了。
3)宿主机上解析域名验证
nameserver关键字,如果没指定nameserver就找不到DNS服务器,其它关键字是可选的。nameserver表示解析域名时使用该地址指定的主机为域名服务器。其中域名服务器是按照文件中出现的顺序来查询的,且只有当第一个nameserver没有反应时才查询下面的nameserver,一般不要指定超过3个服务器。
在宿主上/etc/resolv.conf中nameserver如下:
$ kubectl get svc -n kube-system
$ kubectl get pod -n kube-system -o wide|grep dns
$ kubectl exec -ti busybox-deployment-564c775bbd-g6228 -- cat /etc/resolv.conf
$ cat /etc/resolv.conf
后面四个是k8s dns的配置,把后面四个都放到前面来,再验证:
nameserver 10.1.0.10
nameserver 10.244.2.137
nameserver 10.244.2.138
search default.svc.cluster.local svc.cluster.local cluster.local
$ nslookup nginx-svc-old
$ cat /etc/resolv.conf
$ nslookup baidu.com
可以看到现在可以解析了。也不影响外网域名解析,但是最好还是不要指定超过3个服务器。