云妹导读:
以后,Kubernetes曾经成为云原生的事实标准, Kubernetes原生反对了功能强大的控制器——deployment 、statefulset 、daemonset。但在理论业务场景中,原生控制器无奈满足一些简单和大规模场景中的业务需要。京东智联云开发团队联合多年云原生开发与应用教训,推出了自定义控制器Enhanced statefulset。本文将把咱们的产品性能、技术思考和实现细节全面展示给云原生用户和开发者,以期帮忙大家更好地应用Kubernetes 开始本人的云原生之路。
随着云原生概念逐步深入人心,越来越多的用户开始承受和践行云原生的设计与理念。Kubernetes作为容器治理引擎,提供了弱小的容器编排能力,反对不可变基础设施与申明式Openapi,同时隔离了底层基础设施差别,曾经成为云原生的基石和事实标准。Kubernetes原生反对功能强大的控制器,例如deployment 、statefulset 、daemonset等,能够解决很多用户场景。但随着 Kubernetes 的应用范畴越来越广,原生的控制器曾经无奈满足一些简单和大规模场景中的业务需要。
以京东团体业务为例,因为历史起因,团体内很多根底运维业务包含日志、监控、拜访策略管制、服务发现等都是以IP作为实例惟一标识,这就要求Pod实例可能反对动态IP绑定。同时,咱们并不是简略地为了Kubernetes而上Kubernetes,而是须要尽量利用Kubernetes为业务提供更多的DevOps能力,比方更丰盛的降级策略,故障主动迁徙等。
基于这些思考,最终咱们决定基于CRD扩大(CustomResourceDefinitions,即自定义资源)的机制,通过自定义控制器的形式来提供Pod绑定动态IP性能。这就是本篇文章要讲的产品——Enhanced statefulset。
顾名思义,Enhanced statefulset就是咱们在statefulset的根底上对控制器做了进一步的扩大。它次要解决Pod绑定动态IP问题,同时也解决了statefulset在降级过程中不容许同时降级多个实例的限度。另外,它还能够在节点故障时反对Pod和IP的迁徙。
上面和大家分享一下Enhanced statefulset的外围性能个性:
后面曾经提到了动态IP的重要场景是业务依赖的周边组件都以IP作为实例惟一标识,所以上到Kubernetes后依然须要Pod实例放弃IP不变。置信很多用户也面临着相似的问题,上面就来分享一下实现原理。
咱们次要是通过对Enhanced Statefulset Controller 、 Scheduler、CNI这几个模块扩大来反对Enhanced Statefulset的Pod绑定动态IP。具体模块关系和性能如下图所示:
▲模块关系图▲
Enhanced Statefulset Controller 对动态IP的治理次要是保护更新Static IP CR来实现的。当Controller收到创立申请时,会首先查看要创立的实例是否曾经有对应的static IP CR记录,若记录不存在则会创立一个新的记录。在稍后scheduler实现调度,CNI实现动态IP调配后,controller会监听Pod信息并将其更新到Static IP CR中。反之若创立实例时,对应的static IP CR记录曾经存在则示意这个Pod是删除重建,Controller会将static IP CR中的信息更新到Pod上,scheduler和CNI依据pod上的配置进行亲和性调度和IP调配。
StaticIP CRD中记录了负载类型、负载名称、节点、IP和Pod信息。其中IP信息在Pod实例上以annotation taticip.jke.jdcloud.com/ip-address 形式出现,CNI就是依据这个字段来决定要调配的IP地址。节点信息则是通过affinity属性在pod上出现,这样scheduler就不须要感知节点和IP的拓扑信息,只有依照亲和性调度就能够将Pod调度到动态IP所在的节点上,简化了scheduler的解决逻辑。
1apiVersion: "jke.jdcloud.com/v1" 2kind: StaticIP 3metadata: 4 name: {{workload}}-{{podname}}-{{ipaddress}} 5spec: 6 Ref: {{workload-name}} // 所属 workload 实例名称,如 deployment-xxxx 7 Kind: {{workload}} // workload 类型, 如 deployment 8 Node: Node-{{name}} // node 名称, 如 node-xxxx 9 IP: {{ipaddress}} // 绑定的 ip 地址, 如 10.10.0.110 Pod: {{pod-name}} // pod 名称: pod-xxxxx
〈〈〈左右滑动以查看残缺代码 〉〉〉
咱们对scheduler做的扩大次要是解决Pod资源预留问题。失常流程中当绑定动态IP的Pod删除后,Pod所占用的资源也会被开释,如果有其余新调度的Pod到这个节点上就会占用以后节点的资源。这时如果绑定动态IP的Pod在此节点重建可能就会因为资源有余而失败。为了解决这个问题,咱们对scheduler做了扩大:
- 新增缓存:原有 Node 缓存根底上新增 staticIPNode 缓存,用于计算和缓存 staticIPPod 资源占用状况、缓存 IP 数量、Pod cpu/内存 使用量;
- 新增predicate :
- PodFitsResourcesWithStaticIPPodPred Node 现有资源根底上基于 staticIPPod 占用资源再次过滤,达到资源预占目标;
- CheckPodAnnotationWithStaticIPPred 查看pod 是否蕴含 static ip 的指定 node annotation, 并仅保留指定 node 后果只 fit node 列表。
概括起来外围思路就是将动态IP Pod所占用的资源作为一种非凡资源独自标识,在调度时进行匹配调度达到资源预占目标。
CNI 在动态IP场景下次要实现以下三个性能:
- 依照Pod annotation上指定的IP调配,若无IP则从IP池中随机调配一个IP并将此记录更新到Pod中 ;
- IP地址预留,当绑定动态IP的Pod删除重建时,CNI会查看static IP CR记录,如果记录未删除则IP地址不开释,确保重建的Pod可能拿到绑定的IP;
- 在大规模集群场景下,为了进步SDN网络性能,咱们要求CNI必须应用IP range模式。在这种模式下,弹性网卡上绑定的是IP CIDR,例如10.0.0.0/24,而不是某一个具体IP。IP迁徙,开释和申请都是以CIDR的模式进行。
最初,咱们通过一个创立流程来展现一下Enhanced statefulset对象如何绑定动态IP:
- 用户创立一个enhanced statefulset对象,该申请首先发送到API server;
- enhanced statefulset controller监听到该申请,首先查问是否有该Pod对应的CR记录,若没有则创立新的CR,若曾经有CR,则将CR中的信息更新的新建的Pod中;
- enhanced statefulset controller开始创立Pod;
- Scheduler依据Pod的affinity信息将其调度到相应的节点上,若无affinity信息则是新建pod失常调度。同时调度时会触发资源预留逻辑确保已有的动态IP Pod的资源不被占用;
- CNI查看Pod动态IP记录,如无记录则随机调配IP并将IP信息更新到Pod上,若有记录则按记录调配;
- StaticIP controller监听到Pod上动态IP信息变更,并将此信息更新到CR中。
除了反对动态IP这个强需要,咱们思考的第二个重点就是尽可能将Kubernetes的DevOps能力赋能给业务场景。社区原生的 StatefulSet 在降级过程中不容许同时降级多个实例,这次要是为了某些有状态利用须要顺次按序降级的需要。但这样带来的问题是效率太低,而团体业务对降级失败和程序有肯定容忍度,为了晋升降级效率,咱们定义了MaxUnavailable 参数,它容许利用实例被并行降级,且始终保持最大不可用的实例数不超过 MaxUnavailable 的限度数。
此外,为了保障降级足够可控,Enhanced Statefulset能够通过Partitions进行分批降级。每个批次降级实现后通过再次更新Partitions触发下一次降级,如果发现降级过程中遇到问题也能够进行Rollback回滚或Paused暂停。
通过这些优化,Enhanced Statefulset具备更好了灵活性,既能够兼容原生Statefulset规定严格依照实例程序降级,确保有状态服务的可靠性。又能够兼具相似Deployment的能力,以更高效的形式并发降级。同时还能够分批手工触发,根本笼罩了团体业务的绝大部分场景。
上面通过一个示例来具体理解下:
用户创立了一个Enhanced Statefulset的利用,正本数为6,利用从staticip-example-0到staticip-example-5,Partitions设置为3, MaxUnavailable设置为2。
1apiVersion: jke.jdcloud.com/v1alpha1 2kind: EnhancedStatefulSet 3metadata: 4 name: staticip-example 5 annotations: 6 staticip.jke.jdcloud.com/enable: "true" #关上动态IP性能 7spec: 8 serviceName: enhanced-sts-service-example 9 replicas: 610 selector:11 matchLabels:12 apps: staticip-example13 updateStrategy:14 rollingUpdate:15 maxUnavailable: 2 #最大不可用数量,容许并行降级,并且容忍正本不可用16 partition: 3 #enhanced statefulset创立的Pod都有index,命名从0开始,例如pod-0 pod-1 所有index大于等于partition值的实例降级,通过变更partition值来实现分批降级17 paused: false18 podUpdatePolicy: ReCreate19 type: RollingUpdate20 template:21 metadata:22 labels:23 apps: staticip-example24 spec:25 containers:26 - image: nginx:v1 # nginx:v1 变更为 nginx:v2 触发降级
〈〈〈 左右滑动以查看残缺代码 〉〉〉
当用户将镜像从v1降级到v2时,降级流程如下:
- Enhanced Statefulset Controller将staticip-example-3到staticip-example-5这3个正本并发降级到v2版本,其中staticip-example-4不可用,因为MaxUnavailable以后值为2,不影响利用持续降级;
- 用户将Partitions设置为0,enhanced statefulset controller将残余3个正本staticip-example-0到staticip-example-2并发降级到v2版本,其中staticip-example-2不可用;
- 随后用户对不可用Pod进行手工修复,所有实例均恢复正常。
在执行第二步时,如果第一步降级有两个实例不可用触发MaxUnavailable阈值,则用户在第二步即便将Partitions设置为0也不会触发再次降级。
最初,再和大家聊一下故障迁徙性能。动态IP为业务上Kubernetes带来便当的同时也带来了问题,其中一个比较突出的问题就是故障迁徙场景。故障迁徙有几个前提条件:
- 动态IP Pod和其所绑定的IP须要迁徙到同一个指标节点上,这样能力保障Pod迁徙后IP不变;
- 后面曾经提到在大规模集群下咱们要求CNI必须配置成IP Range模式,这种模式下IP CIDR不能拆分到更细粒度迁徙,一个节点绑定的一个IP CIDR只能迁徙到一个指标节点。这就意味着,所有绑定动态IP的Pod也必须迁徙到同一个指标。这样就带来了一个问题,怎样才能保障指标节点有足够的资源;
- 故障迁徙后,业务心愿最大水平保留原来的物理拓扑,虚机配置与规格;
针对这些问题,咱们以后给出的计划是node migration。根本流程如下:
- 当节点处于失联状态超过容忍的工夫窗口后(用户可依据业务状况配置工夫窗口阈值),node operator会将此节点禁用;
- node operator会创立一台与故障节点同规格同AZ的指标节点;
- node operator将故障节点的IP和Pod 指定迁徙到新节点从新创立,并更新元数据信息;
- 将故障节点删除。
Enhanced statefulset自定义控制器模式既能充分利用Kubernetes平台能力,又能结合实际撑持业务场景,满足业务需要,是咱们在一直摸索中找到的方向与思路。目前Enhanced statefulset在理论反对业务落地过程中还在一直打磨与欠缺中,在公布策略、故障迁徙等场景还有进一步晋升空间。将来,咱们一方面会在团体上云过程中针对简单的业务场景积淀更多的控制器模型,另一方面曾经开始布局将这些能力通过开源和产品化的形式提供给更多用户应用。
浏览原文,理解Kubernetes集群服务
发表回复