Service、Pod、Label、Endpoint、Kube-proxy、Cluster IP、LoadBalance、NodePort、环境变量、DNS、Expose
Service
微服务架构的应用需要有好的服务编排支持。k8s中的Service提供了一套简化的服务代理和发现机制,天然适应微服务架构。
Service定义了服务的入口地址。前端应用(pod)通过入口地址访问其背后由pod副本组成的集群实例。service与其后端pod副本集群间通过label selector实现“无缝对接”。
负载均衡器,为这组pod开启对外服务端口,并且把这些pod的endpoint列表加入端口的转发列表中,客户端就可以通过负载均衡器的对外IP地址+服务端口来访问此服务,而客户端的请求由负载均衡器的算法来决定被转发到哪个pod。
kube-proxy是软件负载均衡器,负责把对service的请求转发到后端的pod实例,并在内部实现服务的负载均衡与会话保持机制。
每个service有全局唯一的虚拟IP地址,即cluster ip。这样,每个服务就变成了具备唯一IP地址的“通信节点”,服务调用就变成了TCP网络通信问题。
pod的endpoint地址会随着pod的销毁和重新创建而发生改变,因为pod的ip会变化。
pod的ip是在容器中配置。service的cluster ip是在iptables中配置,并映射到pod ip。iptables将访问service的流量使用类似轮询的负载均衡策略转发到后端pod。
除了通过cluster ip访问service,还可以通过DNS。
原理
在k8s中,受到RC调控的时候,Pod副本是变化的,对应的虚拟IP也是变化的,比如发生迁移、伸缩或者更新的时候。k8s的Service是抽象概念,它定义了一组Pod逻辑集合以及访问它们的策略。Service的目标是提供一种桥梁,为访问者提供一个固定访问地址,用于在访问时重定向到相应的后端,这使得非k8s原生应用程序,在无须为Kubemces编写特定代码的前提下,轻松访问后端。
需要注意的是,k8s分配给Service的固定IP是虚拟IP,并不是真实的IP,在外部是无法寻址。真实的系统实现上,k8s是通过kube-proxy组件来实现虚拟IP路由及转发。ku-proxy组件是基于iptables实现路由转发。
通过分析、识别并建模系统中的所有服务为微服务-----Kubernetes Service,最终系统由多个提供不同业务能力而又彼此独立的微服务单元所组成,服务之间通过TCP/IP进行通信。
service的访问ip和endpoint/pod ip都会在k8s的dns服务器里存储域名和ip的映射关系。
Service代理外部服务
Service不仅可以代理Pod,还可以代理任意其他后端,比如运行在k8s外部的Mysql、Oracle等。
无头(headless)service没有selector,这样就不会创建相关的endpoints对象。再手动将service映射到指定的endpoints,即通过定义两个同名的service和endPoints来实现的。service抽象类访问pod,也能抽象其他类型的backend。
Service内部负载均衡
当Service的Endpoints包含多个IP的时候,即服务代理存在多个后端,将进行请求的负载均衡。默认的负载均衡策略是轮询或者随机(由kube-proxy的模式决定)。同时,Service上通过设置Service-->spec-->sessionAffinity=ClientIP,来实现基于源IP地址的会话保持。
发布Service(service的种类)
Service的虚拟IP是由k8s虚拟出来的内部网络,外部是无法寻址到的。但有些服务需要被外部访问到。这时就需要增加一层网络转发,即外网到内网的转发。k8s提供了NodePort、LoadBalancer、Ingress三种方式。
- clusterIP(cluster内部访问service)
Cluster中的pod可以通过serviceName.namespace_name访问service。在同一个namespace的可以省略namespace_name。 - nodePort(cluster外部访问service)
service通过cluster节点的静态端口对外提供服务。cluster外部通过任意nodeIP+nodePort访问service。默认端口范围是30000-32767。 - loadbalancer
公有云平台(例如GCE)的load balance对外提供服务。一些高级的L7转发功能,例如基于HTTP header、cookie、URL的转发就做不了。
service的自发现机制
k8s中有一个很重要的服务自发现特性。一旦service被创建,该service的IP和port等信息都可以被注入到pod中供它们使用。k8s支持两种service发现机制:环境变量和DNS。
- 环境变量方式
k8s创建Pod时会自动添加所有可用的service环境变量到该Pod中,如有需要,这些环境变量就被注入Pod内的容器里。环境变量的注入只发生在Pod创建时,且不会被自动更新。这个特点暗含了service和访问该service的Pod的创建时间的先后顺序。 - DNS方式
k8s集群支持DNS服务。这个DNS服务器使用k8s的watchAPI,不间断的监测新的service的创建并为每个service创建一个DNS记录。如果DNS在整个集群范围内都可用,那么所有的Pod都能够自动解析service的域名。
服务发现用service的name与cluster ip地址做dns域名映射即可解决。
k8s通过add-on增值包的方式引入dns系统,把服务名作为dns域名,程序使用服务名来建立通信连接。
多个service如何避免地址和端口冲突
k8s为service分配唯一的ClusterIP,所以当使用ClusterIP:port的组合访问service的时候,这个组合一定不会发生重复。另一方面,kube-proxy为每个service真正打开的是不会重复的随机端口,用户在service描述文件中指定的访问端口会被映射到这个随机端口上。
service的不足
k8s使用iptables和kube-proxy解析service的人口地址,在中小规模的集群中运行良好,但是当service的数量超过一定规模时,仍然有些问题。首当其冲的便是service环境变量泛滥,以及service与使用service的pod两者创建时间先后的制约关系。目前来看,很多使用者在使用k8s时往往会开发一套自己的Router组件来替代service,以便更好地掌控和定制这部分功能。
如果觉得通过yaml方式创建service比较麻烦,kubectl提供expose子命令直接将deployment暴露为服务。
参考
- 《Kubernetes 权威指南》
- 《每天5分钟玩转kubernetes》
- 《Kubernetes 权威指南-企业级容器云实践》
- https://www.jianshu.com/p/d53b5df16e92