关于腾讯云:揭秘有状态服务上-Kubernetes-的核心技术

56次阅读

共计 15106 个字符,预计需要花费 38 分钟才能阅读完成。

背景

随着 Kubernetes 成为云原生的最热门的解决方案,越来越多的传统服务从虚拟机、物理机迁徙到 Kubernetes,各云厂商如腾讯自研上云也主推业务通过 Kubernetes 来部署服务,享受 Kubernetes 带来的弹性扩缩容、高可用、自动化调度、多平台反对等好处。然而,目前大部分基于 Kubernetes 的部署的服务都是无状态的,为什么有状态服务容器化比无状态服务更难呢?它有哪些难点?各自的解决方案又是怎么的?

本文将联合我对 Kubernetes 了解、丰盛的有状态服务开发、治理、容器化教训,为你浅析有状态容器化的疑难点以及相应的解决方案,心愿通过本文,能帮忙你了解有状态服务的容器化疑难点,并能基于本人的有状态服务场景能灵便抉择解决方案,高效、稳固地将有状态服务容器化后跑在 Kubernetes 上,进步开发运维效率和产品竞争力。

有状态服务容器化挑战

为了简化问题,防止适度形象,我将以罕用的 Redis 集群为具体案例,详解如何将一个 Redis 集群进行容器化,并通过这个案例进一步剖析、拓展有状态服务场景中的共性问题。

下图是 Redis 集群解决方案 codis 的整体架构图(援用自 Codis 我的项目 )。

codis 是一个基于 proxy 的分布式 Redis 集群解决方案,它由以下外围组件组成:

  • zookeeper/etcd,有状态元数据存储,个别奇数个节点部署
  • codis-proxy,无状态组件,通过计算 key 的 crc16 哈希值,依据保留在 zookeeper/etcd 内的 preshard 路由表信息,将 key 转发到对应的后端 codis-group
  • codis-group 由一组 Redis 主备节点组成,一主多备,负责数据的读写存储
  • codis-dashboard 集群管制面 API 服务,能够通过它增删节点、迁徙数据等
  • redis-sentinel,集群高可用组件,负责探测、监听 Redis 主的存活,主故障时发动备切换

那么咱们如何基于 Kubernetes 容器化 codis 集群,通过 kubectl 操作资源就能一键创立、高效治理 codis 集群呢?

在容器化相似 codis 这种有状态服务案例中,咱们须要解决以下问题:

  • 如何用 Kubernetes 的语言形容你的有状态服务?
  • 如何为你的有状态服务抉择适合的 workload 部署?
  • 当 kubernetes 内置的 workload 无奈间接形容业务场景时,又该抉择什么样的 Kubernetes 扩大机制呢?
  • 如何对有状态服务进行平安变更?
  • 如何确保你的有状态服务主备实例 Pod 调度到不同故障域?
  • 有状态服务实例故障如何自愈?
  • 如何满足有状态服务的容器化后的高网络性能需求?
  • 如何满足有状态服务的容器化后的高存储性能需求?
  • 如何验证有状态服务容器化后的稳定性?

下方是我用思维导图系统性的梳理了容器化有状态的服务的技术难点,接下来我别离从以上几个方面为你论述容器化的解决方案。

负载类型

有状态服务的容器化首要问题是如何用 Kubernetes 式的 API、语言来形容你的有状态服务?

Kubernetes 为简单软件世界中的各类业务场景形象、内置了 Pod、Deployment、StatefulSet 等负载类型 (Workload),那么各个 Workload 的应用场景别离是什么呢?

Pod,它是最小的调度、部署单位,由一组容器组成,它们共享网络、数据卷等资源。为什么 Pod 设计上它是一组容器组成而不是一个呢?因为在理论简单业务场景中,往往一个业务容器无奈独立实现某些简单性能,比方你心愿应用一个辅助容器帮忙你下载冷备快照文件、做日志转发等,得益于 Pod 的优良设计,辅助容器能够和你的 Redis、MySQL、etcd、zookeeper 等有状态容器共享同个网络命名空间、数据卷,帮忙主业务容器实现以上工作。这种辅助容器在 Kubernetes 外面叫做 sidecar,广泛应用于日志、转发、service mesh 等辅助场景,已成为一种 Kubernetes 设计模式。Pod 优良设计来源于 Google 外部 Borg 十多年运行教训的总结和升华,可显著地升高你将简单的业务容器化的老本。

