通过Service访问Pod
众所周知,我们给一些pod创建好Service之后,就可以通过service访问pod了。
从Kubernetes集群内通过service访问pod有两种办法:
- 通过Service的ClusterIP
如上图所示,我们访问Service的ClusterIP时,请求会被路由到后端的Endpoint(Pod)上。
因为种种原因,Service的ClusterIP是有可能变化的,这样一来,对调用方来说,就麻烦了。
所以我们通常不会直接使用ClusterIP来访问一个服务。 - 通过Service的名字
除了IP外,更常见的一种访问方式是通过Service的name,还是上面的例子,假设我们的namespace是default,那么在Kubernetes集群内可以这样访问:my_service.default.svc.cluster.local
这其实是一个DNS域名,访问时被Kubernetes的DNS组件解析成了Service的ClusterIP。
和普通DNS工作原理是一样的。
这样一来,不管service的ClusterIP变不变,都不会影响调用方了。
那么,问题来了:
这个DNS记录是怎么维护的?
肯定得有个DNS server来干这个活儿,在你的k8s上执行以下这个命令:
gaoyang@IT-C02XQ1HFJG5H standalone % kubectl get pod -A | grep dns
kube-system coredns-6b9cfd868c-fjmz5 1/1 Running 0 16d
kube-system coredns-6b9cfd868c-x7wrx 1/1 Running 0 16d
这个位于kube-system namespace内的叫做coredns的pod就是kubernetes内置的DNS server。
所有关于service和dns的事情都是他做的。
CoreDNS的工作原理示意图如下:
- 新建Kubernetes集群时,自动创建CoreDNS的pod(其实是个Deployment)和service
- CoreDNS建立到api server的长连接(websocket),监听集群中service的相关事件
- 用户创建pod时,kubernetes会自动在pod中把name server设置为CoreDNS的地址,例如下面的nameserver地址被设置成了22.10.0.10,
kubectl -n lemming exec -it standalone-0 -- cat /etc/resolv.conf
search lemming.svc.cluster.local svc.cluster.local cluster.local
nameserver 22.10.0.10
options ndots:5
这个22.10.0.10其实就是CoreDNS对应的service的地址
kubectl get svc -A | grep dns
kube-system kube-dns ClusterIP 22.10.0.10
- 用户创建service时
- kubernetes根据service的selector找到其对应的endpoint(pod),然后将这些信息写入到etcd中
- kubernetes将这个创建service的事件通知给CoreDNS
- CoreDNS从ETCD中得到service和endpoint相关的信息,并根据这些信息创建DNS记录
- 从集群内的pod中访问service时,pod会去找coredns把service的域名翻译成IP,然后剩下的就和直接通过ClusterIP访问没什么区别了
StatefulSet的DNS
现在让我们来来考虑一下StetefulSet的问题。
我们都知道,statefulset会为每一个pod创建一个固定的DNS记录(DNS解析后的IP是pod的IP,而不是service的ip)。但是StatefulSet的文档中明确要求我们先得自己创建一个headless类型的service,才能保证每个pod拥有一个dns记录。pod的dns域名长这样:my_pod.my_service.default.svc.cluster.local
这个结果其实是statefulset和headless service共同作用的结果:statefulset指定了pod的名字,headless service(其实真正干活的是coredns)给这个被指定了名字的pod创建了一个DNS记录。
当然,如果只有headless service,没有statefulset的情况下,pod也是会被创建一个DNS记录的,只不过记录的最前面pod名字的部分是一个随机的字符串。
所以关于上面的第6和第7步,其实是:
CoreDNS监听到service创建的事件后,会判断这是不是一个headless service,如果是的话,就从etcd中找到headless service对应的endpoint的ip,给每一个endpoint都创建一条记录。
因为headless service是没有ClusterIP的,所以直接请求headless service时,返回的其实是一个endpoint ip的列表。
更进一步,对于headless pod的DNS记录,CoreDNS并不是一监听到service创建的事件就会给每个endpoint创建一条DNS记录,如果我们的pod还没启动完,此时通过dns就能访问的话,就会出错了。
所以,CoreDNS真正给Endpoint创建DNS记录的时机是CoreDNS监听到Endpoint(pod)的状态变成ready时,才会创建。
而Pod的ready状态又是由Kubernetes的readiness probe 探针决定的。