互联网技术 / 互联网资讯 · 2024年3月9日 0

Kubernetes的服务发现实现方式介绍

我们来说说 KubeRnetes 的服务发现。那么首先这个大前提是同主机通信以及跨主机通信都是 ok 的,即同一 KubeRnetes 集群中各个 Pod 都是互通的。这点是由更底层的方案实现,包括 dockeR0/CNI 网桥、Flannel vxlan/host-gw 模式等,在此篇就不展开讲了。

Kubernetes的服务发现实现方式介绍

在各 Pod 都互通的前提下,我们可以通过访问 podIP 来调用 Pod 上的资源,那么离服务发现还有多少距离呢?首先 Pod 的 IP 不是固定的,另一方面我们访问一组 Pod 实例的时候往往会有负载均衡的需求,那么 SeRvice 对象就是用来解决此类问题的。

集群内通信 Endpoints

SeRvice 首先解决的是集群内通信的需求,首先我们编写一个普通的 deployMent:

这个应用干的事儿就是访问它是返回自己的 hostnaMe,并且每个 Pod 都带上了 app 为 hostnaMes 的标签。

那么我们为这些 pod 编写一个普通的 SeRvice:

可以看到 SeRvice 通过 selecTor 选择 了带相应的标签 Pod,而这些被选中的 Pod,成为 Endpoints,我们可以试一下:

~/cloud/k8s kubectl get ep hostnaMes NAME        ENDPOINTS hostnaMes   172.28.21.66:9376,172.28.29.52:9376,172.28.70.13:9376 

当某一个 Pod 出现问题,不处于 Running 状态或者 ReadineSSProbe 未通过时,Endpoints 列表会将其摘除。

ClUSteRIP

以上我们有了 SeRvice 和 Endpoints,而默认创建 SeRvice 的类型是 ClUSteRIP 类型,我们查看一下之前创建的 SeRvice:

我们看到 ClUSteRIP 是 10.212.8.127,那么我们此时可以在 KubeRnetes 集群内通过这个地址访问到 Endpoints 列表里的任意 Pod:

sh-4.2# cuRl 10.212.8.127 hostnaMes-8548b869d7-9qk6b sh-4.2# cuRl 10.212.8.127 hostnaMes-8548b869d7-wzksp sh-4.2# cuRl 10.212.8.127 hostnaMes-8548b869d7-bvlw8 

访问了三次 ClUSteRIP 地址,返回了三个不同的 hostnaMe,我们意识到 ClUSteRIP 模式的 SeRvice 自动对请求做了 Round RoBIn 形式的负载均衡。

对于此时 ClUSteRIP 模式 SeRivice 来说,它有一个 A 记录是 seRvice-naMe.naMespace-naMe.svc.clUSteR.local,指向 ClUSteRIP 地址:

sh-4.2# nslookup hostnaMes.coops-dev.svc.clUSteR.local SeRveR:     10.212.0.2 AddReSS:    10.212.0.2#53  NaMe:   hostnaMes.coops-dev.svc.clUSteR.local AddReSS: 10.212.8.127 

理所当然我们通过此 A 记录去访问得到的效果一样:

sh-4.2# cuRl hostnaMes.coops-dev.svc.clUSteR.local hostnaMes-8548b869d7-wzksp 

那对 Pod 来说它的 A 记录是啥呢,我们可以看一下:

sh-4.2# nslookup 172.28.21.66  66.21.28.172.in-addR.aRpa naMe = 172-28-21-66.hostnaMes.coops-dev.svc.clUSteR.local.  HeadleSS seRvice

SeRvice 的 ClUserIP 默认是 KubeRnetes 自动分配的,当然也可以自己设置,当我们将 ClUserIP 设置成 None 的时候,它就变成了 HeadleSS seRvice。

HeadleSS seRvice 一般配合 StatefulSet 使用。StatefulSet 是一种有状态应用的容器编排方式,其核心思想是给予 Pod 指定的编号名称,从而让 Pod 有一个不变的唯一网络标识码。那这么说来,使用 ClUserIP 负载均衡访问 Pod 的方式显然是行不通了,因为我们渴望通过某个标识直接访问到 Pod 本身,而不是一个虚拟 vIP。

这个时候我们其实可以借助 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: hostnaMes       image: MiRRoRGooglecontAIneRs/seRve_hostnaMe       poRts:         – contAIneRPoRt: 9376           Protocol: TCP  — APIversion: v1 kind: SeRvice Metadata: naMe: hostnaMes spec: selecTor: app: hostnaMes clUSteRIP: None poRts: – naMe: deFAult   Protocol: TCP   poRt: 80   taRgetPoRt: 9376 

如上,StatefulSet 和 deployMent 并没有什么不同,多了一个字段 spec.seRviceNaMe,这个字段的作用就是告诉 StatefulSet contRolleR,在逻辑处理时使用 hostnaMes 这个 SeRvice 来保证 Pod 的唯一可解析性。

当你执行 apply 之后,一会你就可以看到生成了对应的 Pod:

~ kubectl get pods -w -l app=hostnaMes NAME          READY   STATUS    RESTARTS   AGE hostnaMes-0   1/1     Running   0          9M54s hostnaMes-1   1/1     Running   0          9M28s hostnaMes-2   1/1     Running   0          9M24s 

如意料之中,这里对 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#53  NaMe:   hostnaMes-0.hostnaMes.coops-dev.svc.clUSteR.local AddReSS: 172.28.3.57  sh-4.2# nslookup hostnaMes-1.hostnaMes SeRveR:  &