通过 Pod 胜利将业务过程容器化了,然而 Pod 自身并不具备高可用、主动扩缩容、滚动更新等个性,因而为了解决以上挑战,Kubernetes 提供了更高级的 Workload Deployment,通过它你能够实现 Pod 故障自愈、滚动更新、并联合 HPA 组件可实现按 CPU、内存或自定义指标实现主动扩缩容等高级个性,它个别是用来形容无状态服务场景的,因而特地适宜咱们下面探讨的有状态集群中的无状态组件,比方 codis 集群的 proxy 组件等。

那么 Deployment 为什么不适宜有状态呢?次要起因是 Deployment 生成的 Pod 名称是变动、无稳固的网络标识身份、无稳固的长久化存储、滚动更新中过程中也无法控制程序,而这些对于有状态而言,是十分重要的。一方面有状态服务彼此通过稳固的网络身份标识进行通信是其高可用、数据可靠性的根本要求,如在 etcd 中,一个日志提交必须要通过集群半数以上节点确认并长久化,在 Redis 中,主备依据稳固的网络身份建设主从同步关系。另一方面,不论是 etcd 还是 Redis 等其余组件,Pod 异样重建后,业务往往心愿它对应的长久化数据不能失落。

为了解决以上有状态服务场景的痛点,Kubernetes 又设计实现了 StatefulSet 来形容此类场景,它能够为每个 Pod 提供惟一的名称、固定的网络身份标识、长久化数据存储、有序的滚动更新公布机制。基于 StatefulSet 你能够比拟不便的将 etcd、zookeeper 等组件较繁多的有状态服务进行容器化部署。

通过 Deployment、StatefulSet 咱们能将大部分事实业务场景的服务进行疾速容器化,然而业务诉求是多样化的,各自的技术栈、业务场景也是迥异的,有的心愿实现 Pod 固定 IP 的,不便疾速对接传统的负载平衡,有的心愿实现公布过程中,Pod 不重建、反对原地更新的,有的心愿能指定任意 Statefulset Pod 更新的,那么 Kubernetes 如何满足多样化的诉求呢?

扩大机制

Kubernetes 设计上对外提供了一个弱小扩大体系,如下图所示(援用自 kubernetes blog),从 kubectl plugin 到 Aggreated API Server、再到 CRD、自定义调度器、再到 operator、网络插件 (CNI)、存储插件 (CSI)。所有皆可扩大,充沛赋能业务,让各个业务可基于 Kubernetes 扩大机制进行定制化开发,满足大家的特定场景诉求。

CRD 和 Aggreated API Server

当你遇到 Deployment、StatefulSet 无奈满足你诉求的时候,Kubernetes 提供了 CRD 和 Aggreated API Server、Operator 等机制给你扩大 API 资源、联合你特定的畛域和利用常识,实现自动化的资源管理和运维工作。

CRD 即 CustomResourceDefinition,是 Kubernetes 内置的一种资源扩大形式,在 apiserver 外部集成了 kube-apiextension-server,不须要在 Kubernetes 集群运行额定的 Apiserver,负责实现 Kubernetes API CRUD、Watch 等惯例 API 操作,反对 kubectl、认证、受权、审计,然而不反对 SubResource log/exec 等定制,不反对自定义存储,存储在 Kubernetes 集群自身的 etcd 上,如果波及大量 CRD 资源须要存储则对 Kubernetes 集群 etcd 性能有肯定的影响,同时限度了服务从不同集群间迁徙的能力。

Aggreated API Server,即聚合 ApiServer,像咱们罕用的 metrics-server 属于此类,通过此个性 Kubernetes 将微小的单 apiserver 按资源类别拆分成多个聚合 apiserver,扩展性进一步增强,新增 API 无需依赖批改 Kubernetes 代码,开发人员本人编写 ApiServer 部署在 Kubernetes 集群中,并通过 apiservice 资源将自定义资源的 group name 和 apiserver 的 service name 等信息注册到 Kubernetes 集群上,当 Kubernetes ApiServer 收到自定义资源申请时,依据 apiservice 资源信息转发到自定义的 apiserver,反对 kubectl、反对配置鉴权、受权、审计,反对自定义第三方 etcd 存储,反对 subResource log/exec 等其余高级个性定制化开发。

总体来说,CRD 提供了简略、无需任何编程的扩大资源创立、存储能力,而 Aggreated API Server 提供了一种机制,让你能对 API 行为有更精细化的控制能力,容许你自定义存储、应用 Protobuf 协定等。

增强型 Workload

为了满足业务上述的原地更新、指定 Pod 更新等高级个性需要,腾讯外部及社区都提供了相应的解决方案。腾讯外部有通过大规模生产环境测验的 StatefulSetPlus(未开源的)和 tkestack TAPP(已开源),社区也还有有阿里的开源我的项目 Openkruise,pingcap 为了解决 StatefulSet 指定 Pod 更新问题也推出了一个目前还处于试验状态的 advanced-statefulset 我的项目。

