富贵险中求!是否应在Kubernetes上运行Redis?
针对无状态服务,业界已拥有成熟解决方案,但对于有状态服务(如数据库、Redis)是否适合容器化与K8s托管,仍存在争议。本文将基于快手在 Redis 云原生化实践中的经验,探讨有关有状态服务的云原生化思考及应对方案。
一、背景
有状态服务究竟是否适合在 K8S上运行? 有状态服务的云原生化如何实现与落地?
二、有状态服务是否适合运行在K8S上?
提升资源利用率:通过“合池”、“统一调度”和“混部”,优化资源使用,显著降低成本。 提高运维效率:利用 Kubernetes 的声明式 API 和控制器模型,提升业务运维效率,确保技术先进性。 降低运维成本:统一基础设施减少运维维护成本,提升整体管理效率。
K8S 社区推出了 StatefulSet Workload,为每个实例分配一个全局有序递增的编号。这一机制为实例提供了稳定的网络标识和存储标识,从而维护了拓扑状态和数据状态。
然而,拓扑状态在运行过程动态变化,仅靠 StatefulSet 提供的编号难以满足需求。
针对已有 Workload 无法应对动态拓扑关系等复杂场景的问题,自定义 Workload 和 Operator 提供了扩展能力,这也促进了 Redis Operator、MySQL Operator 等多种有状态 Operator 的出现。
然而,从零开始开发一套 Operator 的成本往往使许多数据库团队却步。即使复用现有的 Operator,企业内自定义运维逻辑与现有逻辑的融合依然复杂且具挑战性。
三、快手如何将Redis运行在K8S上?
多分片与多实例关系表达:需清晰定义多分片架构及单分片下的多实例层次关系。同时,需要支持分片数量和单分片下多实例数量的动态变化,以应对不同负载和使用场景的需求。
生命周期变化过程中数据管理:在分片或实例生命周期变化时,如何有效管理数据是一个重要挑战。需确保数据的一致性和可靠性,包括分片数量变化时的数据重平衡,以及分片内实例数量变化时的数据备份与恢复策略等。
单分片内多实例动态拓扑关系的表达:在一个分片下,如何实时表达多个实例之间的动态拓扑关系变化,并基于实时拓扑结构实现服务发现和灰度发布等能力。
分层架构设计:基于不同 Workload 分别管理分片及其下的实例;具体而言,使用一个 StatefulSet 管理每个分片下的多个实例,并在此基础上构建一层新的工作负载,以实现对整个 Redis Server 集群中多个分片的统一管理;
生命周期钩子支持:支持分片和实例维度的生命周期 Hook 能力,允许在不同生命周期阶段执行自定义的数据管理操作;
动态拓扑感知与服务发现:引入角色探测和角色标记能力,进一步实现系统基于动态拓扑关系的服务发现和灰度发布能力。
与 StatefulSet 相比,其增加支持了角色定义、角色探测方式以及角色更新策略的定义抽象;同时,InstanceSet 控制器在实例运行过程中会动态探测角色变化,并将角色信息以标签(label)的方式更新到实例的元数据中,从而支持基于角色的服务发现等能力。
除此之外,快手和 KubeBlocks 社区一起共建了 InstanceSet 直管 Pod 和 PVC、InstanceSet 实例异构配置、指定实例缩容、并发控制、多种更新策略等增强能力,使其能够灵活应对复杂的业务场景。
Component:将组件的定义与组件实例解耦。通过引用组件定义,可以生成对应的 InstanceSet,使得组件管理更加灵活;并支持实例维度的生命周期管理。
Shard:用于生成一组相同的 Component 实例,主要适用于类似 Redis Server 的分片场景,便于管理和扩展;并支持分片维度的生命周期管理。
Cluster:用于定义整个有状态服务集群,在 Redis 场景下,可以统一表达 Proxy、Server 和 Sentinel ,并设置它们的启动拓扑关系。
统一调度能力:通过联邦作为统一资源下发入口,实现实例在多个成员集群之间的调度。
统一视图能力:联邦作为统一资源获取入口,统一获取联邦和成员集群的相关资源。
InstanceSet Controller 放置在成员集群中 Cluster Operator 和 Component Operator 放置在联邦集群中
调度决策:根据调度建议,决策每个集群应该部署多少个实例; InstanceSet 拆分与分发:负责拆分 InstanceSet,并将其分发到多个成员集群。
实例拆分管理:确保在联邦部署模式下,Redis 集群的实例列表全局有序唯一; 管控规则拆分:保证在联邦部署模式下,InstanceSet 的管控规则全局符合预期。这包括管理灰度变更的顺序和并发度控制等。
如何区分预期与非预期的运维操作? 如何系统性避免非预期的运维操作?
Redis 团队重点关注如何定义 Redis Cluster 对象,并将已有的运维经验通过定义的方式进行转化; 而将 Redis Cluster 对象提交给 K8S 后的工作,则由容器云团队进行保障,包括 Operator 的研发与维护、调度处理等。
四、总结