作者:defooli 腾讯CSIG工程师
前言
在后台服务体系中,基础设施是运行在业务逻辑之下的计算、网络、存储资源以及通用的基础服务。如果没有完善的基础设施,业务团队只能以"小作坊"形式运作,具有较弱的服务治理能力,产生效率较低,大部分时候只是为了满足业务短期需求,如果出问题了再安排人力来优化,但是并不能很好收敛架构不完善带来的效率和质量问题,特别对于ToB的场景,质量和口碑犹其重要,不应该有持续的服务质量问题。针对如何实现一套完整的基础设施及其应具备的能力,下面做了一些思考和分析。
基础构架的设计思考
总的来说,业务所需要的基础设施能力就两个:高效开发测试和高质量的运营。具体有:
如上图所示,开发和测试同学可以基于账号信息路由到自己的版本,发布变更时可以控制新版本的流量比例(基于账号hash或其他维度)
基于以上考虑,设计了下面的构架图:
在上图中的各模块的功能是:
相关问题
1.为什么用sidecar而不是直连方式,或者是都走gateway网关?
对于直连方式,sidecar提供了与业务逻辑无关的框架能力,更好地实现基础架构运营维护。相对gateway方式,sidecar即可以解决gateway网关的中心化依赖问题,在性能上,本地sidecar增加的延时一般不超过1毫秒,而gateway在不同机房里至少增加几毫秒的延时。
2.为什么服务端不用再经过一次sidecar?
上面的架构中,所有服务入口流量都会经过一次gateway或sidecar,已基本实现完整的构架能力,唯一不足是不能在服务端实现过载保护能力,但是实践过程中,这个功能基本用不上。增加一跳也会带来不稳定性和性能损耗。
3.为什么不直接使用k8s的名字服务以及DNS能力,或者直接使用etcd/zk中间件作为名字服务?
基于DNS的名字服务有性能稳定性不高,不确定的ttl以及排障困难问题,基于k8s service无法实现客户端的节点级熔断能力,不能针对单节点人工屏蔽,不支持set、不支持降级等。
4.为什么不用流量劫持的透明代理能力
透明代理能力的好处是存量的服务被调地址不可改也可以迁移到sidecar体系中,但是即使没这个能力,改造成本也不高,只要在框架层修改服务地址指向本地就行,另外Sidecar劫持会带来较大的负作用,比如排查问题困难和性能损耗。
5.四层流量(如mysql/redis、私有协议等)如何管理?
可在Sidecar中配置L4层代理,基于端口号做4层转发。
基于云原生的实现思路
在2021年的今天,已经有大量的开源工具和框架去解决通用的业务问题,云原生技术日渐成熟,我们也有另一个思路去考虑基础设施的架构设计问题了。先看下使用云原生技术带来的好处:
但是也要面对这些问题:
针对上面两个问题,需要在选型时选择合适的开源项目,充分了解开源项目的功能与业务需求,考虑开源项目的插件化扩展能力、代码质量、文档齐全和社区的活跃度及生态完善程度。
基于上面对基础设施的思考,下面对云原生实现的选型做了分析。
名字服务
名字服务是基本设施架构中最核心模块,应该具有高性能、高可用、支持Set等能力。Consul在开源的名字服务方案影响力较高,腾讯开源的TSeer(https://github.com/Tencent/TSeer)也有不错的能力,但是文档不太全,社区活跃度比较小。
Gateway与Sidecar
Gateway和Sidecar功能基本一致,应该使用相同的流量转发程序,envoy和linkerd都是不错的选择,envoy的扩展能力和活跃度好一些,可以基于envoy做数据面的转发。这里的问题是envoy需要把所有被调服务的配置都提前下发,如果在sidecar中使用,会有大量无效配置下发,在服务数量多时,会影响系统的稳定性,这也是istio中的问题。解决方案是先通过gateway冷启动(sidecar默认转发到gateway),再异步触发配置下发到相应的sidecar。
容器管理与资源调度
容器方案与虚拟机相比,主要解决了两个问题:
在容器编排中,k8s已经成为业界标准,不过k8s更多是解决资源编排和网络问题,没有服务治 理能力,基于k8s的knative、kubevela对k8s作了一层封装,某些场景下更符合开发团队的使用需求。
K8S的网络模型是开放的,目前也有非常多的实现,考虑性能和问题定位效率,不建议使用overlay类型的实现。calico的bgp模式可以直接使用原有数据中心的网络结构,如果使用云上的k8s,一般厂商都会实现类似的网络方案,容器和vm使用同一个网络平面。
考虑容灾容错,上层服务应该可以部署在多个k8s集群中,在单集群任何类型的故障发生时,也还可以正常对外提供服务。
基础公共服务
以下基础公共服务在大多业务场景是需要用到的,但是跟具体的业务逻辑关联不大,使用公共服务方式可以较好提高业务开发效率和架构能力。
配置管理
支持权限管理、富文本编辑器、SDK、版本管理和灰度功能。携程开源的apollo相对比较成熟。
日志
日志功能分为三部分,日志采集上报、日志存储和日志展示。ELK是最成熟的方案。
监控告警
监控功能分为三部分,其中监控上报有推和拉两种模式,拉的方式上报更简单(不用上报),监控存储最好使用时序数据库,监控规则配置除了按数量或百分比之后,应该还有同比能力。
云原生的Prometheus具有不错的性能和开源生态,强大的查询语法能满足业务常见的监控配置需求。
调用链
调用链一般集成到框架中,主要用于排查超时问题和了解调用拓扑图。Jaeger和Zipkin都是非常成熟的实现方案。
其他可能用到的公共服务
测试框架:具有api测试、压测、拨测能力。
定时任务管理:因为执行不是主动触发的,异步任务是比较容易出问题的,比如在计费、定时重置等业务逻辑中,定时任务管理系统可以针对异步任务的场景默认有重试和监控告警功能。
分布式事务管理:支持事务ID生成和任务流程编排,在某个子任务失败时支持重试和回滚。
分布式频控:支持精确和高性能的频控模式。
可视化管理:支持服务的部署、分组显示和下线,显示节点列表和状态,可临时屏蔽节点流量,显示服务的监控、日志、调用链及拓扑图。
(以上暂无发现有无成熟的云原生实现)
RPC的选型思考
虽然基础架构支持多种开发框架,但是统一技术栈可以减少沟通成本和长期运营成本,在RPC选型上,目前grpc和thrift是最有影响力的两种rpc实现,两者性能差异不大,grpc的文档和生态相对好一些。考虑前后端的高效联调和交互,可以通过proto协议文件生成typescript代码,前端直接通过gateway调用后端的rpc接口,而后端可以是cpp/go/java等不同语言实现的服务端。
CI/CD的实践
CI: 将代码集成的代码库时,可以增加代码规范检查、静态代码检查、单元测试增量代码覆盖率检查、接口自动化测试(墨盒)等,只有以上步骤都通过才可以合并代码。
CD:将代码转线上运行的版本,需要有编译、版本管理、灰度发布、回滚等功能。
基础设施的维护方式
基础设施建设的最终目标是解决产品的开发运营效率和服务质量问题,从业务团队的角度上,肯定是希望与业务逻辑无关的功能由基础设施来提供,只需专注于业务逻辑的实现,而不是花时间精力了解、接入各类平台或重复造轮子。从基础设施提供方来说,不应被动地接受业务方提出来各类需求,而是对业务模型有充分了解,明确业务边界,做好产品化能力,既提供稳定可靠的公共服务来满足业务通用需求,也可以对业务个性化需求做插件化开发和独立部署。
构架扩展能力
随着业务规模的增长,对基础架构也会带来新的挑战,一般会遇到的问题:
上面提出一个解决思路:每个业务(如限定50-100的开发人员规模或10w核资源之类)可以使用一套独立的基础设施,基础服务版本可以独立升级,gateway使用的公共网段跟业务服务使用不同的网段,所有业务都可以访问公共网段,这样做既解决了业务间隔离的问题,也可以做到业务间足够的连通性。不同业务可以使用不同的vpc,对于跨业务访问的请求,流量转到sidecar后,先获取另一个业务注册的gateway地址,然后再能过另一个业务的gateway来访问不同业务的服务。
总结
完善的基础设施建设可以为开发和运营提高效率,较好地保障业务的服务质量以及产品口碑。以名字服务/gateway/sidecar为核心的构架是一种比较优雅实现思路,而基于云原生的基础设施相比于完全自研的方式有很多好处,在节省人力的同时也方便长期运营,上文针对云原生选型问题也做了一些总结。基础设施的维护方式要从业务需求出发,最终目标能帮助业务解决非业务逻辑相关的问题,最后针对业务规模增长的问题,提出了使用公共网段部署gateway、多业务独立部署基础服务的解决方案。
近期热文
让我知道你在看