StatefulSetPlus 是为了满足腾讯外部大量传统业务上 Kubernetes 而设计的,它在兼容 StatefulSet 全副个性的根底上,反对容器原地降级,对接了 TKE 的 ipamd 组件,实现了固定 IP,反对 HPA,反对 Node 不可用时,Pod 主动漂移实现自愈,反对手动分批降级等个性。

Openkruise 蕴含一系列 Kubernetes 增强型的控制器组件,包含 CloneSet、Advanced StatefulSet、SideCarSet 等,CloneSet 是个专一解决无状态服务痛点的 Workload,反对原地更新、指定 Pod 删除、滚动更新过程中反对 Partition,Advanced StatefulSet 顾名思义,是个加强版的 StatefulSet,同时反对原地更新、暂停和最大不可用数。

应用增强版的 workload 组件后,你的有状态服务就具备了传统虚拟机、物理机部署模式下的原地更新、固定 IP 等优越个性。不过,此时你是间接基于 StatefulSetPlus、TAPP 等 workload 容器化你的服务还是基于 Kubernetes 扩大机制定义一个自定义资源,专门用于形容你的有状态服务各个组件,并基于 StatefulSetPlus、TAPP 等 workload 编写自定义的 operator 呢?

前者适宜于简略有状态服务场景,它们组件少、治理不便,同时不须要你懂任何 Kubernetes 编程常识,无需开发。后者实用于较简单场景,要求你懂 Kubernetes 编程模式,晓得如何自定义扩大资源、编写控制器。你能够联合你的有状态服务畛域常识,基于 StatefulSetPlus、TAPP 等增强型 workload 编写一个十分弱小的控制器,帮忙你一键实现一个简单的、多组件的有状态服务创立和管理工作,并具备高可用、主动扩缩容等个性。

基于 operator 扩大

在咱们上文的 codis 集群案例中,就能够抉择通过 Kubernetes 的 CRD 扩大机制,自定义一个 CRD 资源来形容一个残缺的 codis 集群,如下图所示。

通过 CRD 实现申明式形容完你的有状态业务对象后,咱们还须要通过 Kubernetes 提供的 operator 机制来实现你的业务逻辑。Kubernetes operator 它的外围原理就是控制器思维,从 API Server 获取、监听业务对象的冀望状态、理论状态,比照冀望状态与理论状态的差别,执行一致性调谐操作,使理论状态合乎冀望状态。

它的外围工作原理如上图(援用自社区)所示。

  • 通过 Reflector 组件的 List 操作,从 kube-apiserver 获取初始状态数据(CRD 等 )。
  • 从 List 申请返回构造中获取资源的 ResourceVersion,通过 Watch 机制指定 ResourceVersion 实时监听 List 之后的数据变动。
  • 收到事件后增加到 Delta FIFO 队列,由 Informer 组件进行解决。
  • Informer 将 delta FIFO 队列中的事件转发给 Indexer 组件,Indexer 组件将事件长久化存储在本地的缓存中。
  • operator 开发者可通过 Informer 组件注册 Add、Update、Delete 事件的回调函数。Informer 组件收到事件后会回调业务函数,比方典型的控制器应用场景,个别是将各个事件增加到 WorkQueue 中,operator 的各个协调 goroutine 从队列取出音讯,解析 key,通过 key 从 Informer 机制保护的本地 Cache 中读取数据。
  • 比方当收到创立一个 Codis CRD 对象的事件后,发现理论无这个对象相干的 Deployment/TAPP 等组件在运行,这时候你就能够通过的 Deployment API 创立 proxy 服务,TAPP API 创立 Redis 服务等。

调度

在解决完如何基于 Kubernetes 内置的 workload 和其扩大机制形容、承载你的有状态服务后,你面临的第二个问题就是如何确保有状态服务中“等价”Pod 跨故障域部署,确保有状态服务的高可用性?

首先如何了解“等价”Pod 呢?在 codis、TDSQL 集群中,一组 Redis/MySQL 主备实例,负责解决同一个数据分片的申请,通过主备实现高可用。因主备实例 Pod 负责的是同数据分片,因而咱们称之为等价 Pod,生产环境冀望它们应跨故障域部署。

其次如何了解故障域?故障域示意潜在的故障影响范畴,可按范畴分为主机级、机架级、交换机级、可用区级等。一组 Redis 主备案例,至多应该实现主机级高可用,任意一个分片所在的主实例所在的节点故障,备实例应主动晋升为主,整个 Redis 集群所有分片仍可提供服务。同样,在 TDSQL 集群中,一组 MySQL 实例,至多应该实现交换机、可用区级别容灾,以确保外围的存储服务高可用。

