CoreDNS 介绍

服务发现是 K8s 的一项很重要的功能。K8s 的服务发现有两种方式,一种是将 svc 的 ClusterIP 以环境变量的方式注入到 pod 中,一种就是 DNS,从 1.13 版本开始,coreDNS 就取代了 kube dns 成为了内置的 DNS 服务器

  • 环境变量
    Pod创建的时候,服务的IP和Port会以环境变量的形式注入到pod里
  • DNS
    K8s集群部署一个Dns服务器,Service创建成功后,会在Dns服务器里写入一些记录,想要访问某个服务,通过Dns服务器解析出对应的Ip和Port,从而实现k8s集群内部的服务访问

CoreDns可以解决Service的发现问题,k8s将Service的名称当做域名注册到CoreDns中,通过Service的名称就可以访问其提供的服务

K8s DNS策略

Kubernetes中Pod的DNS 策略有四种类型

  • Default:Pod 继承所在主机上的 DNS 配置
  • ClusterFirst:K8s 的默认设置;先在 K8s 集群配置的 coreDNS 中查询,查不到的再去继承自主机的上游 nameserver 中查询
  • ClusterFirstWithHostNet:对于网络配置为 hostNetwork 的 Pod 而言,其 DNS 配置规则与 ClusterFirst 一致
  • None:忽略 K8s 环境的 DNS 配置,只认Pod的dnsConfig设置

resolv.conf 文件分析

部署 pod 的时候,如果用的是 K8s 集群的 DNS,那么 kubelet 在起 pause 容器的时候,会将其 DNS 解析配置初始化成集群内的配置
创建一个nginx-web的deployment,其pod的resolv.conf文件如下:

[root@k8s-master ~]# kubectl exec -it nginx-web-579fdb4c89-7w6x6 cat /etc/resolv.conf.
nameserver 10.96.0.2
search default.svc.cluster.local svc.cluster.local cluster.local
options ndots:5

在集群中 pod 之间互相用 svc name 访问的时候,会根据 resolv.conf 文件的 DNS 配置来解析域名,下面来分析具体的过程

域名解析的过程

pod的resolv.conf 文件主要有三个部分,分别为 nameserver、search 和 option,而这三个部分可以由 K8s 指定,也可以通过 pod.spec.dnsConfig字段自定义

nameserver

resolv.conf 文件的第一行 nameserver 指定的是 DNS 服务的 IP,这里就是 coreDNS 的 clusterIP

[root@k8s-master ~]# kubectl get -n kube-system svc|grep dns
kube-dns        ClusterIP   10.96.0.2     <none>        53/UDP,53/TCP,9153/TCP    20d

也就是说所有域名的解析,都要经过 coreDNS 的虚拟 IP 10.96.0.10 进行解析,不论是 Kubernetes 内部域名还是外部的域名

search 域

resolv.conf 文件的第二行指定的是 DNS search 域,解析域名的时候将要访问的域名依次带入search域,进行 DNS 查询
比如在pod中访问一个域名为nginx-web的服务,其进行的 DNS 域名查询的顺序是

nginx-web.default.svc.cluster.local. -> nginx-web.svc.cluster.local. -> nginx-web.cluster.local.

直到查到为止

options

resolv.conf 文件的第三行指定的是其他项,最常见的是 dnots,dnots 指的是如果查询的域名包含的点 “.” 小于 5,则先走 search 域,再用绝对域名;如果查询的域名包含点数大于或等于 5,则先用绝对域名,再走 search 域,K8s 中默认的配置是 5

也就是说如果访问的是 a.b.c.e.f.g ,那么域名查找的顺序如下

a.b.c.e.f.g. -> a.b.c.e.f.g.default.svc.cluster.local. -> a.b.c.e.f.g.svc.cluster.local. -> a.b.c.e.f.g.cluster.local.

如果访问的是 a.b.c.e,那么域名查找的顺序如下

a.b.c.e.default.svc.cluster.local. -> a.b.c.e.svc.cluster.local. -> a.b.c.e.cluster.local. -> a.b.c.e.

pod之间的通信

通过svc访问

K8s中Pod之间通过svc访问的时候,会经过 DNS 域名解析,再拿到 ip 通信,而 K8s 的域名全称为 "..svc.cluster.local",而我们通常只需将 svc name 当成域名就能访问到 pod,这一点通过上面的域名解析过程并不难理解

有两个 deployment,一个叫 busybox,在 default 这个namespace 下;一个叫nginx-test,在test-env这个namespace下,svc 同名

[root@master ~]# kubectl get po
NAME                           READY   STATUS    RESTARTS   AGE
busybox-5bbb5d7ff7-dh68j       1/1     Running   0          8m35s

[root@master ~]# kubectl exec -it busybox-5bbb5d7ff7-dh68j sh
/ # wget nginx-web
wget: bad address 'nginx-web'

/ # wget nginx-web.test-env
Connecting to nginx-web.test-env (10.96.0.69:80)
saving to 'index.html'
index.html           100% |*****************************************************|   612  0:00:00 ETA
'index.html' saved

[root@master ~]# kubectl -n test-env get svc
NAME         TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE
nginx-web   ClusterIP   10.96.0.69   <none>        80/TCP    14m

