诺基亚杭州测试2022年第01期文章分享-第四篇

"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

诺基亚杭州测试协会

对外

“诺蓝测试”

扫描上方二维码

关注更多精彩信息