那么如何实现下面所述等价 Pod 跨故障域部署呢?

答案是调度。Kubernetes 内置的调度器可依据你的 Pod 所需资源和调度策略,自动化的将 Pod 调配到最佳节点,同时它还提供了弱小的调度扩大机制,让你轻松实现自定义调度策略。个别状况下,在简略的有状态服务场景下,你能够基于 Kubernetes 提供的亲和和反亲和高级调度策略,实现 Pod 跨故障域部署。

假如心愿通过容器化、高可用部署一个含三节点的 etcd 集群,故障域为可用区,每个 etcd 节点要求散布在不同可用区节点上,咱们如何基于 Kubernetes 提供的亲和 (affinity) 和反亲和 (anti affinity) 个性实现跨可用区部署呢?

亲和与反亲和

很简略,咱们能够通过给部署 etcd 的 workload 增加如下的反亲和性配置,申明目标 etcd 集群 Pod 的标签,拓扑域为 node 可用区,同时是硬亲和规定,若 Pod 不 满足规定将无奈调度。

那么调度器又遇到被增加了反亲和配置的 Pod 后是如何调度的呢?

affinity:
  PodAntiAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
    - labelSelector:
        matchExpressions:
        - key: etcd_cluster
          operator: In
          values: ["etcd-test"]
      topologyKey: failure-domain.beta.Kubernetes.io/zone

首先调度器监听到 etcd workload 生成的的待调度 Pod 后,通过反亲和配置中的标签查问出已调度 Pod 的节点、可用区信息,而后在调度器的筛选阶段,若候选节点可用区与已调度 Pod 可用区统一,则淘汰,最初进入评优阶段的节点都是满足 Pod 跨可用区部署等条件限度的节点,依据调度器配置的评优策略,抉择出一个最优节点,将 Pod 绑定到此节点上,最终实现 Pod 跨可用区部署、容灾。

然而在 codis 集群、TDSQL 分布式集群等简单场景中,Kubernetes 自带的调度器可能就无奈满足你的诉求了,然而它提供了如下的扩大机制帮忙你自定义调度策略,实现各种简单场景的调度诉求。

自定义调度策略、extend scheduler 等

首先你能够批改调度器的筛选 / 断言 (predicates) 和评分 / 优先级 (priorities) 策略,配置满足你业务诉求的调度策略。比方你心愿降低成本,用最小的节点数撑持集群所有服务,那么咱们须要让 Pod 尽量优先往满足其资源诉求、已分配资源较多的节点上调度。此场景,你就能够通过批改 priorities 策略,配置 MostRequestedPriority 策略,调大权重。

而后你能够基于 Kubernetes 调度器实现 extend scheduler,在调度器的 predicates 和 priorities 阶段,回调你的扩大调度服务,已满足你的调度诉求。比方你心愿负责同一个数据分片的一组 MySQL 或 Redis 主备实例实现跨节点容灾,那么你就能够实现本人的 predicates 函数,将同组已调度 Pod 的节点从候选节点中删除,保障进入 priorities 流程的节点都是满足你业务诉求的。

接着你能够基于 Kubernetes 的调度器实现本人独立的调度器,部署独立的调度器到集群后,你只须要通过将 Pod 的 schedulerName 申明为你独立的调度器即可。

scheduler framwork

最初 Kubernetes 在 1.15 版本中推出了一个新的调度器扩大框架,它在调度器的外围流程前后都减少了 hook。抉择待调度 Pod,反对自定义排队算法,筛选流程提供了 PreFilter 和 Filter 接口,评分流程减少了 PreScore,Score,NormalizeScore 等接口,绑定流程提供 PreBind 和 Bind,PostBind 三个接口。基于新的调度器扩大框架,业务可更加精细化、低成本的管制调度策略,自定义调度策略更加简略、高效。

高可用

解决完调度问题后,咱们的有状态服务曾经能够高可用的部署了。然而高可用部署不代表服务能高可用的对外的提供服务,容器化后咱们兴许会遇到比传统物理机、虚拟机模式部署更多的稳定性挑战。稳定性挑战可能来自业务编写的 operator、Kubernetes 组件、docker/containerd 等运行时组件、linux 内核等,那如何应答以上各种因素导致的稳定性问题呢?

咱们在设计上应把 Pod 异样当作常态化案例解决,任一 Pod 异样后,在容器化场景中,咱们该当具备自愈的机制。若是无状态服务,咱们只需为业务 Pod 增加正当的存活和就绪查看即可,Pod 异样后主动重建,节点故障 Pod 主动漂移到其余节点。然而在有状态服务场景中,即使承载你有状态服务的 workload,反对节点故障后 Pod 主动漂移性能,却也可能会因 Pod 自愈工夫过长和数据安全性等无奈满足业务诉求,为什么呢?

