乐趣区

关于k8s:支持Pod-绑定静态-IP-基于K8s的自定义控制器Enhanced-Statefulset


云妹导读:

以后,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 做了扩大:

  1. 新增缓存: 原有 Node 缓存根底上新增 staticIPNode 缓存, 用于计算和缓存 staticIPPod 资源占用状况、缓存 IP 数量、Pod cpu/ 内存 使用量;
  2. 新增 predicate:
  • PodFitsResourcesWithStaticIPPodPred Node 现有资源根底上基于 staticIPPod 占用资源再次过滤, 达到资源预占目标;
  • CheckPodAnnotationWithStaticIPPred 查看 pod 是否蕴含 static ip 的指定 node annotation, 并仅保留指定 node 后果只 fit node 列表。

概括起来外围思路就是将动态 IP Pod 所占用的资源作为一种非凡资源独自标识,在调度时进行匹配调度达到资源预占目标。

CNI 在动态 IP 场景下次要实现以下三个性能:

  1. 依照 Pod annotation 上指定的 IP 调配,若无 IP 则从 IP 池中随机调配一个 IP 并将此记录更新到 Pod 中;
  2. IP 地址预留,当绑定动态 IP 的 Pod 删除重建时,CNI 会查看 static IP CR 记录,如果记录未删除则 IP 地址不开释,确保重建的 Pod 可能拿到绑定的 IP;
  3. 在大规模集群场景下,为了进步 SDN 网络性能,咱们要求 CNI 必须应用 IP range 模式。在这种模式下,弹性网卡上绑定的是 IP CIDR,例如 10.0.0.0/24, 而不是某一个具体 IP。IP 迁徙,开释和申请都是以 CIDR 的模式进行。

最初,咱们通过一个创立流程来展现一下 Enhanced statefulset 对象如何绑定动态 IP:

  1. 用户创立一个 enhanced statefulset 对象,该申请首先发送到 API server;
  2. enhanced statefulset controller 监听到该申请,首先查问是否有该 Pod 对应的 CR 记录,若没有则创立新的 CR,若曾经有 CR,则将 CR 中的信息更新的新建的 Pod 中;
  3. enhanced statefulset controller 开始创立 Pod;
  4. Scheduler 依据 Pod 的 affinity 信息将其调度到相应的节点上,若无 affinity 信息则是新建 pod 失常调度。同时调度时会触发资源预留逻辑确保已有的动态 IP Pod 的资源不被占用;
  5. CNI 查看 Pod 动态 IP 记录,如无记录则随机调配 IP 并将 IP 信息更新到 Pod 上,若有记录则按记录调配;
  6. 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 时, 降级流程如下:

  1. Enhanced Statefulset Controller 将 staticip-example- 3 到 staticip-example- 5 这 3 个正本并发降级到 v2 版本,其中 staticip-example- 4 不可用,因为 MaxUnavailable 以后值为 2,不影响利用持续降级;
  2. 用户将 Partitions 设置为 0,enhanced statefulset controller 将残余 3 个正本 staticip-example- 0 到 staticip-example- 2 并发降级到 v2 版本,其中 staticip-example- 2 不可用;
  3. 随后用户对不可用 Pod 进行手工修复,所有实例均恢复正常。

在执行第二步时,如果第一步降级有两个实例不可用触发 MaxUnavailable 阈值,则用户在第二步即便将 Partitions 设置为 0 也不会触发再次降级。

最初,再和大家聊一下故障迁徙性能。动态 IP 为业务上 Kubernetes 带来便当的同时也带来了问题,其中一个比较突出的问题就是故障迁徙场景。故障迁徙有几个前提条件:

  1. 动态 IP Pod 和其所绑定的 IP 须要迁徙到同一个指标节点上,这样能力保障 Pod 迁徙后 IP 不变;
  2. 后面曾经提到在大规模集群下咱们要求 CNI 必须配置成 IP Range 模式,这种模式下 IP CIDR 不能拆分到更细粒度迁徙,一个节点绑定的一个 IP CIDR 只能迁徙到一个指标节点。这就意味着,所有绑定动态 IP 的 Pod 也必须迁徙到同一个指标。这样就带来了一个问题,怎样才能保障指标节点有足够的资源;
  3. 故障迁徙后,业务心愿最大水平保留原来的物理拓扑,虚机配置与规格;

针对这些问题,咱们以后给出的计划是 node migration。根本流程如下:

  1. 当节点处于失联状态超过容忍的工夫窗口后(用户可依据业务状况配置工夫窗口阈值),node operator 会将此节点禁用;
  2. node operator 会创立一台与故障节点同规格同 AZ 的指标节点;
  3. node operator 将故障节点的 IP 和 Pod 指定迁徙到新节点从新创立,并更新元数据信息;
  4. 将故障节点删除。

Enhanced statefulset 自定义控制器模式既能充分利用 Kubernetes 平台能力,又能结合实际撑持业务场景,满足业务需要,是咱们在一直摸索中找到的方向与思路。目前 Enhanced statefulset 在理论反对业务落地过程中还在一直打磨与欠缺中,在公布策略、故障迁徙等场景还有进一步晋升空间。 将来,咱们一方面会在团体上云过程中针对简单的业务场景积淀更多的控制器模型,另一方面曾经开始布局将这些能力通过开源和产品化的形式提供给更多用户应用。

浏览原文,理解 Kubernetes 集群服务

退出移动版