揭秘有状态服务上 Kubernetes 的核心技术

背景

随着 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࿰

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值