Rails Rails On K8S 使用两周年的经验与教训

pengpeng · 2023年02月14日 · 最后由 amonlei 回复于 2023年03月19日 · 922 次阅读

我所在的公司主要产品是一款 SaaS 化的护理管理系统,当前业务系统后台技术栈为 Rails + MySQL,前端技术栈为 jQuery + Bootstrap。产研团队近三年保持在 10 人左右,产品与技术的人员比例维持在 1:3 左右。

为什么选择 Kubernetes

从 2021 年之前,基本上也是采用裸机部署,从最初的 1 台机器,扩展到 3 台机器(机器规格:4C16G),但是在业务高峰时,服务器负载过高,导致服务不可用,这时候就需要手动扩容,需要多久才能让业务恢复正常,全凭运维同学手速。这种情况下,我们开始考虑如何实现快速弹性扩缩容,最开始考虑的使用阿里云的弹性伸缩(Elastic Scaling Service),但是并不好用,就放弃了,后来开始考虑使用 Kubernetes 来实现弹性扩容。

虽然我认为我准备的很充分了,也搭建了线上真实 K8S 环境测试,但是还是遇到了很多坑,这里就简单记录一下。对我们团队来说,收益远大于投入。

一次只做一件事

由于当时的服务器和数据库资源都在阿里云青岛机房,当时阿里云青岛时不支持 VPC 的,不支持 VPC 就无法建立 K8S 集群,所有我就选择了同时迁移数据库和迁移服务到 K8S 集群,然后就出了大问题,最终折腾了 1 周,才把数据库迁移过来,并把业务运行稳定下来,还是领导替我顶住了压力,如果在大公司,我可能就被辞退了。

静态资源 CDN

K8S 部署之后,访问量突增,排查下来是静态资源没有走 CDN 的缘故,之前单机部署的时候,Nginx 代理一下就好了,上到 K8S 之后,需要做一些处理,简单说就是把静态资源发布之前推到 CDN,然后再发布到 K8S,这样就可以保证静态资源走 CDN,这样就可以保证静态资源的访问速度。

弹性伸缩

用了一段时间 K8S 后,发现弹性伸缩并不理想,业务高峰依然会出现 504 之类的错误,大部分问题是数据库查询性能不佳,导致数据库压力过大,后来就开始优化业务查询性能,优化后,弹性伸缩还是不理想,结合自己的业务特性,调整伸缩阈值,同时配合定时扩缩容,基本上做到了降本增效。

抢占式实例

在没有使用 K8S 之前,数据库用的是阿里云 RDS,服务器用的是阿里云 ECS,基本上会采取包年包月的购买方式,一般一次买 3-5 年,会有 50%-~70% 的折扣。但是调整到阿里云之后,最开始我们也打算继续这种策略,但是并不能节省成本,最终,我们保留了几台基本服务器,其他的都是使用抢占式实例,这样相对来说可以节省成本。

无限扩容

K8S 并不能无限扩容,通常来说,最多的制约因素是数据库连接数,由于在 K8S 上,扩缩容比较方便,很容易在不影响真实业务的情况下,测试系统的极限性能。

新手 5 分钟完整部署

我们的核心业务系统还是一个 Rails 大单体架构(20 万 行代码),但是对于刚加入一周的新手同学也能平稳地完成一次部署。

我们的部署流程简单粗暴:

  1. 提交代码、PR 审核通过,合并到预发布分支,再合并到主分支。
  2. 如果需要更新静态资源或者执行数据库迁移,执行对应的 rake 任务。
  3. 打包镜像(核心:把代码打包到镜像内)、推送镜像到私有仓库。
  4. 更新对应的 Deployment(先 线上测试环境、后正式环境),K8S 会自动拉取镜像并重启 Pod。 核心命令 bash sh deploy/push.sh #打包镜像并推送到私有仓库,并更新 deploy/*/service/web.yaml 文件中的镜像版本 # mina assets 按需执行 这一步会自动等于到一台服务器上,执行资源编译并推送到 CDN # mina migrate 按需执行 kubectl apply -f deploy/test/service/web.yaml #更新测试环境 kubectl apply -f deploy/pro/service/web.yaml #更新正式环境(滚动更新)

镜像管理

虽然 Docker 提供了多层打包技术,但是我没用,我选择手动分层。

  • 第一层是基础镜像,包含了 Ruby、Node 等基础环境,这个镜像是不会变的,只有在基础镜像有更新的时候才会更新。
  • 第二层是中间镜像,中间镜像是在基础镜像的基础上,执行 bundle install,这个镜像也是不会变的,只有 Gemfile 文件有变动时才需要更新
  • 第三层是最终镜像,最终镜像把代码打包到镜像内,这个镜像是会变的,每次代码有变动时都需要更新。但是由于代码通常只有几十 MB,如果 JS 没变动也不需要编译动作,所以这个镜像打包很快。

基于手动分层的镜像管理,我们可以很方便快速地发布新版本,通常来说,Gemfile 文件变动的频率比较低,所以中间镜像也不会变动太频繁。

200 倍算力的扩容

由于业务特性,每到月、年、季度初都需要生成大量的报表,前面也提到过,K8S 并不能无限扩容,核心限制主要是数据库,为了应对大量报表计算服务(计算服务也是基于 Rails 开发的,计算引擎使用的是 ClickHouse),月末我们会提前手动扩容数据库(MySQL 从库和 MongoDB)一般是数据库 CPU 提升到原来的 32 倍,例如原配置是 1C2G,会扩容到 32C128G。计算服务扩容采用的定时扩容。

原来需要 3~5 天才能完成的报表计算任务,基本上就可以在 3-5 小时内完成。

