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字段进行设置,可以设置如下信息
    1. nameservers:一组dns服务器的列表,最多可设置3个
    2. searchs:一组用于域名搜索的dns域名后缀,最多6个
    3. 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个服务器。

posted @ 2022-06-18 17:04  大数据老司机  阅读(6015)  评论(0编辑  收藏  举报