假如在 codis 集群中,一个 Redis 主节点所在 node 忽然”失联“了,此时若期待 5 分钟才进入自愈流程,那么对外将造成 5 分钟的不可用性,显然对重要的有状态服务场景是无奈承受的。即使你放大节点失联自愈工夫,你也无奈保障其数据安全性,万一此时集群网络呈现了脑裂,失联节点也在对外提供服务,那么将呈现多个 master 双写,最终可能导致数据失落。

那么有状态的服务平安的高可用解决方案是什么呢?这取决于有状态服务自身高可用实现机制,Kubernetes 容器平台层是无奈提供平安的解决方案。罕用的有状态服务高可用解决方案有主备复制、去中心化复制、raft/paxos 等共识算法,上面我别离繁难论述三者的区别和优劣势,以及介绍在容器化过程中的注意事项。

主备复制

像咱们下面探讨的 codis 集群案例、TDSQL 集群案例都是基于主备复制实现的高可用,实现上相比去中心化复制、共识算法较简略。主备复制又可分为主备全同步复制、异步复制、半同步复制。

全同步复制是指主收到一个写申请后,必须期待全副从节点确认返回后,能力返回给客户端胜利,因而若一个从节点故障,整个零碎就会不可用,这种计划为了保障多正本集的一致性,而就义了可用性,个别应用不多。

异步复制是指主收到一个写申请后,可及时返回给 client,异步将申请转发给各个正本,然而若还未将申请转发到正本前就故障了,则可能导致数据失落,但可用性是最高的。

半同步复制介于全同步复制、异步复制之间,它是指主收到一个写申请后,至多有一个正本接收数据后,就能够返回给客户端胜利,在数据一致性、可用性上实现了均衡和取舍。

基于主备复制模式实现的有状态服务,业务须要实现、部署主备切换的 HA 服务,HA 服务按实现架构,可分为被动上报型和分布式探测型。被动上报型以 TDSQL 中 MySQL 主备切换为例,各个 MySQL 节点上部署有 agent,向元数据存储集群 (zookeeper/etcd) 上报心跳,若 master 心跳失落,HA 服务将疾速发动主备切换。分布式探测型以 Redis sentinel 为例,部署奇数个哨兵节点,各个哨兵节点定时探测 Redis 主备实例的可用性,彼此之间通过 gossip 协定交互探测后果,若对一个主 Redis 节点故障达到多数派认可,那么就由其中一个哨兵发动主备切换流程。

总体来说,基于主备复制的有状态服务,在传统的部署模式,节点故障后,依赖运维、人工替换节点。容器化后的有状态服务,可通过 operator 实现故障节点主动替换、疾速垂直扩容等个性,显著升高运维复杂度,然而 Pod 可能会产生重建等,应部署负责主备切换的 HA 服务,负责主备 Pod 的切换,以进步可用性。若业务对数据一致性十分敏感,较频繁的切换的可能会导致增大失落数据的概率,可通过应用 dedicated 节点、稳固及较新的运行时和 Kubernetes 版本等缩小不稳固因素。

去中心化复制

跟主从复制相同的就是去中心化复制,它是指在一个 n 正本节点集群中,任意节点都可承受写申请,但一个胜利的写入须要 w 个节点确认,读取也必须查问至多 r 个节点。你能够依据理论业务场景对数据一致性的敏感度,设置适合 w / r 参数。比方你心愿每次写入后,任意 client 都能读取到新值,若 n 是 3 个正本,你能够将 w 和 r 设置为 2,这样当你读两个节点时候,必有一个节点含有最近写入的新值,这种读咱们称之为法定票数读 (quorum read)。

AWS 的 dynamo 零碎就是基于无中心化的复制算法实现的,它的长处是节点角色都是平等的,升高运维复杂度,可用性更高,容器化难度更低,无需部署 HA 组件等,但缺点是去中心化复制,务必会导致各种写入抵触,业务须要关注抵触解决等。

共识算法

基于复制算法实现的数据库,为了保障服务可用性,大多数提供的是最终一致性,不论是主从复制还是去中心化复制,都存在肯定的缺点,无奈满足数据强统一、高可用的诉求。

如何解决以上复制算法的窘境呢?

答案就是 raft/paxos 共识算法,它最早是基于复制状态机背景下提出来的,由共识模块、日志模块、状态机组成,如下图(援用自 Raft 论文 )。通过共识模块保障各个节点日志的一致性,而后各个节点基于同样的日志、程序执行指令,最终各个复制状态机的后果是一致性的。这里我以 raft 算法为例,它由 leader 选举、日志复制、安全性组成,leader 节点故障后,follower 节点可疾速发动新的 leader 选举,并确保数据安全性,follwer 节点故障后,只有少数节点存活,就不影响集群整体可用性。

