说说 Kubernetes 是怎么实现服务发现的( 二 )


这个时候我们其实可以借助 DNS , 每个 Pod 都会有一条 A 记录 pod-name.service-name.namespace-name.svc.cluster.local 指向 podIP , 我们可以通过这条 A 记录直接访问到 Pod 。
我们编写相应的 StatefulSet 和 Service 来看一下:
--- apiVersion: apps/v1 kind: StatefulSet metadata: name: hostnames spec: serviceName: "hostnames" selector: matchLabels:app: hostnames replicas: 3 template: metadata:labels:app: hostnames spec:containers:- name: hostnamesimage: mirrorgooglecontainers/serve_hostnameports:- containerPort: 9376protocol: TCP--- apiVersion: v1 kind: Service metadata: name: hostnames spec: selector: app: hostnames clusterIP: None ports: - name: defaultprotocol: TCPport: 80targetPort: 9376
如上 , StatefulSet 和 deployment 并没有什么不同 , 多了一个字段 spec.serviceName , 这个字段的作用就是告诉 StatefulSet controller , 在逻辑处理时使用 hostnames 这个 Service 来保证 Pod 的唯一可解析性 。
当你执行 apply 之后 , 一会你就可以看到生成了对应的 Pod:
~ kubectl get pods -w -l app=hostnames NAMEREADYSTATUSRESTARTSAGE hostnames-01/1Running09m54s hostnames-11/1Running09m28s hostnames-21/1Running09m24s
如意料之中 , 这里对 Pod 名称进行了递增编号 , 并不重复 , 同时这些 Pod 的创建过程也是按照编号依次串行进行的 。我们知道 , 使用 deployment 部署的 Pod 名称会加上 replicaSet 名称和随机数 , 重启后是不断变化的 。而这边使用 StatefulSet 部署的 Pod , 虽然 podIP 仍然会变化 , 但名称是一直不会变的 , 基于此我们得以通过固定的 DNS A 记录来访问到每个 Pod 。
那么此时 , 我们来看一下 Pod 的 A 记录:
sh-4.2# nslookup hostnames-0.hostnames Server:10.212.0.2 Address:10.212.0.2#53Name:hostnames-0.hostnames.coops-dev.svc.cluster.local Address: 172.28.3.57sh-4.2# nslookup hostnames-1.hostnames Server:10.212.0.2 Address:10.212.0.2#53Name:hostnames-1.hostnames.coops-dev.svc.cluster.local Address: 172.28.29.31sh-4.2# nslookup hostnames-2.hostnames Server:10.212.0.2 Address:10.212.0.2#53Name:hostnames-2.hostnames.coops-dev.svc.cluster.local Address: 172.28.23.31
和之前的推论一致 , 我们可以通过 pod-name.service-name.namespace-name.svc.cluster.local 这条 A 记录访问到 podIP , 在同一个 namespace 中 , 我们可以简化为 pod-name.service-name 。
而这个时候 , Service 的 A 记录是什么呢:
sh-4.2# nslookup hostnames Server:10.212.0.2 Address:10.212.0.2#53Name:hostnames.coops-dev.svc.cluster.local Address: 172.28.29.31 Name:hostnames.coops-dev.svc.cluster.local Address: 172.28.3.57 Name:hostnames.coops-dev.svc.cluster.local Address: 172.28.23.31
【说说 Kubernetes 是怎么实现服务发现的】原来是 Endpoints 列表里的一组 podIP , 也就是说此时你依然可以通过service-name.namespace-name.svc.cluster.local这条 A 记录来负载均衡地访问到后端 Pod 。
iptables
或多或少我们知道 Kubernetes 里面的 Service 是基于 kube-proxy 和 iptables 工作的 。Service 创建之后可以被 kube-proxy 感知到 , 那么它会为此在宿主机上创建对应的 iptables 规则 。
以 CluserIP 模式的 Service 为例 , 首先它会创建一条 KUBE-SERVICES 规则作为入口:
-A KUBE-SERVICES -d 10.212.8.127/32 -p tcp -m comment --comment "default/hostnames: cluster IP" -m tcp --dport 80 -j KUBE-SVC-NWV5X2332I4OT4T3
这条记录的意思是:所有目的地址是 10.212.8.127 这条 CluserIP 的 , 都将跳转到 KUBE-SVC iptables 链处理 。
那么我们来看 KUBE-SVC 链都是什么:
-A KUBE-SVC-NWV5X2332I4OT4T3 -m comment --comment "default/hostnames:" -m statistic --mode random --probability 0.33332999982 -j KUBE-SEP-WNBA2IHDGP2BOBGZ -A KUBE-SVC-NWV5X2332I4OT4T3 -m comment --comment "default/hostnames:" -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-X3P2623AGDH6CDF3 -A KUBE-SVC-NWV5X2332I4OT4T3 -m comment --comment "default/hostnames:" -j KUBE-SEP-57KPRZ3JQVENLNBR