公众号「架构成长指南」,专注于生产实践、云原生、分布式系统、大数据技术分享。

对k8s有点了解技术人员,应该都只知道k8s是有服务注册发现的,今天就分析下这个原理,看看怎么实现的。

什么是服务注册与发现

服务注册与发现是一种机制,用于在集群中动态地发现和连接不同的服务,比如我们在开发微服务时,经常使用的EurekaNacos


Service B 把自己注册到 Service Registry 叫做 服务注册

Service A 从 Service Registry 发现 Service B 的节点信息叫做 服务发现

K8s 中为什么需要服务发现动态性

在K8s集群中,Pod和服务的数量和位置都是动态变化的,Pod有可能伸缩、重新部署或迁移,在这样的环境下,如果硬编码的服务地址是不可行的,所以服务注册与发现使得我们的系统能够自动感知到这种变化。

透明性

服务注册与发现使得我们的系统可以使用服务名称来访问其他服务,而不需要关心具体的IP地址和端口号。

负载均衡

通过服务注册与发现可以实现负载均衡,将请求均匀地分发到多个后端服务实例。

容错性

当服务实例发生故障或不可用时,服务注册与发现可以自动检测并从服务发现机制中移除不可用的实例。这样,请求将被自动路由到可用的实例上,提高应用程序的容错性和可用性。

k8s 服务注册发现原理

基于上面的介绍,我们了解到K8s中的Pod的生命周期是短暂的,他们的IP地址会不断变化,如果让服务消费方去管理这些Pod IP在做负载均衡调用Pod,那么会很复杂,为了对外提供统一的入口来提供服务,所以k8s创建了Service,不管是内部还是外部统一调用 Service,然后再由 Service 转发到后端Pod

Endpoints

Pod 的地址管理则由Endpoints管理,根据Service名称可以查询Endpoints信息,当通过API创建/修改service对象时,endpoints控制器的监听到Service对象,然后根据Service的配置的选择器创建一个endpoints对象,此对象将pod的IP、容器端口信息存储到etcd中。

他们之间关系如下:

同时Endpoints控制器会监听与Pod相关的事件,包括上下线事件,一旦Endpoints控制器接收到这些事件,它会相应地更新Endpoints资源,将不可用的Pod从Endpoints列表中移除。

域名解析

由于Service的 IP有可能会变,如果在代码里面写死Service IP后期维护起来也是比较麻烦的事情,所以通过在创建一个Service时,CoreDNS会为该Service添加一个域名解析记录,将Service的名称解析为相应的Cluster IP地址。这样其他Pod或服务可以通过使用Service名称来访问该Service。

kube-proxy

kube-proxy 是集群中每个节点上运行的网络代理,它负责将集群内部的Service暴露给其他Pod或外部网络。它通过在Node节点上设置网络规则和转发规则,将Service的请求转发到正确的目标Pod

同时kube-proxy实现负载均衡算法,将进入Service的请求均匀地分发到后端的Pod实例。这确保了在多个副本的情况下,Service能够平衡地处理请求,提高可用性和性能。

kube-proxy 通过监听知道了Service、endpoints对象的创建,然后把ServiceCLUSTER-IP 和端口信息拿出来,创建iptables NAT规则做转发或通过ipvs模块创建VS服务器,这样经过CLUSTER-IP的流量都被转发到后端pod。


当Service的目标Pod位于同一节点上时,kube-proxy会将请求直接转发到该节点上的Pod,而不会跨节点转发。这种情况下,请求不会被发送到其他节点上。

然而,如果Service的目标Pod分布在多个节点上,kube-proxy可以通过负载均衡算法将请求转发到其他节点上的Pod。

示例演示

下面我们基于两个配置文件,验证下上面的结论

nginx-deployment.yaml

apiVersion: apps/v1kind: Deploymentmetadata:  name: nginx-deployment  labels:    app: nginxspec:  replicas: 4  selector:    matchLabels:      app: nginx  template:    metadata:      labels:        app: nginx    spec:      containers:      - name: nginx        image: mirrorgooglecontainers/serve_hostname        ports:        - containerPort: 80

serve_hostname是k8s官方提供的debug镜像,返回hostname的web server,访问pod时会返回hostname。

nginx-service.yaml

apiVersion: v1kind: Servicemetadata:  name: nginx-servicespec:  ports:    - name: service-port      port: 80      protocol: TCP      targetPort: 9376  selector:    app: nginx  type: ClusterIP

可以看到service 的selector属性指定了app: nginx,这样就能匹配 deplyment 中定义的 nginx pod

我们依次执行以上两个文件,最后获取到信息如下

Service地址查看

kubectl get svc nginx-service

Pod信息查看

 kubectl get pods -l app=nginx -o wide

Endpoints信息查看

根据service名称查询

kubectl get ep nginx-service

CoreDNS信息验证

登录任意Pod,执行ping命令,可以看到根据Service 名称解析到了Service cluster ip

负载均衡验证

登录任意 pod,执行curl nginx-service,请求 service的 80 端口,会返回目标 pod名称

以上我们讲了什么是服务发现,以及 k8s 的服务发现是怎么实现的,希望对你有所帮助。