基于共识算法实现的有状态服务,典型案例是 etcd/zookeeper/tikv 等,在此架构中,服务自身集成了 leader 选举算法、数据同步机制,使得运维和容器化复杂度相比主备复制的服务要显著升高,容器化更加平安。即使容器化过程中遇上 Bug 导致 leader 节点故障,得益于共识算法,数据安全和服务可用性简直不受任何影响,因而优先举荐将应用共识算法的有状态服务进行容器化。

高性能

实现完有状态服务在 Kubernetes 中更稳的运行的指标后,下一步指标则是谋求高性能、更快,而有状态服务的高能又依靠底层容器化网络计划、磁盘 IO 计划。在传统的物理机、虚拟机部署模式中,有状态服务领有固定的 IP、高性能的 underlay 网络、高性能的本地 SSD 磁盘,那么在容器化后,如何达到传统模式的性能呢?我将别离从网络和存储别离繁难论述 Kubernetes 的解决方案。

可扩大的网络解决方案

首先是可扩大、插件化的网络解决方案。得益于 Google 多年的 Borg 容器化运行教训和教训,在 Kubernetes 的网络模型中,每个 Pod 领有独立的 IP,各个 Pod 能够跨主机通信而需 NAT,同时 Pod 也能够与 Node 节点实现网络互通。Kubernetes 优良的网络模型良好的兼容了传统的物理机、虚拟机业务的网络计划,让传统业务上 Kubernetes 更加简略。最重要的是,Kubernetes 提供了凋谢的 CNI 网络插件规范,它形容了如何为 Pod 调配 IP 和实现 Pod 跨节点容器互通,各个开源、云厂商能够基于本人业务业务场景、底层网络,实现高性能、低提早的 CNI 插件,最终达到跨节点容器互通。

在基于 CNI 实现的各种 Kubernetes 的网络解决方案中,按数据包的收发模式实现可分为 underlay 和 overlay 两类。前者是间接基于底层网络,实现互联互通,领有良好的性能,后者是基于隧道转发,它是在底层网络的根底上,加上隧道技术,构建一个虚构的网络,因而存在肯定的性能损失。

这里我别离以开源的 flannel 和 tke 集群网络计划为例,论述各自的解决方案、优缺点。

在 flannel 中,它设计上后端反对 udp、vxlan、host-gw 等多种转发模式。udp 和 vxlan 转发模式是基于 overlay 隧道转发模式实现,它反对将原始申请封装在 udp、vxlan 数据包内,而后基于 underlay 网络转发给目标容器。udp 是在用户态进行数据的封解包操作,性能较差,个别用于 debug 和不反对 vxlan 协定的低版本内核。vxlan 是在内核态实现了数据的封解包操作,性能损失较小。host-gw 模式则是间接通过下发每个子网的 IP 路由信息到各个节点上,实现跨主机的 Pod 网络通信,无需数据包的封解包操作,相比 udp/vxlan,性能最佳,但要求各主机节点的二层网络是连通的。

在 tke 集群网络计划中,咱们也反对多种网络通信计划,经验了从 global route、VPC-CNI 到 Pod 独立网卡的三种模式的演进。global route 即全局路由,每个节点退出集群时,会调配一个惟一的 Pod cidr,tke 会通过 VPC 的接口下发全局路由到用户 VPC 的子机所在的母机上。当用户 VPC 的容器、节点拜访的 ip 属于此 Pod cir 时,就会匹配到此全局路由规定,转发到指标节点上。此计划中 Pod CIDR 并不属于 VPC 资源,因而它不是 VPC 的一等公民,无奈应用 VPC 的平安组等个性,然而其简略、同时在用户 VPC 层不须要任何的数据解封包操作,性能无较大的损失。

为了解决容器 Pod IP 不是 VPC 一等公民而导致一系列 VPC 个性无奈应用的问题,tke 集群实现了 VPC-CNI 网络模式,Pod IP 来自用户 VPC 的子网,跨节点容器网络通信、节点与容器通信与 VPC 内的 CVM 节点通信原理统一,底层都是基于 VPC 的 GRE 隧道路由转发实现,数据包在节点内通过策略路由转发到指标容器。基于此计划,容器 Pod IP 可享受 VPC 的个性,实现 CLB 直连 Pod,固定 IP 等一系列高级个性。