题外话,数据库有往存储与计算分离的发展趋势,如果计算节点可以自动弹性扩容,这里的报表服务后续也能做到自动扩缩容。

容器化与 Rails 版本有关系吗

没关系,比如我这里有好几个 Rails 3 的项目,也有 Rails 4、5 的项目。都部署在 K8S 集群中,运行良好。

如果能升级 Rails 版本,尽量升级,如果升级成本太高,那就维持现状吧,后续可以考虑服务分拆,把他分解掉。

如何提升团队运维能力

我这里没有专职的运维工程师,每个人都能做一些运维相关的工作,我相对来说了解的多一些,但主要工作还是业务开发。

容器化是趋势,需要技术线领导(CTO)的支持,也需要推动容器化的技术人员提前储备技能,比如在没有上 K8S 之前,我就拿一些小项目用 Docker Compose 练手,这样在上 K8S 的时候,就不会陌生了。

我也让小伙伴们本地尽量基于 Docker 搭建开发环境,提前熟悉容器化相关的技术。

节省成本吗?

服务器的成本我认为并没有节省,在没使用 K8S 之前,服务器的平均使用效率在 40% 左右,使用了 K8S 后,平均使用效率在 20% 左右,但是由于使用的是抢占式实例,所以成本并没有增加太多。 我使用的是阿里云的 ACK 服务,也就是 K8S 的集群是托管于阿里云的,这样就不需要自己搭建 K8S 集群了,节省了很多时间,但是成本也相应的增加了。

人力成本是节省的大头,因为不需要专职的运维工程师了,也不需要救火了,因为 K8S 会自动扩缩容,通过调节 Deployment 配置,也能很多做到蓝绿发布等,之前有 20% 的时间用于救火,现在不到 5%

还是就是建立线上测试环境也比较方便,一条命令即可。

总体来说,成本是节省了,但是不是很多。

安全性提升

我们的报表服务,之前是通过公网调用接口地址,现在都是在集群内部调用,不用 K8S 也能实现内部调用,只是 K8S 的方式更加方便,现在只要不是最终用户调用的服务,我都不会做成公开服务,安全性也有所提升。

容器化会导致性能下降吗

容器化不同于虚拟机,虚拟机是完全隔离的,容器是共享内核的,所以性能上会有所下降,但是这个下降不会很大,我做过粗略的测试,性能下降在 5% 左右,我认为这个下降幅度是可以接受的。

有状态服务的容器化

首先,尽可能转成无状态服务,其次状态尽可能存在用户端、数据库、或者共享存储上。能不用有状态服务就不用有状态服务。 比如 Session,如果之前是存储在服务器磁盘上的话,可以考虑存储在浏览器端,或者存储在 Redis 上等,这样对应的服务就可以无状态化了。 还有一些业务,比如要生成图片,之前可能是生成后存储在服务器上,现在可以考虑存储在 NAS 上,或者存储在阿里云 OSS 上,数据库中只存文件的 URL 地址。

哪些服务不适合容器化

数据库服务不适合容器化。虽然相同配置的数据库,使用云数据库比自己搭建的数据库要便宜,但是云数据库无需考虑太多运维问题, 无需专职的数据库运维工程师,多付的钱其实就是运维成本。

什么时候考虑使用 K8S

天时、地利、人和

  • 日 PV(去除静态资源访问)在 300 万以上;
  • 业务高峰期扩容不及时,经常需要救火,客户经常抱怨,内部人员也经常抱怨;
  • 有专职的运维工程师,或者有人学习过 K8S;
  • 业务在持续上升期;
  • 领导全力支持。

总结

没必要一上来就上 K8S,先把业务做好,再考虑容器化,不要因为容器化而容器化,容器化只是为了解决问题。

容器化是趋势,K8S 是容器化集群管理的事实标准,如果你的业务适合容器化,那么 K8S 是你的选择。

原文地址:http://note.upgradecoder.com/2023/04.html

我粗略的看了一下,说的不对请见凉。用阿里云就是为了省运维岗,引入 k8s,又把这个优势给冲掉了。数据库永远是瓶颈,还是得分库分表。

amonlei 回复

不对,用阿里云是为了省运维,当服务规模大了以后,运维成本又高了,用 k8s 再一次省运维。

巧啊,场景和技术栈跟我们公司很像,都是 SaaS 系统,也是后端 Rails+MySQL,前端 jQuery+Bootstrap,并且也部署在阿里云青岛区。报表我们也准备用 ClickHouse 的,最终还是用了 StarRocks,写入并发过高还是有问题,复杂的 join 查询效果也不理想。我们系统项目代码快 40 万行了,目前还是传统 cap 部署,之前瓶颈一直都在数据库,分库分表后就稳定了。应用流量比较稳定,工作日均请求 4000w+,CPU 使用率经常保持在 50%~70%,没有明显的流量激增,也没怎么搞过突发扩容。现在有新的团队,准备搞容器化了。

lifuzho 回复

ClickHouse 使用成本偏高,不过由于业务已经深度使用了,也没必要换了。StarRocks/Doris 使用门槛比较低,如果有了对应的 ActiveRecord 的 Adapter,使用起来会更流畅。

哈哈,和我们也有点像。😀 DB 也是可以自动扩容的,PolarDB 是否了解,也支持伸缩。 K8S 还有一个好处,可以快速创建测试环境,如果不需要了也可以直接移除。

amonlei 回复

什么时候考虑分库分表的,分了之后业务复杂度得高很多?

原文地址无法访问。

wikimo 回复

数据库性能满足不了要求或者业务彼此之间要解耦合的时候就要考虑了

pengpeng 回复

那是无比成功时候面临的问题了,人工不成问题的时候

需要 登录 后方可回复, 如果你还没有账号请 注册新账号