在 Kubernetes 大行其道的今天,数据库容器化对于云原生团队而言是一个极具吸引力,但往往不知道从何下手的挑战。
开源 MySQL、PostgreSQL 诞生于 PC Server 时代,往往被用于存储业务的重要数据,放到 Kubernetes 里面也许需要更多的投入和勇气;但是对于 Redis 这种跟容器技术诞生于同一个时代,而且大多只用作缓存用途的数据库,容器化是否就更加容易呢?很多云原生团队确实是这样想的,但是真正实践后才发现 Redis 这个“软柿子”并不是那么好捏。
太容易了吧,只是...
Redis 容器化的起步非常容易,通过 Redis 官方提供的 Docker 镜像几秒钟就可以拉起 Redis 服务。将应用与 Redis 部署在同一个 Kubernetes 极大简化了上手难度,只有两个小小的问题:
Redis 服务并不是高可用的
只要 Redis 容器发生调度,Redis 容器的 IP 就会变化,应用侧的连接池配置也会随之失效。为了适应容器环境的起伏,Redis 容器的 IP 不能直接暴露给应用,需要增加 VIP 或 DNS 域名来提供固定的连接地址。
Redis 服务并不是高可靠的
只要 Redis 容器所在的宿主机发生宕机,Redis 容器的持久化卷就可能发生损坏,应用数据也会随之丢失。虽然有大量开发者将 Redis 当作易失的内存数据库来使用,但是也有不少开发者将 Redis 用作键值数据的持久化存储,所以 Redis 容器化不可避免地涉及到分布式块存储或本地盘同步方案,只有这样才能具备数据持久化能力。
不出意外的话,就要出意外了
通常,雄心勃勃的云原生团队当然不能满足于提供玩具级别的 Redis 服务,对多个 Redis 容器进行编排的进阶方案自然而然地被提上议程。在进阶方案里面,Redis 服务将具有多个分布于不同宿主机的 Redis 容器(副本),可以应对一到多个 Redis 容器的故障,能够提供持续可靠的服务。
Redis 内核并不具备分布式能力,需要引入外部组件来完成 Redis 容器的角色判断和复制关系配置。Sentinel(哨兵)组件在这方面久经考验,社区也提供了相应的容器编排方案。用 Bitnami 提供的 Redis Helm Chart 就可以很容易部署出含有 Sentinel 组件的 Redis 主备集群。只要正确配置副本数、规格参数并对内核参数做少量调整,Redis 服务的质量立即就能抬升一个档次。如果应用负载较为稳定,这个方案的效果会很不错。但是一旦涉及到故障或扩缩容场景,马上会遇到几个不那么容易解决的问题:
Redis 服务能力存在永久受损的风险
云原生团队往往没有现成的分布式块存储,本地盘资源相对比较常见。当 Redis 容器被分配到本地盘宿主机后,容器就会变相被“固定”到该宿主机上。遇到硬件故障时,如果宿主机能迅速恢复,那么上面的 Redis 容器也能随之快速恢复;反之,Redis 容器将会一直处于 Pending 状态,无法被调度到其它健康的宿主机上。虽然 Redis 服务依旧是可靠可用的,但是服务能力会受到永久损伤,而 Pending 的容器状态也能顺带逼死患有强迫症的工程师。
Redis 服务能力的天花板较低
在本地盘宿主机场景下,Redis 服务能力的天花板也是比较低的。被“固定”到宿主机上后,Redis 容器的内存使用上限也被限制在了该宿主机上。宿主机上面运行的容器越多,Redis 容器能使用的内存就越少。相同的问题也会发生在 Redis 容器能使用的存储容量上。因为被“固定”在宿主机上,Redis 容器能使用的存储容量上限就是其它容器用剩的宿主机本地盘余量。CPU 资源的问题倒是没那么明显,Redis 用不了多核,云淡风轻。
Redis 服务的扩缩容问题
在遇到业务高峰的时候,难免需要对 Redis 服务进行扩容。根据遇到的容量挑战,扩容手段又分为垂直扩容和横向扩容两个大类。如果业务整体数据量没变,只是需要缓存的热点数据变多了,那么垂直扩容一下 Redis 容器的内存就可以了;但是一旦业务的整体数据量变大了,需要垂直扩容存储容量,云原生团队就无法通过 helm 修改 StatefulSet 配置实现变配,只能切换到手工模式劫持底层 PVC 才能实现。劫持的代价会在以后需要进行横向扩容的时候暴露出来,新增的 Redis 容器会采用老的配置,原本同构、自动化的 Redis 服务,变成了异构、手工的缝合怪。
还有隐藏关卡!
高可用、高可靠方面的问题,从架构设计和拓扑图上一眼就能看出来,一般在 Day 1 就会被云原生团队关注和解决;而服务能力在特定场景下的挑战是比较微妙的,取决于真实的业务场景和云原生团队的经验,往往在 Day 2 才会被察觉。审慎的云原生团队不应该在生产环境使用原生的 Kubernetes 工作负载来运行容器化的数据库,这就像坐着用纸糊的船出海一样危险。
Kubernetes 提供的自定义资源能够更好地聚合存储、计算和网络资源,通过 API 的方式提供“声明式”的数据库服务。目前市面上有几个知名的 Redis Operator 提供了更进一步的方案,帮助云原生团队解决 Day 2 的常见问题,比如:
- Redis Enterprise Operator by RedisLabs
- KubeDB by AppsCode
- KubeBlocks by ApeCloud
- Redis Operator by Spotahome
- Redis Operator by OpsTree
RedisLabs、AppsCode 和 ApeCloud 提供的 Operator 功能是企业级的,在能力方面更完整;而 Spotahome 和 OpsTree 提供的 Redis Operator 则是完全开源的,功能虽然少但胜在简单易懂。通过 Release Notes 和 Changelog 可知,Spotahome 最后一次发布版本的时间是 2022.1.19 而 OpsTree 则是 2022.11.10,对问题的响应速度上面恐怕会差点意思,这一点需要特别注意。
无论选了哪一款 Redis Operator,云原生团队都要有这样一个预期:真实业务场景下的网络环境非常复杂,有可能会对 Redis 服务支持的网络方案带来挑战。这个挑战往往会发生在跨 Kubernetes 部署的新应用需要读写存量 Redis 集群的时候。如果没有规划好应对措施,很有可能会阻碍业务的上线效率。考虑到业务侧存在多种 SDK 的使用方法,Redis 服务需要支持如下的部署模式才能满足长远的要求:
单节点(客户端只访问主库)
- Redis Server 通过 Nodeport 暴露主库的地址
- Redis Server 通过 LoadBalancer 暴露主库的地址
双节点(客户端只访问主库)
- Redis Server 通过 Nodeport 暴露主库的地址
- Redis Server 通过 LoadBalancer 暴露主库的地址
双节点或多节点(客户端访问 Sentinel 实现读写分离)
- Redis Server 和 Sentinel 组件通过 HostNetwork 暴露 Redis、Sentinel 的副本地址
- Redis Server 和 Sentinel 组件通过 Nodeport 暴露 Redis、Sentinel 的副本地址
- Redis Server 和 Sentinel 组件通过 LoadBalancer 暴露 Redis、Sentinel 的副本地址
Sharding
- Redis Server 通过 HostNetwork 暴露副本地址
Sharding + Proxy
- Proxy Server 通过 Nodeport 暴露连接地址
- Proxy Server 通过 LoadBalancer 暴露连接地址
咦,为什么 Redis Sharding 跟其它的形态不一样,只能使用 HostNetwork?这涉及到 Redis 官方跟云厂商的各种博弈。简而言之,就是官方想把 Sharding 形态作为收费功能,而代码的 License 又是 BSD 协议的。为了不让云厂商薅到羊毛,Redis 官方故意没有实现 announce-ip 的功能,使得原生的 Redis Sharding 形态无法运行在云厂商的网络环境。然而,云厂商也不甘示弱,“帮”Redis 官方把 announce-ip 功能的坑给填上了,付出了一点点代价就能继续大卖特卖。讨厌的是,Redis 官方和云厂商的拉锯,导致 Redis Sharding 形态在容器环境里只能使用 HostNetwork 暴露地址,为云原生团队带来了额外的阻力。这些商业利益的纠葛,也是 Redis 容器化过程中需要不断被关注到的。
我还是要捏
是不是觉得 Redis 容器化并不是那么好上手,公有云全托管服务收些溢价也情有可原?
这个感觉没错,但是别急着放弃。公有云厂商最重要的数据库技术方向之一就是容器化,而容器化的挑战起点就是保障弹性能力以及支持多种网络方案。在块存储、对象存储、VPC 网络以及四层负载均衡等支撑性产品的加持下,公有云厂商实现数据库容器化会更加容易,技术方案上面也会更加花哨(比如固定容器 IP、升级 Kuberntes 不重启等等)。而大部分云原生团队没有软件定义存储(SDS)和软件定义网络(SDN)的支持,实现数据库容器化就会更有挑战。
幸运的是,大部分云原生团队需要支持的业务场景也不像公有云厂商所面临的那么复杂,如果能够选对方向、收敛需求,循序渐进地积累生产经验,数据库容器化的考验就不会呼啸而来。业界也不乏 Redis 容器化的实践分享,有的大幅降低了成本,有的让业务团队实现了 self-serving。
冲着提升资源利用率和研发效能的收益,这个柿子就算有点硬,也值得一捏。
想要在生产环境实现 Redis 容器化?KubeBlocks 帮你一起捏一捏这个硬柿子。还想了解更多 Redis 容器化的解决方案?欢迎扫码添加小助手,一起进群讨论交流。
作者信息
张云杨,前阿里云数据库产品负责人,2016-2019 年任阿里云数据库产品总监,负责 RDS、PolarDB、Redis、MongoDB 等核心产品。目前作者就职于云猿生数据,是容器化数据库管控 KubeBlocks 和 Serverless MySQL 的产品负责人。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。