近期为了满足游戏、存储等业务对容器网络性能更加极致的要求,TKE 团队又推出了下一代网络计划,Pod 独占弹性网卡的 VPC-CNI 模式,不再通过节点的网络协议栈,极大缩短容器拜访链路和延时,并使 PPS 能够达到整机下限。基于此计划咱们实现了 Pod 绑定 EIP/NAT,不再依赖节点的外网拜访能力,反对 Pod 绑定平安组,实现 Pod 级别的平安隔离,具体可阅读文章开端的相干文章。

基于 Kubernetes 的可扩大网络模型,业务能够实现特定场景的高性能网络插件。比方腾讯外部的 tenc 平台,基于 SR-IOV 技术的实现了 sriov-cni CNI 插件,它能够给 Kubernetes 提供高性能的二层 VLAN 网络解决方案。特地是对网络性能要求高的场景,比方分布式机器学习训练,游戏后端服务等。

可扩大的存储解决方案

介绍完可扩大的网络解决方案后,有状态服务的另一大外围瓶颈则是高性能存储 IO 诉求。在传统的部署模式中,有状态服务个别应用的是本地硬盘,并依据服务的类型、规格、对外的 SLA,抉择 HDD、SSD 等不同类型的磁盘。那么在 Kubernetes 中如何满足不同场景下的存储诉求呢?

在 Kubernetes 存储体系中,此问题被拆分成若干个子问题来优雅解决,并具备良好的可扩展性、可维护性,无论是本地盘、还是云盘、NFS 等文件系统都可基于其扩大实现相应的插件,并实现了开发、运维职责拆散。

那么 Kubernetes 的存储体系是如何构建的呢?

我通过如何给你的有状态 Pod 利用挂载一个数据存储盘为案例,来介绍 Kubernetes 的可扩大存储体系,它能够分为以下步骤:

  • 利用如何申请一个存储盘呢?(消费者 )
  • Kubernetes 存储体系是如何形容一个存储盘的呢?人工创立存储盘呢还是自动化按需创立存储盘?(生产者)
  • 如何将存储资源池的盘与存储盘申请者的诉求进行匹配?(控制器)
  • 如何形容存储盘的类型、数据删除策略、以及此类型盘的服务提供者信息呢?(storageClass)
  • 如何实现对应的存储数据卷插件?(FlexVolume、CSI)

首先 Kubernetes 中提供了一个名为 PVC 的资源,形容利用申请的存储盘的类型、拜访模式、容量规格,比方你想给 etcd 服务申请一个存储类为 cbs,大小 100G 的云盘,你能够创立一个如下的 PVC。

apiVersion: v1
kind: PersistentVolumeClaim
spec:
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 100Gi
  storageClassName: cbs

其次 Kubernetes 中提供了一个名为 PV 的资源,形容存储盘的类型、拜访模式、容量规格,它对应一块实在的磁盘,反对通过人工和主动创立两种模式。下图形容的是一个 100G 的 cbs 硬盘。

apiVersion: v1
kind: PersistentVolume
spec:
  accessModes:
  - ReadWriteOnce
  capacity:
    storage: 100Gi
  persistentVolumeReclaimPolicy: Delete
  qcloudCbs:
    cbsDiskId: disk-r1z73a3x
  storageClassName: cbs
  volumeMode: Filesystem

接着,当利用创立一个 PVC 资源时,Kubernetes 的控制器就会尝试将其与 PV 进行匹配,存储盘的类型是否统一、PV 的容量大小是否满足 PVC 的诉求,若匹配胜利,此 PV 的状态会变成绑定,控制器会进一步的将此 PV 对应的存储资源 attach 到利用 Pod 所在节点上,attach 胜利后,节点上的 kubelet 组件会将对应的数据目录挂载到存储盘上,进而实现读写。

以上就是利用申请一个盘的流程,那么在容器中如何通过 PV/PVC 这套体系实现反对多种类型的块存储和网络文件系统呢?比方块存储服务反对一般 HHD 云盘,SSD 高性能云盘,SSD 云盘,本地盘等,近程网络文件系统反对 NFS 等。其次是 Kubernetes 控制器如何按需动静的去创立 PV 呢?

为了反对多种类型的存储诉求,Kubernetes 提供了一个 StorageClass 的资源来形容一个存储类。它形容了存储盘的类别、绑定和删除策略、由什么服务组件提供资源创立。比方高性能版和根底版的 MySQL 服务依赖不同类型的存储磁盘,你只须要创立 PVC 的时候填写相应的 storageclass 名字即可。

最初,Kubernetes 为了反对开源社区、云厂商泛滥的存储数据卷,提供了存储数据卷扩大机制,从晚期的 in-tree 的内置数据卷、到 FlexVolume 插件、再到当初曾经 GA 的的容器化存储 CSI 插件机制,存储服务提供商可将任意的存储系统集成到 Kubernetes 存储体系中。比方 storage cbs 的 provisioner 是腾讯云的 TKE 团队,咱们会基于 Kubernetes 的 flexvolume/CSI 扩大机制,通过腾讯云 CBS 的 API 实现创立、删除 cbs 硬盘。

