"Kubernetes进阶实战"
——之SERVICE篇 ——
Kubernetes,简称K8s,8代表“ubernete”8个字符的替代缩写。K8s是一个开源的,用于管理云平台中多个主机上的容器化的应用。Kubernetes的目标是让部署容器化的应用简单且高效, 提供了应用部署、规划、更新、维护的一种机制。
“vRAN”又称Cloud RAN,是指在更灵活的体系结构中实现RAN,在基于通用处理器的软件平台中虚拟化网络功能。vRAN简化了新功能和算法的部署,以简化资源使用。vRAN还将网络功能与底层硬件分离,使灵活、动态的RAN环境成为可能,从而顺利进入5G未来。更详细地说,vRAN 2.0是一个运行在容器中的云化基站,K8s作为一个开源的container管理工具,自然的被选择用来部署vRAN2.0相关的容器应用。接下来的篇幅将重点分享K8s的service部分,service在vRAN2.0 中为cluster之内的组件通信起到了很重要的作用。
#Service定义和特点#
在对service的定义和特点进行介绍之前,先让我们一起来了解为什么需要service。在同一个cluster内部,每个pod都会被分配一个IP, 每个pod的container,都可以使用自己的pod IP和其他的pod 通信,访问其他pod提供的服务,他们之间可以认为是一个抽象的局域网,而且同一个pod内IP是共享的。由于Pod受控于控制器资源对象,存在生命周期,在自愿(维护升级)和非自愿(node down, 资源不足)中断后只能被重建的新pod对象取代;再者同一类型的pod的scale-in/out, 随之变化的还有pod IP,IP的变动或应用规模的伸缩会导致客户端通过pod IP访问错误或者无法有效地使用新增的pod对象。
Service资源用于为此类pod对象提供一个固定统一的访问接口及负载均衡的能力,并借助DNS系统的服务发现功能,解决客户端发现并访问容器化应用的难题。
Service是K8s核心资源类型之一,它是一种抽象,通过规则定义出由一个或者多个pod对象组合而成的逻辑集合,以及访问这组pod的策略。
Service关联pod资源的规则要借助于label selector完成,并通过自己的IP地址和端口调度代理请求至组内的pod对象上,service对象的IP称为Cluster IP,它是K8s配置的一种虚拟IP,在service对象创建后即保持不变,并且能被同一个cluster内的pod所访问。Service和pod对象创建没有先后要求,这样做到运维和开发解耦。
Service资源会通过API server持续监视标签选择器匹配到的后端的pod对象的变动,例如IP地址变动,对象增加减少等,service并不直接链接至pod对象,而是通过endpoint资源(由IP和port组成的列表),这些IP/port是service的标签选择器匹配到的pod资源。
一个clusterIP service对象就是K8s node上的一些iptables或ipvs规则,用于将到达service对象的IP地址的流量调度转发到相应的endpoint对象指向的IP和port上。
接下来给大家介绍K8s service在诺基亚vRAN上面的实际应用例子。
#Service在vRAN上的应用#
►
ClusterIP Service
当一个被标签选择器匹配到的pod状态变成ready时,此pod便会被service捕获加到endpoint列表,所以pod内的container需要配置readiness probe来确保container的服务在发布到service前可用。
当一个service的标签选择器匹配到多个pod,即有多个endpoints时,默认的调度是随机分配到service下面的endpoints。(kube-proxy分布式负载均衡)。
►
NodePort Service
集群外部的客户,可以通过任何一个node的nodeIP和port访问nodeport类型的service,典型的即通过任一node的floating IP+暴露出来的port访问。K8s会预留一个端口范围给nodeport用,默认是30000~32767之间的端口。集群内部的用户,仍然可以通过clusterIP访问,nodePort type service 如下图:
►
Service forwarding(kube-proxy)
K8s环境搭建完毕,需要创建一些辅助性的系统级的pod,比如kube-proxy pod和coreDNS pod来帮助我们使用k8s service,如下即kube-system namespace下的系统级的pod:
Kube-proxy转发规则有iptables和ipvs两种,比如当前我们测试环境中使用了iptables。
[root@zoe-0 zoe0]# netstat -anp|grep “kube-proxy”
tcp 0 0 127.0.0.1:10249 0.0.0.0:* LISTEN 4834/kube-proxy
[root@zoe-0 zoe0]# curl localhost:10249/proxyMode
Iptables
Kube-proxy的作用即为k8s service创建iptables规则,来达到service转发的目的。
►
Service discovery(coreDNS)
K8s创建pod时,会为每个service在pod内创建如下环境变量用于服务发现:
${SVCNAME}_SERVICE_HOST
${SVCNAME}_SERVICE_PORT
这种服务发现存在一定的局限,只能发现同一个namespace内的service,而且service需要先于pod创建。于是coreDNS pod就应运而生了,和kube-proxy一样,coreDNS也是K8s安装后需要立即部署的附加组件,coreDNS pod即用于服务发现。
coreDNS 本身也是一种cluster IP type的service:
创建service资源对象时,coreDNS会为它自动创建DNS记录用于名称解析,于是pod可直接使用标准的DNS名称来访问这些service资源,每个service对象相关的DNS记录包含如下两个:
${SVCNAME}.${NAMESPACE}.${CLUSTER_DOMAIN}
${SVCNAME}.${NAMESPACE}.svc.${CLUSTER_DOMAIN}
${CLUSTER_DOMAIN}可以通过kubelet启动时指定,默认是cluster.local
这些信息会在kubelet创建pod时以 DNS配置的相关信息注入到pod内container的/etc/resolv.conf配置文件中。
我们可以通过nslookup来检查coreDNS解析出来的service IP:
我们可以查询不在一个namespace下的服务IP,此服务在lds namespace下:
下图是一个cluster IP service发现和转发的流程图:
►
Headless service
有的时候我们不需要service的负载均衡,典型的用法就是statefulset,statefulset控制器创建的pod是有状态的,这样的pods发布的service是不能做负载均衡的。
客户端需要直接访问 service资源的后端pod,这时就应该向客户端暴露每个pod的IP地址,而不再是中间层service的clusterIP,这种类型的service称为headless service。
Headless service没有clusterIP,于是kube-proxy便无须处理此类请求,也就没有了负载均衡的需要。
Headless service 的配置如下:
#日常工作痛点分析#
痛点1:对service和namespace理解不够,查询某service一直在代码里hardcode default namespace,如果namespace不是 default,则查询该service失败。
当前解决方案:理解了service的DNS解析策略后,就会知道查询一个service,会按照DNS的resolv.conf里面配置的顺序查找,查询当前namespace下service直接用service name即可匹配第一条规则,如果查询其他namespace下的service,需要用service name.namespace查找,这样匹配到第二条规则。
痛点2:某个container内进程在启动的过程中起了REST server并且把该REST service作为K8s service发布出去,container内进程后续初始化阶段用到了这个K8s service,而当时因为该container所在的 pod下,还有其他container还没有起好,导致该pod没有ready,这样container发布出去的service没有endpoint可用,container初始化失败。
当前解决方案:通过publishNotReadyService解开依赖,虽然K8s并不推荐使用没有ready的service,但此时该container rest service已经起好可用,后续的初始化可以直接使用此K8s service, 而不需要等到其他container ready,即此pod内的 container没有太大的耦合性。
痛点3:有些pipeline为了节省资源没有部署log服务的 pod,被测container打印每条log都需要查找log service的DNS,search所有的后缀,导致DNS繁忙,从而影响container内其他K8s service的DNS查询。
当前解决方案:部署log service pod,减少无效DNS解析,我们需要关注coreDNS pod的CPU load。
痛点4:Headless service没有clusterIP,K8s service直接被解析成匹配到的pod IP,但是service的DNS有缓存,默认30s,2N的pod发生switchover后,发送到service的请求不能立即转发到新的active pod上。
当前解决方案:修改headless service为cluster IP service, 虽然目前我们的2N service都是改成了statefulset的pod,但是我们不是真正的有状态服务,比如我们的PVC在switchover后都是希望继承的,而不是两个独立的 PVC。
以上即为我们在使用K8s service的实际操作和经验分享,希望对大家有所帮助。
END
诺基亚杭州测试协会
对外
“诺蓝测试”
扫描上方二维码
关注更多精彩信息