kubernetes集群内部DNS解析原理

当kubernetes初始化完成后,在kube-system名称空间下会出现kube-dns的service服务与coredns的pod

$ kubectl get svc -n kube-system        
NAME       TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)                  AGE
kube-dns   ClusterIP   241.254.0.10   <none>        53/UDP,53/TCP,9153/TCP   25d

$ kubectl get pod  -n kube-system -o wide| grep coredns 
coredns-7844787459-f7sb4         1/1     Running   0          25d   241.255.0.123     master   <none>           <none>
coredns-7844787459-rg9rz         1/1     Running   0          25d   241.255.0.124     master   <none>           <none>

CoreDNS是一个DNS解析的组件,作为集群内的DNS服务器,为集群内部提供域名解析服务。比如当集群内Pod要通过service名称访问后端pod时,就会用到coredns的解析服务。CoreDNS服务监视Kubernetes API,当使用kubectl命令向apiServer提交service请求时,CoreDNS会为每一个Service创建DNS记录用于域名解析。

DNS解析原理

当客户端Pod想要通过service name访问后端服务时,会先通过客户端pod自身的dns文件(/etc/resolv.conf)指向dns服务器,
DNS服务器将service name转换成对应ip,客户端Pod在通过ip请求后端服务

DNS配置策略

每个Pod所使用的DNS策略,是通过pod.spec.dnsPolicy字段设置的,共有4种DNS策略:

  • ClusterFirst:默认策略,表示使用集群内部的CoreDNS来做域名解析,Pod内/etc/resolv.conf文件中配置的nameserver是集群的DNS服务器,即kube-dns的地址。
  • Default:Pod直接继承集群node节点的域名解析配置,也就是,Pod会直接使用宿主机上的/etc/resolv.conf文件内容。
  • None:忽略k8s集群环境中的DNS设置,Pod会使用其dnsConfig字段所提供的DNS配置,dnsConfig字段的内容要在创建Pod时手动设置好。
  • ClusterFirstWithHostNet:宿主机与 Kubernetes 共存,这种情况下的POD,既能用宿主机的DNS服务,又能使用kube-dns的Dns服务,需要将hostNetwork打开。

ClusterFirst

apiVersion: v1
kind: Pod
metadata:
   name: mypod
   labels:
      app: mypod
spec:
   containers:
     - name: mynginx
       image: mynginx:v1
   dnsPolicy: ClusterFirst  #字段设置为ClusterFirst(该值为默认值,不设置也是该值)
#namserver指向kube-dns service地址
$ kubectl exec mypod -- cat /etc/resolv.conf 
nameserver 241.254.0.10
search default.svc.cluster.local svc.cluster.local cluster.local localdomain
options ndots:5

Default

apiVersion: v1
kind: Pod
metadata:
   name: mypod
   labels:
      app: mypod
spec:
   containers:
     - name: mynginx
       image: mynginx:v1
   dnsPolicy: Default
#pod内的resolv.conf与宿主机的resolv.conf一致
$ kubectl exec mypod -- cat /etc/resolv.conf 
nameserver 192.168.234.2
search localdomain

$ cat /etc/resolv.conf 
search localdomain
nameserver 192.168.234.2

None

apiVersion: v1
kind: Pod
metadata:
   name: mypod
   labels:
      app: mypod
spec:
   containers:
     - name: mynginx
       image: mynginx:v1
   dnsPolicy: None
   dnsConfig:
     nameservers: ["192.168.234.1","192.168.234.2"]  #最多可指定3个IP,当Pod的dnsPolicy设置为None时,列表必须至少包含一个IP地址
     searches:                                       #Pod中主机名查找的DNS搜索域列表
       - default.svc.cluster.local
       - svc.cluster.local
       - cluster.local
     options:
       - name: ndots
         value: "5"
#pod内resolv.conf文件为dnsConfig配置
$ kubectl exec mypod -- cat /etc/resolv.conf 
nameserver 192.168.234.1
nameserver 192.168.234.2
search default.svc.cluster.local svc.cluster.local cluster.local
options ndots:5

ClusterFirstWithHostNet

apiVersion: v1
kind: Pod
metadata:
   name: mypod
   labels:
      app: mypod
spec:
   containers:
     - name: mynginx
       image: mynginx:v1
   hostNetwork: true   #hostNetwork为true时,表示与宿主机共享网络空间
   dnsPolicy: ClusterFirst  #即使dnsPolicy设置为集群优先,由于hostNetwork: true也会强制将dnsPolicy设置为Default

#所以Pod内resolv.conf与宿主机相同
$ kubectl exec mypod -- cat /etc/resolv.conf 
nameserver 192.168.234.2
search localdomain
apiVersion: v1
kind: Pod
metadata:
   name: mypod
   labels:
      app: mypod
spec:
   containers:
     - name: mynginx
       image: mynginx:v1
   hostNetwork: true
   dnsPolicy: ClusterFirstWithHostNet
#只有dnsPolicy: ClusterFirstWithHostNet,此时pod既可以使用宿主机网络也可以使用kube-dns网络
$ kubectl exec -it mypod -- cat /etc/resolv.conf 
nameserver 241.254.0.10
search default.svc.cluster.local svc.cluster.local cluster.local localdomain
options ndots:5

DNS解析5s超时
参考链接:https://monkeywie.cn/2019/12/10/k8s-dns-lookup-timeout/
主要通过配置resolv.conf的single-request-reopen和single-request选项来避免

  • single-request-reopen (glibc>=2.9) 发送 A 类型请求和 AAAA 类型请求使用不同的源端口。这样两个请求在 conntrack 表中不占用同一个表项,从而避免冲突。
  • single-request (glibc>=2.10) 避免并发,改为串行发送 A 类型和AAAA类型请求,没有了并发,从而也避免了冲突。

修改/etc/resolv.conf文件,在最后加入一行文本:

options single-request-reopen
或者
options single-request

k8s部署pod可通过修改 pod 的 postStart hook 来设置

containers:
  - lifecycle:
      postStart:
        exec:
          command:
            - /bin/sh
            - -c
            - "/bin/echo 'options single-request-reopen' >> /etc/resolv.conf"

或者通过修改 pod 的 template.spec.dnsConfig 来设置

template:
  spec:
    dnsConfig:
      options:
        - name: single-request-reopen

注: 需要 k8s 版本>=1.9并且不支持alpine基础镜像的容器,因为apline底层使用的musl libc库并不支持这些 resolv.conf 选项,所以如果使用alpine基础镜像构建的应用,还是无法规避超时的问题