可以看到当直接用 nginx-web 去访问的时候,提示 bad address,说明域名错了,因为在不同的 namespace 下,所有的 search 域都找过了还是找不到,当用 nginx-web.test-env 去访问的时候,会解析到 10.96.0.69 这个 IP,而这个 IP 正是nginx-web 的 ClusterIP,所以在不同的 namespace 下的 pod 通过 svc 访问的时候,需要在 svc name 后面加上 .

pod的hostname与subdomain

K8s 中如果不指定 pod 的 hostname,其默认为 pod.metadata.name,通过 spec.hostname 字段可以自定义,另外还可以给 pod 设置 subdomain,通过 spec.subdomain 字段

[root@k8s-master ~]# cat nginx.yaml
apiVersion: v1
kind: Pod
metadata:
  name: nginx
  labels:
    name: nginx
spec:
  hostname: domain-test
  subdomain: subdomain-test
  containers:
  - image: nginx
    name: nginx
---
apiVersion: v1
kind: Service
metadata:
  name: subdomain-test
spec:
  selector:
    name: nginx
  ports:
  - port: 80
    targetPort: 80
    protocol: TCP

查看这个 pod 的 hostname和hosts文件

[root@k8s-master ~]# kubectl get po -owide | grep nginx
nginx                              1/1     Running   0          100s   10.244.2.54   k8s-node2    <none>           <none>
[root@k8s-master ~]# kubectl exec -it nginx bash
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
root@domain-test:/# cat /etc/hosts 
# Kubernetes-managed hosts file.
127.0.0.1       localhost
::1     localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
fe00::0 ip6-mcastprefix
fe00::1 ip6-allnodes
fe00::2 ip6-allrouters
10.244.2.54     domain-test.subdomain-test.default.svc.cluster.local    domain-tes

在 busybox 容器中访问这个 pod

[root@localhost ~]# kubectl exec -it busybox-5bbb5d7ff7-dh68j sh
/ # wget domain-test.subdomain-test
Connecting to domain-test.subdomain-test (10.244.2.54:80)
saving to 'index.html'
index.html           100% |*****************************************************|   612  0:00:00 ETA
'index.html' saved

/ # wget subdomain-test
Connecting to subdomain-test (10.96.0.71)
wget: can't open 'index.html': File exists

可以看到,当访问 domain-test.subdomain-test 解析出来的是 10.244.2.54,这个是 nginx 的 pod ip,而不是 clusterIP;而访问 subdomain-test 时,解析出来的是 10.96.0.71,这是 clusterIP,属于正常的 svc name 途径

coreDNS Corefile文件

CoreDNS 实现了应用的插件化,用户可以选择所需的插件编译到可执行文件中;CoreDNS 的配置文件是 Corefile 形式的

[root@k8s-master ~]# kubectl get -n kube-system cm coredns -oyaml
apiVersion: v1
data:
  Corefile: |
    .:53 {
        log
        errors
        health {
            lameduck 5s
        }
        ready
        kubernetes cluster.local in-addr.arpa ip6.arpa {
            pods insecure
            fallthrough in-addr.arpa ip6.arpa
            ttl 30
        }
        prometheus :9153
        forward . /etc/resolv.conf
        cache 30
        loop
        reload
        loadbalance
    }
...

Corefile 文件分析

第一部分

kubernetes cluster.local in-addr.arpa ip6.arpa {
   pods insecure
   upstream
   fallthrough in-addr.arpa ip6.arpa
}

指明 cluster.local 后缀的域名,都是 kubernetes 内部域名,coredns 会监听 service 的变化来维护域名关系,所以cluster.local 相关域名都在这里解析

第二部分

proxy . /etc/resolv.conf

proxy 指 coredns 中没有找到记录,则去 /etc/resolv.conf 中的 nameserver 请求解析,而 coredns 容器中的 /etc/resolv.conf 是继承自宿主机的,实际效果是如果不是 k8s 内部域名,就会去默认的 dns 服务器请求解析,并返回给 coredns 的请求者

第三部分
prometheus:CoreDNS 的监控地址为: http://localhost:9153/metrics ,满足 Prometheus 的格式
cache:允许缓存
loop:如果找到循环,则检测简单的转发循环并停止 CoreDNS 进程
reload:允许 Corefile 的配置自动更新。在更改 ConfigMap 后两分钟,修改生效
loadbalance:这是一个循环 DNS 负载均衡器,可以在答案中随机化 A,AAAA 和 MX 记录的顺序

指定 hosts

有时候某个域名的服务在集群外,希望在集群内访问到,我们可以在 corefile 中指定 hosts 的方法实现,具体方式是将域名及对应的 ip 以 hosts 插件的方式加入到 corefile 中

hosts {
    10.244.2.54 a.com
    fallthrough
}

其中,10.244.2.54 是 nginx-web 的 pod ip,然后再在上面的 busybox pod 中访问a.com 这个服务

[root@master ~]# kubectl exec -it busybox-5bbb5d7ff7-dh68j sh
/ # wget a.com
Connecting to a.com (10.244.2.54:80)
saving to 'index.html'
index.html           100% |*****************************************************|   612  0:00:00 ETA
'index.html' saved
文章作者: 鲜花的主人
版权声明: 本站所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 爱吃可爱多
Kubernetes Kubernetes
喜欢就支持一下吧
打赏
微信 微信
支付宝 支付宝