apiVersion: storage.Kubernetes.io/v1
kind: StorageClass
parameters:
  type: cbs
provisioner: cloud.tencent.com/qcloud-cbs
reclaimPolicy: Delete
volumeBindingMode: Immediate

为了满足有状态等服务对磁盘 IO 性能的极致谋求,Kubernetes 基于以上介绍的 PV/PVC 存储体系,实现了 local pv 机制,它可防止网络 IO 开销,让你的服务领有更高的 IO 读写性能。local pv 外围是通过将本地盘、lvm 分区形象成 PV,应用 local pv 的 Pod,依赖提早绑定个性实现精确调度到指标节点。

local pv 的要害核心技术点是容量隔离 (lvm、xfs quota)、IO 隔离 (cgroup v1 个别要定制内核,cgroup v2 反对 buffer io 等)、动静 provision 等问题,为了解决以上或局部痛点,社区也诞生了一系列的开源我的项目,如 TopoLVM(反对动静 provision、lvm),sig-storage-local-static-provisioner 等我的项目,各云厂商如腾讯外部也有相应的 local pv 解决方案。总体而言,local pv 实用于磁盘 io 敏感型的 etcd、MySQL、tidb 等存储服务,如 pingcap 的 tidb 我的项目就举荐在生产环境应用 local pv。

local pv 的毛病是节点故障后,数据无法访问、可能失落、无奈垂直扩容 (受限于节点磁盘容量等)。因而这对有状态服务自身和其 operator 提出了更高要求,服务自身须要通过主备复制协定和共识算法,保证数据安全性。任一节点故障后,operator 能及时扩容新节点,从冷备、leader 快照进行数据恢复。如 tidb 的 tikv 服务,当检测到实例有异样后,会主动扩容新实例,通过 raft 协定实现数据的复制等。

混沌工程

通过以上技术计划,解决了负载类型选型、自定义资源扩大、调度、高可用、高性能网络、高性能存储、稳定性测试等一系列痛点后,咱们可基于 Kubernetes 的构建稳固、高可用、弹性伸缩的有状态服务。

那么如何验证容器化后的有状态服务稳定性呢?

社区提供了多个基于 Kubernetes 实现的混沌工程开源我的项目,比方 pingcap 的 chaos-mesh,提供了 Pod chaos/Network chaos/IO chaos 等多种故障注入。基于 chaos mesh,你能够疾速注入 Pod 故障、磁盘 IO、网络 IO 等异样到集群中任意 Pod,帮忙你疾速发现有状态服务自身和 operator Bug、测验集群的稳定性。在 TKE 团队中,咱们基于 chaos mesh 排查、复现 etcd Bug,压测 etcd 集群的稳定性,极大的升高了咱们复现简单 Bug 的难度,帮忙咱们晋升 etcd 内核的稳定性。

总结

本文通过从有状态集群中的各个组件 workload 选型、扩大机制的抉择,介绍了如何应用 Kubernetes 的形容、部署你的有状态服务。有状态服务出于其特殊性,数据安全、高可用、高性能是其外围指标,为了保障服务的高可用,可通过调度和 HA 服务来实现。通过 Kubernetes 的多种调度器扩大机制,你能够将你的有状态服务的等价 Pod 实现跨故障域部署。通过主备切换服务和共识算法,你能够实现主节点故障后,备节点主动晋升为主,以保障服务的高可用性。高性能次要取决于网络和存储性能,Kubernetes 提供了 CNI 网络模型和 PV/PVC 存储体系、CSI 扩大机制来满足各种业务场景下的定制需要。最初介绍了混沌工程在有状态服务中的利用,通过混沌工程你能够模仿各类异样场景下,你的有状态服务容错性,帮忙你测验和晋升零碎的稳定性。

参考资料
  • custom resources
  • Extend Kubernetes
  • tidb-operator
  • 腾讯云容器服务 TKE 推出新一代零损耗容器网络
  • chaos mesh

容器服务(Tencent Kubernetes Engine,TKE)是腾讯云提供的基于 Kubernetes,一站式云原生 PaaS 服务平台。为用户提供集成了容器集群调度、Helm 利用编排、Docker 镜像治理、Istio 服务治理、自动化 DevOps 以及全套监控运维体系的企业级服务。
【腾讯云原生】云说新品、云研新术、云游新活、云赏资讯,扫码关注同名公众号,及时获取更多干货!!

正文完
 0