kubernetes网络之DNS

默认DNS策略

Pod默认的dns策略ClusterFirst,意思是先通过kubernetes的权威DNS服务器(如CoreDNS)直接解析出A记录或CNAME记录;如果解析失败,再根据配置,将其转发给上游DNS服务器。以CoreDNS为例,它的配置文件Corefile如下所示:

 1➜  ~ kubectl get cm -n kube-system coredns -o yaml
 2apiVersion: v1
 3data:
 4  Corefile: |
 5    .:53 {
 6        errors
 7        health {
 8           lameduck 5s
 9        }
10        ready
11        kubernetes cluster.local in-addr.arpa ip6.arpa {
12           pods insecure
13           fallthrough in-addr.arpa ip6.arpa
14           ttl 30
15        }
16        prometheus :9153
17        forward . /etc/resolv.conf
18        cache 30
19        loop
20        reload
21        loadbalance
22    }
23kind: ConfigMap
24...

第17行使用forward插件配置了上游域名服务器为主机的/etc/resolv.conf中指定的nameserver

Service和DNS

尽管kubelet在启动容器时,会将同namespace下的Service信息注入到容器的环境变量中:

 1➜  ~ kubectl get svc | grep kubernetes
 2kubernetes                      ClusterIP   192.168.0.1       <none>        443/TCP                                             347d
 3
 4➜  ~ kubectl exec -it debug-pod -n default -- env | grep KUBERNETES
 5KUBERNETES_SERVICE_PORT=443
 6KUBERNETES_PORT=tcp://192.168.0.1:443
 7KUBERNETES_PORT_443_TCP_ADDR=192.168.0.1
 8KUBERNETES_PORT_443_TCP_PORT=443
 9KUBERNETES_PORT_443_TCP_PROTO=tcp
10KUBERNETES_PORT_443_TCP=tcp://192.168.0.1:443
11KUBERNETES_SERVICE_PORT_HTTPS=443
12KUBERNETES_SERVICE_HOST=192.168.0.1

但是通常情况下我们使用DNS域名解析的方式进行服务注册和发现。

Kubernetes中的DNS应用部署好以后,会对外暴露一个服务,集群内的容器可以通过访问该服务的Cluster IP进行域名解析。DNS服务的Cluster IP由Kubelet的cluster-dns参数指定。并且在创建Pod时,由Kubelet将DNS Server的信息写入容器的/etc/resolv.conf文件中。

查看resolv.conf文件的配置:

1➜  ~ k exec -it debug-pod -n default -- cat /etc/resolv.conf
2nameserver 192.168.0.2
3search default.svc.cluster.local svc.cluster.local cluster.local
4options ndots:5
  • nameserver 192.168.0.2这一行即表示DNS服务的地址(Cluster IP)为192.168.0.2

  • search这一行表示,如果无法直接解析域名,则会尝试加上default.svc.cluster.local, svc.cluster.local, cluster.local后缀进行域名解析。

    其中default是namespace,cluster.local是默认的集群域名后缀,kubelet也可以通过--cluster-domain参数进行配置。

也就是说:

  • 同namespace下,可以通过nslookup + kubernetes解析域名
  • 不同namespace下,可以通过nslookup + kubernetes.defaultkubernetes.default.svckubernetes.default.svc.cluster.local解析域名

因为dns服务器会帮你补齐全域名:kubernetes.default.svc.cluster.local

{svc name}.{svc namespace}.svc.{cluster domain}就是kubernetes的FQDN格式。

Headless Service的域名解析

无论是kube-dns还是CoreDNS,基本原理都是通过watch Service和Pod,生成DNS记录。常规的ClusterIP类型的Service的域名解析如上所述,DNS服务会返回一个A记录,即域名和ClusterIP的对应关系:

1➜  ~ k exec -it debug-pod -n default -- nslookup kubernetes.default
2Server:		192.168.0.2
3Address:	192.168.0.2#53
4
5Name:	kubernetes.default.svc.cluster.local
6Address: 192.168.0.1

Headless Service的域名解析稍微复杂一点。

ClusterIP可以看作是Service的头,而Headless Service,顾名思义也就是指定他的ClusterIP为None的Service。

直接解析

当你直接解析它的域名时,返回的是EndPoints中的Pod IP列表:

这个EndPoints后端的Pod,不仅可以通过在service中指定selector来选择,也可以自己定义,只要名字和service同名即可。

 1➜  ~ k exec -it debug-pod -n default -- nslookup headless
 2Defaulting container name to debug.
 3Use 'kubectl describe pod/debug-pod -n default' to see all of the containers in this pod.
 4Server:		192.168.0.2
 5Address:	192.168.0.2#53
 6
 7Name:	headless.default.svc.cluster.local
 8Address: 1.1.1.1
 9Name:	headless.default.svc.cluster.local
10Address: 2.2.2.2
11Name:	headless.default.svc.cluster.local
12Address: 3.3.3.3

给Pod生成A记录

如果Pod.spec中指定了hostnamesubdomain,并且subdomain和headleass service的名字相同,那么kubernetes DNS会额外给这个Pod的FQDN生成A记录:

1➜  ~ k exec -it debug-pod -n default -- nslookup mywebsite.headless.default.svc.cluster.local
2Server:		192.168.0.2
3Address:	192.168.0.2#53
4
5Name:	mywebsite.headless.default.svc.cluster.local
6Address: 10.189.97.217

Pod的FQDN是:{hostname}.{subdomain}.{pod namespace}.svc.{cluster domain}

ExternalName Service

ExternalName 类型的Service,kubernetes DNS会根据ExternalName字段,为其生成CNAME记录,在DNS层进行重定向。

1apiVersion: v1
2kind: Service
3metadata:
4  name: external
5  namespace: default
6spec:
7  type: ExternalName
8  externalName: my.example.domain.com
1➜  ~ k exec -it debug-pod -n default -- nslookup external
2Server:		192.168.0.2
3Address:	192.168.0.2#53
4
5external.default.svc.cluster.local	canonical name = my.example.domain.com.
6Name:	my.example.domain.com
7Address: 66.96.162.92