共计 3546 个字符,预计需要花费 9 分钟才能阅读完成。
SegmentFault[1] 是一家综合性技术社区,因为它的内容跟编程技术严密相干,因而访问量的稳定也和这一群体的作息时间深度绑定。通常状况下 web 页面的申请量峰值在 800 QPS 左右,但咱们还做了前后端拆散,所以 API 网关的峰值 QPS 是申请量峰值的好几倍。
SegmentFault 架构演变
SegmentFault 作为一家技术社区,它的零碎架构演变过程是很有意思的。
- 2012 年,我还在北京的一家公司工作,我在出租屋里写下 SegmentFault 的第一行代码时,基本没有想到它将来会成为我的事业。过后我的想法很简略,就是想帮忙中文开发者用母语在像 Stack Overflow 这样的网站上发问,因而它的第一个版本十分简陋,思考到它的访问量很少,以及我本人的经济能力有余,于是将它放在了国外的 VPS 托管商 Linode 上,所有的利用、数据库、缓存都挤在一个实例上。
- 2013-2014 年,我独自进去守业,SegmentFault 的业务也逐步步入正轨。咱们抉择了本人购买服务器去机房托管,当然服务器也是从淘宝上购买的二手服务器,呈现问题的时候,咱们的团队在当地又去不了机房,只能等管理员去机房帮咱们解决。在 2014 年的时候,SegmentFault 网站被 DDos 攻打,机房为了不牵累其余服务器,间接把咱们的网线拔掉了。
- 2014-2019 年,国内云计算技术从起步到逐步成熟,于是咱们把整个网站从物理服务器迁徙到了云服务上。当然应用上并没有什么不同,只是把物理机器替换成了虚拟主机。
- 2020 年至今,随着云原生理念的衰亡,咱们的业务模式也产生了很大变动,为了让零碎架构适应这些变动,咱们把网站的次要业务都迁徙到了 KubeSphere 上。
为什么抉择 Kubernetes?
SegmentFault 的零碎架构倒退遇到不少挑战,促使咱们不得不往 K8s 架构上迁徙。
- 首先,尽管咱们是一家小公司,但业务线非常复杂。思否整个公司在 30 人左右,技术人员大概只占其中三分之一,所以承载这么大的业务量,累赘还是很重的。而且守业公司的业务线调整十分频繁,临时性工作也比拟多,传统的零碎架构,在应答这种伸缩性要求比拟高的场景是比拟吃力的。
- 其次,简单的场景引发了简单的配置管理,不同的业务要用到不同的服务、不同的版本,即应用自动化脚本,效率也不高。
- 另外,咱们的工作人员有余,所以没有专职运维,当初 OPS 的工作是由后端开发人员轮值的。但后端开发人员还有本人本职工作要做,所以咱们期待最现实的场景是能把运维工作全副自动化。
- 最初也是最重要的一点,就是咱们要管制老本,这是高情商的说法,低情商就是一个字“穷”。当然,如果资金短缺,以上的问题都不是问题,然而对于守业公司(特地是像咱们这种访问量比拟大,然而又不像电商、金融等收益好的公司)来说,咱们必将且长期处于这个阶段。因而是否管制好老本,是一个十分重要的问题。
前后端拆散
2020 年以前,SegmentFault 的网站还是十分传统的后端渲染页面的办法,所以服务端的架构也非常简单。服务端将浏览器的 http 申请转发到后端的 php 服务,php 服务渲染好页面后再返回给浏览器。这种架构用原有的部署办法还能撑持,也就是在各个实例上部署 php 服务,再加一层负载平衡就根本满足需要了。
然而随着业务的继续倒退,后端渲染的形式曾经不适宜咱们的我的项目规模了,因而咱们在 2020 年做了架构调整,筹备将前后端拆散。前后端拆散的技术特点我在这里就不赘述了,这里次要讲它给咱们带来了哪些零碎架构上的挑战。一个是入口增多,因为前后端拆散不仅波及到客户端渲染 (CSR),还波及到服务端渲染 (SSR),所以响应申请的服务就从繁多的服务变成了两类服务,一类是基于 node.js 的 react server 服务(用来做服务端渲染),另一类是 基于 php 写的 API 服务(用来给客户端渲染提供数据)。而服务端渲染自身还要调用 API,而咱们为了优化服务端渲染的连贯和申请响应速度,还专门启用了应用专有通信协定的外部 API 服务。
所以实际上咱们的 WEB SERVER 有三类服务,每种服务的环境各不相同,所需的资源不同、协定不同,各自之间可能还有相互连接的关系,还须要负载平衡来保障高可用。在疾速迭代的开发节奏下,应用传统的零碎架构很难再去适应这样的构造。
咱们迫切需要一种可能疾速利用、不便部署各种异构服务的成熟解决方案。
Kubernetes 带来了什么?
开箱即用
首先是开箱即用,实践上来说,这应该是 KubeSphere 的长处,咱们间接点一点鼠标就能够打造一个高可用的 K8s 集群。这一点对咱们这种没有专职运维的中小团队来说很重要。依据我的亲身经历,要从 0 到 1 搭建一个高可用的 K8s 集群,还是有门槛的,没有接触过这方面的运维人员,一时半会搞不定,其中的坑也十分多。
如果云厂商能提供这种服务是最好的,咱们不必在服务搭建与系统优化上破费太多工夫,能够把更多的精力放到业务下来。之前咱们还本人搭建数据库、缓存、搜寻集群,起初全副都应用云服务。这也让咱们的观点有所转变,云时代的根底服务,应该把它视为基础设施的一部分加以利用。
用代码治理部署
如果能把运维工作全副用代码来治理,那就再现实不过了。目前 K8s 的确给咱们提供了这样一个能力,当初咱们每个我的项目都有一个 Docker 目录,外面搁置了不同环境下的 Dockerfile、K8s 配置文件等。不同的我的项目、不同的环境、不同的部署,所有都能够在代码中形容进去加以治理。
比方咱们之前提到同样的 API 服务,应用两种协定,变成了两个服务。在当初的架构下,就能够实现后端代码一次书写、离开部署。其实这些文件就代替了很多部署操作,咱们须要做的只是定义好当前执行命令把它们推送到集群。
而一旦将这些运维工作代码化当前,咱们就能够利用现有的代码管理工具,像写代码一样来调整线上服务。更要害的一点是,代码化之后无形中又减少了版本治理性能,这离咱们现实中的全自动化运维又更近了一步。
继续集成,疾速迭代
继续集成标准化了代码公布流程,如果可能将继续集成和 K8s 的部署能力联合起来,无疑能大大放慢我的项目迭代速度。而在应用 K8s 之前,咱们就始终用 GitLab 作为版本管理工具,它的继续集成性能对咱们来说也比拟实用。在做了一些脚本革新之后,咱们发现它也能很好地服务于现有的 K8s 架构,所以也没有应用 K8s 上诸如 Jenkins 这样的服务来做继续集成。
步骤其实也很简略,做好平安配置就没什么问题。咱们本地跑完单元测试之后,会主动上线到本地的测试环境。在代码合并到上线分支后,由管理员点击确认进行上线步骤。而后在本地 build 一个镜像推送到镜像服务器,告诉 K8s 集群去拉取这个镜像执行上线,最初执行一个脚本来查看上线后果。整个流程都是可视化可追踪的,而且在代码治理界面就能够实现,不便开发者查看上线进度。
总结经验
治理好根底镜像
目前咱们用一个专门的仓库来治理这些根底镜像,这能够使开发人员领有与线上统一的开发环境,而且后续的版本升级也能够在根底镜像中对立实现。
除了将 Dockerfile 文件对立治理以外,咱们还将镜像 build 服务与继续集成联合起来。每个 Dockerfile 文件都有一个所属的 VERSION 文件,每次批改外面的版本号并提交,零碎都会主动 build 一个相应的镜像并推送到仓库。根底镜像的管理工作齐全自动化了,大大减少了人为操作带来的谬误与凌乱。
KubeSphere 应用教训
- 不要把日志服务放到集群里。这一点在 KubeSphere 文档中就有提及。具体到日志服务,次要就是一个 Elastic 搜寻服务,自建一个 Elastic 集群即可。因为日志服务自身负载比拟大,而且对硬盘的持续性需要高,如果你发现日志服务自身就占据了集群里相当大的资源,就得失相当了。
- 如果生产环境要保障高可用,还是要部署 3 个或以上的节点。从咱们应用的教训来看,主节点偶然会呈现问题。特地是遇到节点机器要保护或者降级的时候,多个主节点能够保障业务的失常运行。
- 如果你自身不是专门提供数据库或缓存的服务商,这类高可用服务就不要上 K8s,因为要保障这类服务高可用自身就要消耗你大量的精力。我倡议还是尽量用云厂商的服务。
- 正本的规模和集群的规模要匹配。如果你的容器只有几个节点,但一个服务外面扩大了上百个正本,零碎的调度会过于频繁从而把资源耗尽。所以这两者要相匹配,在零碎设计的时候就要思考到。
最初一点感想:当做完容器化后,会发现利用在集群里运行的时候并不需要占用那么多台服务器。这是因为升高了资源的粒度,所以能够做更多的精细化布局,因而应用效率也进步了。
脚注
[1] SegmentFault: https://segmentfault.com/
作者
祈宁 SegmentFault CTO & 联结创始人