首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >云原生通用 Serverless 架构在搜推广数据密集型应用中的设计与实践

云原生通用 Serverless 架构在搜推广数据密集型应用中的设计与实践

原创
作者头像
睿宇
修改2025-12-11 13:38:03
修改2025-12-11 13:38:03
1.5K0
举报

前言:

此方案之前是为了提升京东搜推广研效而设计,最终除了通过 BaaS 底座通用能力复用提升了研发效率,和 FaaS 更细粒度二层调度编排提升了资源使用率之外,也通过存算解耦实现了多分片应用更新从小时级到秒级的跨越,大幅提升了发布效率。该方案基于原生 K8s 控制面能力和类边缘计算的 Node 模式(自研轻量 Kubelet 代理及自定义容器运行时),实现了工作负载环境无关性,支持混合部署共享集群中单租户的自定义调度和编排;另外 Serverless BaaS 底座采用 C++ 实现,兼具高性能和语言无关性,可支持数据密集型和计算密集型及各类混合负载 FaaS 场景。后来借助调度框架和底座的通用性,通过扩展 K8s 调度器和控制器支持负载感知及热点迁移等能力,并配合 Shared Prefix KVCache 和 RDMA 等技术,也实现了降低 LLM TTFT 和 TBT 的 PD 分离智能调度能力。相关技术曾在 QCon、GOTC 等大会分享过,此次承蒙腾讯云架构师同盟邀约,整理万字成文,系统总结了该方案的设计思路与实践经验,希望能为大家的云原生架构设计与落地实践带来启发。

1 云原生溯源与架构选型背景

为清晰阐述京东搜推广场景 Serverless 架构的设计初衷,本节首先回顾云原生的演进与实践理念,再结合业务痛点与技术瓶颈,详细讲解为何选择 Serverless 作为架构升级方向。

1.1 云原生溯源和实践理念综述

云原生概念的起源可追溯至 2013 年,由敏捷开发领域技术领导者 Pivotal 公司的技术经理 Matt Stine 首次提出 “CloudNative(云原生)” 概念。2013 年至 2019 年期间,其定义持续演进,Pivotal 官方最终将云原生概括为四大要点:DevOps、持续交付、微服务与容器。2015 年云原生计算基金会(CNCF)成立,最初将云原生定义为三大特征,容器化封装、面向微服务架构、支持容器编排调度;2018 年,CNCF 进一步将服务网格(Service Mesh)与声明式 API 纳入定义范畴。

可见,云原生的定义并非一成不变,不同组织及同一组织在不同阶段,对其内涵的界定均存在差异。随着 AI 技术的快速发展,云原生在坚守 “适配云环境、弹性扩展、自动化运维” 等特质的基础上,进一步延伸出对 GPU 集群调度、高显存支持等 AI 工作负载的适配能力,涵盖 AI 专属治理、AI 辅助部署等关键功能,未来或将持续向 “AI Native” 方向迭代演进。本质上,云原生是面向云环境构建与运行应用的技术体系及方法论。“CloudNative” 作为组合词,“Cloud” 指向应用部署于云环境而非传统数据中心,“Native” 则强调应用从设计之初即原生适配云架构,以最优形态在云上运行,最大化发挥云平台的弹性与分布式优势。

在实践层面,云原生架构的落地可参考相关要素与设计理念。其中,Matt Stine 提出的 云原生 15 要素、Kevin Hoffman 倡导的相关设计理念,以及社区沉淀的实践准则均具参考价值。这些要素与理念中,部分落地门槛较低,部分则需结合业务场景进行深度适配。理想情况下,云原生应用应尽可能贴合这些要求,以充分释放云平台的技术价值,更好发挥云的优势。

1.2 从云原生 OS 中看声明式 API 和 CRD

在上述云原生特性中,容器、微服务、服务网格、不可变基础设施等均是行业内实践改造的重点方向,而声明式 API 往往以 “使用为主”,深度改造的场景相对较少。声明式本质是一种设计理念与工作模式,其传递的是 “把便利留给使用者,把复杂度留给实现者” 的设计哲学,其设计与实现难度远高于命令式模式。

这一结论的逻辑在于,命令式 API 的痛点在于错误处理机制的局限性。大规模分布式系统中故障不可避免,若发出的命令未得到响应,调用方往往只能通过盲目重试尝试恢复,而无差别重试可能引发级联故障;同时,命令式 API 处理高并发访问时,还需额外处理幂等性、请求顺序等复杂问题。相比之下,声明式 API 系统会天然持久化系统的当前状态与期望状态,无需调用方关注执行细节。简言之,命令式侧重 “如何做(How)”,声明式侧重 “要什么(What)”,后者更易于组合复用,且能显著提升代码的可读性与可维护性。

进一步来看,若将 K8s 视为云原生时代的操作系统内核,那声明式 API 则等价于该内核的系统调用接口。用户自定义资源(CRD,Custom Resource Definition)则是扩展这一系统调用能力的工具。借助 CRD,可灵活新增与 K8s 内置资源平级的自定义资源,且该能力可延伸至各类基础设施资源的管理。本文后续将探讨的自定义灰度发布、多集群管理及数据密集型应用多分片管理等能力,均以 CRD 为实现载体。

1.3 云原生堆栈——分层架构解析与全景技术图谱概览

延续前文将 K8s 类比为云原生时代操作系统的思路,云原生全景图可进一步类比为操作系统的技术堆栈——通过层层递进的技术层,实现云原生应用的构建、管理与运行。最底层为供应层(Provisioning),提供构建安全云原生基础设施的工具集,包括自动化部署工具、容器仓库、安全合规框架、密钥管理系统等。往上一层为运行时层(Runtime),作用是为容器化应用组件提供运行环境与通信支撑,关键技术涵盖云原生存储、容器运行时、云原生网络等。再上一层为编排与管理层(Orchestration & Management),涵盖业界主流的编排调度、服务协调与发现、RPC 框架、服务代理、API 网关及 Service Mesh 等能力。最顶层为应用定义与开发层(App Definition & Development),提供应用程序定义与开发的全流程工具链,包括数据库、流处理与消息队列、应用定义与镜像构建以及持续集成和持续交付等。

除此之外,另有两个垂直贯穿上述四层的能力层,分别是平台层与观察分析层,其价值是将不同技术层的工具与能力整合为一体化解决方案。例如,平台层包含 K8s 发行版、托管服务、容器化部署工具等;观察分析层则涵盖监控、日志、追踪、混沌工程及持续优化等可观测性技术。

值得注意的是,在 CNCF Landscape 定义的技术范畴之外,另有两大技术方向与本文分享高度相关,其一为云原生发展高级阶段的形态也是近年备受关注的 Serverless;其二为一度被视为云计算下一波浪潮的 WASM(WebAssembly)。

1.4 Service Mesh——平衡微服务能力与分布式压力

在阐述为何选择 Serverless 之前,首先回顾下前序关联的 Service Mesh 的建设背景与实践价值。微服务是分散能力的,解决系统复杂度问题,是逻辑垂直拆分;而分布式分散压力,解决系统性能问题,是物理横向拆分。二者相辅相成、密不可分。

以搜推广为代表的应用,需应对 “千人千面” 的业务场景与日益复杂的需求,既要求高效的研发迭代能力,也追求极致的系统性能。但在实践过程中,系统面临多重挑战,一方面,缺乏灵活的高级流量控制能力与完备的跨语言适配能力,需进一步升级以应对大促期间的突发流量冲击,同时满足跨部门业务联动的发展需求;另一方面,搜推广各子业务虽基于同一套 RPC 框架开发,但基于差异化需求独立迭代后,相互兼容性面临挑战,导致分布式场景下的共性问题难以聚焦解决与能力复用;此外,C++ 在京东集团技术体系中仅搜推广场景广泛应用,作为相对小众的开发语言,其服务治理相关支持较为薄弱。

Service Mesh 天然具备语言无关、高级流控、应用透明、独立迭代等特性,恰好能够针对性解决上述问题。因此过去三年间带领团队完成了 Service Mesh 在搜推广核心链路的落地,通过去中心化网关、“加权本地耗时感知 + 远端负载感知” 复合策略负载均衡、自适应熔断与限流等关键技术优化,显著提升了系统吞吐能力与可用率,一定程度降低了分布式系统的复杂性,实现了微服务解耦与分布式压力分担的动态平衡。但即便如此,系统仍存在部分 Service Mesh 无法覆盖的问题,这也成为后续引入 Serverless 架构的重要契机。

1.5 业务开发和运维面临的伸缩性、灵活性与弹性问题

这些问题集中体现为伸缩性(Scalable)、灵活性(Flexible)、弹性(Resilient)三大维度的挑战:

  • 伸缩性维度:受业务特性影响,当前资源规格偏大且整体资源紧张,从机器就位到业务上线流程冗长,同时模块间、逻辑集群间资源无法跨域调配,资源利用率偏低;加之数据与应用部署耦合,导致系统伸缩、扩展能力受限。期望通过拆分服务粒度、解耦数据与应用部署,提升资源调度效率与系统伸缩性。
  • 灵活性维度:当前存在物理机与容器混合部署的场景,基础框架能力无法对齐复用;发布策略单一且缺乏无感灵活机制,仅能在低峰期采用固定策略进行垂直伸缩,难以快速响应突发流量。期望实现多资源类型(物理机、虚拟机、容器)的统一调度,支持自定义灵活拓扑与自适应伸缩,提升系统对业务变化的响应效率。
  • 弹性维度:系统级异常(如网络故障)发生时,需依赖运维人员手动摘除异常实例;资源充足场景下仍需人工介入扩容并手动调度分组,系统自主恢复能力不足。期望基于 K8s 声明式 API 的终态管理能力,实现异常实例的自动检测、摘除,以及资源池支撑下的实例自动拉起与调度,强化系统弹性自愈能力。

面对上述多维度痛点,如何构建适配业务特性的解决方案呢?

1.6 Serverless——云原生时代应用的“高级语言”

对,Serverless,回顾云计算的演进历程,容器技术的普及实现了云上应用的标准化与规模化部署,让业务在资源利用率提升、高可用保障等层面充分享受云原生红利。但在研发模式上,业务开发仍延续传统大型分布式系统的构建思路,未能充分释放云计算的技术红利。

Serverless 恰好针对性解决了这一问题。作为继 K8s 容器化、Service Mesh 透明化通信与治理之后的 “云原生第三驾马车”,Serverless 堪称云时代应用开发的 “高级语言”,真正践行了 “Cloud as a Computer” 的理念。开发者仅需聚焦业务逻辑开发,无需关注大型分布式系统的底层复杂性,同时进一步细化并标准化了微服务及分布式系统在伸缩性、灵活性与弹性方面的能力。

从技术构成来看,Serverless = BaaS(Backend as a Service)+ FaaS(Function as a Service):

  • BaaS 底座:统一承载并沉淀业务共性能力(如 AB 实验、配置管理)与通用基础能力(如服务发现、注册中心),实现能力的复用。开发者可聚焦业务逻辑,无需关注分布式系统底层细节,显著缩短业务研发周期。
  • FaaS 框架:以轻量函数为设计理念,一方面有效拆分服务粒度,更小的业务单元可更好适配云平台的调度编排能力;另一方面实现业务逻辑解耦,提升并发开发效率与系统扩展性。

2 技术挑战及应对策略

Serverless 架构在适配搜推广数据密集型场景的落地过程中并非一帆风顺,需直面业务特性与技术架构适配的多维度挑战。本节将聚焦 Serverless 实践中的关键技术难点展开拆解,并对应阐述针对性的应对策略,为同类场景的落地提供可复用的实践思路。

2.1 搜推广业务的应用特点及落地挑战

如上一章节所述,业务在开发与运维层面的痛点,与搜推广场景的特性密切相关,该类应用具备四大典型特征:数据体量庞大、计算密集、业务链路复杂、实时响应要求高。这些特性叠加现有技术环境,为 Serverless 架构的落地带来多重挑战。

  1. 单分片规格偏大,伸缩灵活性受限:电商平台商品规模已达数十亿级,虽可通过数据分片缓解存储与计算压力,但受业务准确性要求限制,分片数量无法过度拆分,导致单分片规格偏大;且分片部署与集群物理拓扑强绑定,进一步制约了系统的伸缩与扩展能力。
  2. K8s 控制面权限不足,调度编排受限:现有 K8s 架构采用 “多业务共用 Master 控制面、Namespace 隔离” 的部署模式,受权限管控限制,无法获取完整控制面权限,难以支撑 Serverless 所需的灵活调度与编排能力。
  3. 混合部署环境复杂,统一调度难度高:业务集群中因历史原因仍保留大量物理机,形成 “物理机 + 容器” 的混合部署形态,且存在多租户 Namespace、云上云下混合环境等场景,导致资源无法实现跨环境、跨租户的灵活统一调度。
  4. 大规模部署下性能敏感,额外损耗不可忽视:终端用户的单次请求,在个性化多路召回、多分片检索等流程下,后端需联动数十个服务协同处理。当前集群规模已达 10W + 实例、百万核级别,额外组件的资源开销与链路多跳带来的性能损耗,对系统整体性能构成显著影响,因此架构的性能也尤为关键。

2.2 业务集群外独立 K8s 集群解锁控制面完整能力

现有架构中业务以多租户模式共享 K8s 集群,通过 Namespace 实现隔离,既无法整合为单一物理集群,也没有类似 KubeZoo 等的方案。受限于权限管控,难以基于 CRD 开展自定义调度与编排,成为 Serverless 落地的关键阻碍。

针对 “多业务共享 K8s 集群、控制面权限受限” 问题,最初曾考虑在 Serverless BaaS 底座中自主实现调度编排能力,但 K8s 本身已是业界成熟的调度编排框架,重复造轮子会增加研发成本与维护复杂度,因此最终放弃该思路。经过多轮方案论证,设计了 “容器化 Node 重组新 K8s 集群” 的架构(如左侧示意图所示),也就是 “Docker (Containerd) in Docker + Kubernetes Node in Container”,即把原有私有云多租户环境中的容器,重新封装为新 K8s 集群的 Node 节点。

具体做法是在私有云与同地域公有云中选取 VM 节点,通过 Keepalived、Haproxy 构建高可用负载均衡层,并搭配专有 etcd 集群,共同搭建独立的高可用 K8s Master 集群。为保障数据传输的安全性与网络性能,公有云与私有云机房之间已部署专线互联。K8s 控制面采用标准配置,此处不做赘述,下文将重点阐述 Node 节点组件设计。

2.3 轻量 Kubelet 代理与容器运行时优化(Containerd+Shim V2)

首先,我们来看轻量化的 Kubelet 代理设计。Kubelet 是连接 Kubernetes 控制面与工作节点的核心组件,主要职责是持续监听 Pod 规范、确保 Pod 在节点上按预期运行,并实时上报节点状态。在容器环境中部署 Kubelet,首要解决的是其对底层系统权限与依赖的适配问题。由于容器内无法调用 Systemd 服务,需将 Kubelet 的 Cgroup Driver 强制调整为 Cgroupfs;另外通过 Kubeadm 将容器注册为 Node 时,可省略与本场景无关的系统校验及冗余配置,避免无效资源消耗。再者考虑到作为 Node 的容器本身资源有限,如左图所示,我们对 Kubelet 进行模块化精简,仅保留 Pod 生命周期管理、容器状态维护、Node 信息上报等核心能力,将 Cloud Provider、Secret、Volume 等非必需 Manager 组件移除,确保 Kubelet 在受限资源下实现轻量化运行。

其次,从容器管理视角来看,Kubelet 的核心依赖组件为 KubeletGenericRuntimeManager。该组件通过调用业界容器三大开放接口之一的容器运行时接口( CRI,Container Runtime Interface ),与底层容器运行时完成交互,承担容器创建 / 销毁、镜像管理、Pod Sandbox 生命周期管控等关键操作。因此,清晰理解 CRI 的演进脉络,对后续在容器内部构建自定义运行时具有关键指导意义。

纯 Docker 早期及高版本的演进细节此处不再赘述,聚焦 Containerd 及 Shim V2 的技术由来。如右图所示,自 Docker 1.11 版本起,容器启动流程被拆分为 “Containerd → Containerd-Shim → Runc” 的架构,有效避免因 Docker Daemon 故障导致所有容器整体退出的风险。Kubernetes 在 1.24 版本中移除内置 Dockershim,转而通过独立的 CRI-Docker 维持兼容性;与之对应,Containerd 1.0 版本通过 CRI-Containerd 适配 CRI 接口,1.1 版本进一步将 CRI 逻辑内置以简化调用链路,而 1.2 版本推出的 Shim V2 接口规范更是实现了关键突破,它将每个 Pod 的运行逻辑封装为独立 Shim 进程,同时支持通过标准化方式扩展自定义运行时,为我们后续实现自定义 Runtime 提供了技术基础。

2.4 自定义 Containerd Shim V2 实现与 CNI 网络适配

为支撑 “以容器作为 Node” 的架构,并规避容器嵌套带来的性能损耗,我们基于 Containerd 1.2 的 Shim V2 标准接口,自主研发了 Containerd-Shim-Redstone-V2 组件,同时完成了容器网络接口(Container Network Interface,CNI )的场景化适配,为新架构的网络连通性与运行效率提供保障。

从 Shim V2 的实现逻辑来看,主要优化点是 “避免容器嵌套运行”,也就是并未在宿主容器内再启动子容器承载业务,而是通过 Shim V2 的 Start 接口,将容器 RootFS 中的业务文件直接挂载至作为 Node 的宿主容器环境,随后调用自主研发的 BaaS 底座加载业务动态库。这一模式类似 “航空母舰搭载舰载机”,作为 Node 的宿主容器是 “航母”,其内置的 ServiceHost(BaaS 核心组件)是 “甲板与保障系统”,多个业务程序则是 “舰载机”,直接运行于 BaaS 底座之上,既保障了资源复用,又彻底消除了容器嵌套的性能开销(BaaS 底座的详细设计将在后续章节展开)。

此外,容器启动流程中网络连接也是重要环节,在完成文件系统、Namespace 命名空间、Cgroup 资源限制等基础配置后,必须通过 CNI 将容器接入集群网络。CNI 的规范可概括为三点,一是通过配置文件指定网络插件及参数;二是接收 CRI 传递的容器 Namespace、ID 等运行时信息;三是由插件完成网络配置后返回 Pod IP。其主要功能则聚焦于三方面,Pod IP 的分配与管理、同节点 Pod 间的通信、跨节点 Pod 间的通信。针对我们的架构特性,早期尝试的 Flannel、Calico 插件因需支持复杂跨节点二层通信,无法直接适配 “轻量 Node” 场景。考虑到当前架构暂不涉及跨节点二层通信需求,最终采用 “Bridge 为主插件 + 辅助插件组合” 的轻量化方案,以 Bridge 插件实现 Pod 与宿主网络的桥接,Loopback 插件保障 Pod 内部回环通信,Host-Local 插件完成 IP 地址的本地管理,同时通过 Portmap、Firewall Tuning 等 Meta 插件实现端口映射与防火墙配置。

至此,通过 Shim V2 组件的性能优化与 CNI 网络的场景化适配,我们已完整实现 “将容器作为 Node、重组 K8s 集群” 的架构,为后续 BaaS 与 FaaS 的协同运作奠定了运行基础。

2.5 参考 StatefulSetPlus 实现自定义分批灰度能力

在前述外置 K8s 控制面、轻量 Node 代理、自定义 CRI/CNI 的基础上,我们已完成控制面权限解锁与资源重组,接下来需聚焦业务落地的诉求,适配数据密集型多分片应用的灰度发布能力,这类场景下的灰度发布需具备精细化管控能力,而 K8s 原生控制器无法满足该需求,因此我们基于 CRD 自研分批灰度控制器,参考腾讯 TKE StatefulSetPlus Workload 的设计思路。

腾讯 TKE StatefulSetPlus 作为增强型 StatefulSet Workload,除兼容 StatefulSet 原生特性(稳定唯一的网络标识、持久化存储、有序优雅的部署 / 伸缩 / 删除 / 停止)外,还扩展了原地升级、Node 失联时 Pod 自动漂移等能力,其中最重要的是分批灰度更新能力。基于此,我们实现了支持分批并发灰度更新、Operator 高可用部署的自定义灰度控制器。

主要逻辑是在 K8s OnDelete 更新模式基础上进行扩展,并依赖两类自研 CRD 完成管控。一是 BatchDeployConfig CRD,用于传递 Pod 灰度配置信息,包括总批次数量、当前执行批次、待更新 Pod 列表等;二是 BatchDeployStatus CRD,用于记录并更新各批次 Pod 的发布状态。通过两类 CRD 的协同调度,完成灰度发布全流程的状态流转与集群协调。该方案最终实现了贴合数据密集型分片应用的精细化分批灰度发布能力(如右图所示),比如整体发布流程分为三批执行,各批次分别更新 2 个、3 个、4 个 Pod,且同一批次内的 Pod 更新支持并发执行,既保障了分片应用发布的安全性,又提升了灰度发布的效率。

2.6 基于自研 CRD + Operator 支持多分片与多集群管理

在实现自定义灰度发布能力后,跨南北地域机房的资源配额管控与多集群协同,成为亟待解决的问题。提及多集群管理,业界经典方案是 Kubernetes 集群联邦(Federation),其价值已从基础的 “多集群管控与高可用” 延伸至混合云、多云场景。但 Federation v1 存在致命缺陷,API Server 单点风险显著,且 API 成熟度与 K8s API 不匹配,最终被官方终止维护。后续的 KubeFed(Federation v2)虽做优化,移除了独立 API Server,并通过 CRD 扩展联邦资源,强化跨集群编排能力,但强制区分 Host(主集群)与 Member(成员集群)的架构过于厚重,诸多功能与我们的业务场景冗余。

针对 KubeFed 的设计冗余问题,我们摒弃 “主从集群” 的角色绑定,提出 “所有集群对等” 的轻量化方案,并通过自定义 CRD 构建贴合多分片业务的能力体系。该设计的优势在于无需依赖特定主集群,从架构根源规避单点风险,同时仅保留多集群管理的功能,大幅降低运维复杂度。而且这一模式之前已在微信大规模业务场景中得到落地验证,具备高可靠性。具体架构如右图所示,三个 CRD 各司其职又相互协同,形成完整的多集群管理闭环。

其中,CRD-RedstoneShardGroup 一方面实现多集群统一资源配额管控,另一方面结合业务完成多分片协同管理,最终确保跨地域资源调度的平衡性与多分片运行的协同稳定性;CRD-ClusterDeploy 聚焦单集群应用部署与生命周期管理,实现集群级别的精准管控;CRD-StatefulSetExt 作为 StatefulSet 的增强版本,原生实现底层 Pod 自定义灰度与更新策略。从技术选型来看,扩展 Karmada 并结合 OpenKruise 也可满足基础需求,但为保障架构的高度自主可控性与业务适配灵活性,我们最终选择自研方案,既贴合搜推广场景的资源调度特性,也为后续能力扩展预留空间。

2.7 平台能力通过 OpenAPI 集成到现有 PaaS 平台

在具备多租户集群调度编排、多集群管理和可灰度、多分片管理等能力后,我们选择以 “OpenAPI 为中间层” 的方式将能力输出至现有系统,而非自建独立 UI 平台。这一选择主要基于两点考量,一是延续用户已有的操作习惯,降低使用成本;二是适配业务侧多套发布系统的差异(算法、架构、数据等场景诉求区别较大),通过 OpenAPI 统一输出有机编排、自定义灰度、集群联邦、弹性调度等底层能力,实现对多套上层应用平台的能力增强。

结合右图的架构与流程,具体集成逻辑如下(对应 IaaS - 云平台 - 应用层的层级协同):

  1. IaaS 层池化底座构建:开发者平台通过 OpenAPI 调用,在公 / 私有云环境中创建容器、物理机或 VM 资源;并在这些资源上部署包含轻量 Kubelet、自定义容器运行时,以及 BaaS 底座核心组件(Redstone Agent)的 “K8s Work Node”,同时完成 IP 标记等节点打标操作,将这些资源构建为标准化的池化运行底座。
  2. 云平台层能力调度:开发者平台通过 OpenAPI 向 Redstone Serverless Platform 发起请求,根据业务需求发布 / 更新多分片应用对应的 FaaS 实例。
  3. 应用层实例拉起:池化底座中的 K8s Work Node 通过 Watch 机制感知到 Pod 绑定事件后,触发 Redstone Agent 加载对应的 FaaS 业务程序,完成实例的动态拉起。

通过 OpenAPI 的集成模式,既复用了现有 PaaS 平台的用户入口与操作流程,又实现了 Serverless 底层能力对多业务场景的统一支撑。

3 Serverless 架构设计与实践

前面解决了通用调度编排的瓶颈,接下来聚焦 Serverless BaaS 底座与 FaaS 框架的设计和落地。这两者是支撑搜推广业务场景 Serverless 能力的关键。

3.1 Serverless 云平台整体架构和 BaaS 底座设计

基于第二部分的技术基础,下面如左图所示先来看一下 Serverless 云平台的整体架构。OpenAPI 对内通过 client-go 与 K8s 控制面交互,对外对接各类工具平台;Master Cluster 作为集群控制面,提供声明式 API 及 CRD 扩展能力,负责资源调度编排的决策;轻量级的 KubeLite 承担调度的上下传达,一边与控制面交互上报状态,一边协同容器运行时完成资源绑定;Redstone Runtime 是容器运行时,负责驱动 Service Host 加载或卸载对应的 Function;而 Service Host 是运行业务应用和数据的载体,也就是 BaaS 底座;资源池则支持物理机、虚拟机、容器等多种资源类型。

Service Host 作为 BaaS 底座,如右图所示是最原始的设计图,最初的设计主要是实现存算解耦,让应用与数据的开发、上线、迭代流程相互分离。要达成这一点,需要从集群视角做 PaaS 化设计,便有了左面的 Cluster Mgr,也就是左面的架构。Service Host 内部的 App Mgr 是应用管理模块,负责应用的加载、热更、卸载及服务状态维护;Data Mgr 是数据管理模块,既为应用提供运行时数据访问,也帮应用层屏蔽了数据加载、更新、发布和一致性维护的细节;还有 Built-in 模块,是平台内置的基础应用,比如状态统计、Admin 接口操作等功能都在这里承载。

业务侧的应用或数据 Function 在我们的场景里被称为 App Bundles,因为这些函数体量较大,是以 “一捆” 的形式存在,包含动态库和配置文件。 Cluster Manager 是集群管理平台,从集群视角对这些 App Bundles 进行管理调度,提供多租户、分布式、集群拓扑等云化特性。此外,架构图未展示的脚手架工具,还提供了 Bazel 编译、Cpplint 检查、Protobuf 代码自动生成等能力,辅助开发流程的自动化落地。

3.2 应用管理架构及解耦框架支持通用模式

进一步来看,应用管理的实现比较直接,主要是通过 dlopen、dlclose 和 dlsym 这几个接口,完成业务动态库的加载、卸载与调用。不过早期的架构存在一些问题,主要是计算框架和基础组件是直接耦合在 BaaS 底座里的,App Bundle 的基类中已经整合了常用组件、图引擎等计算框架,这就导致新框架要对接进来时,得重新适配底座的框架逻辑,尤其是像 bRPC、gRPC 这类纯 RPC 服务,还得额外实现图相关的 Op 定义,流程很繁琐。

另外,不是所有服务都需要用到全套基础组件,有些业务只需要部分功能,耦合的架构会造成不必要的资源冗余。这其实是通用架构的共性问题,所以后来把服务框架和基础组件从底座里拆分了出来,这些框架和组件变成独立的第三方类库,业务可以自主决定是否引入、引入哪些,同时开放维护权限给业务侧,灵活适配不同场景的需求。

对应的 FaaS 代码也调整成了三层继承架构。最底层是基类,定义所有服务的共有操作;中间层是不同类型的 Handler,比如 Data 侧有索引、词典等自定义实现,App 侧有 Chain、Graph 等业务引擎对应的实现;尤其是 CommonServiceHandler 这一层,除了支持通用的 RPC 调用,还能兼容任意服务入口直接运行,这个特性在后续的多语言实践中会具体展开。

3.3 数据管理标准化设计和自定义扩展支持

下面看一下数据管理标准化接口设计和自定义扩展支持。Data Bundle 是平台上承载数据能力的基本单元,它会把同一类型的数据和对应的访问能力打包在一起,通过 SDK 对 App 屏蔽掉底层的实现细节。既可以提供标准化的访问接口,也支持自定义接口,同时自身还包含数据的自更新逻辑。

平台本身已经内置了几种常用的 Data 类型,比如架构图里的索引、模型、词表,还有各类业务字典等。要是这些内置能力满足不了业务需求,用户可以按照接口规范自定义 Data Bundle,这时候的 Data Bundle 相当于 App Bundle 的特化版本,不仅要实现 Data Bundle 对应的接口(像代码里的 Init、Get、Update 这些方法),也得符合 App Bundle 的接口定义,这样才能和上层应用正常协同。

底层的 Data Bundle Manager 负责统一管理所有 Data Bundle,它可以根据数据名称和版本,对外提供标准化的多类型数据访问接口。同时从架构图里也能看到,这个 Manager 还能处理全量更新、实时增量、模型推送等不同事件,支撑数据的动态管理和同步。

3.4 FuncGroup 开发效率之外的业务收益

FuncGroup 带来的业务收益主要体现在 “解耦” 和 “组合” 两种典型场景里。第一种是 App 与数据的解耦,对于数据密集型应用来说,这个优化的提升很明显。因为不用重新拉取加载数据、不用销毁重建容器,再加上 Func 的镜像体积更小、分发速度更快,原本单分片1小时左右的发布时间,现在能缩短到十秒左右,直接把发布效率拉到了秒级。

第二种收益是 “组合”,所谓“天下大事,合久必分,分久必合”,近几年不少应用开始从微服务回归单体形态,比如 Istio 1.5 版本就是典型案例。微服务拆分确实方便了开发和发布,但合并运行的性能会更高。而现在的架构刚好能兼顾,开发部署时还是微服务的模式,到了运行阶段可以把上下游服务组合在同一个 Service Host 里,既保留了微服务的灵活,又拿到了单体架构的性能优势。

这种组合模式还有额外的资源收益,一方面通过就近调度,跨机器通信变成了机器内通信,能降低链路耗时;另一方面也能盘活大规格机器的资源碎片,比如之前 72 核的机器,申请 64 核或两个 32 核后剩下的 8 核会浪费,现在可以用这些余量部署小规格的远程调用应用,把资源碎片也充分利用了起来。

3.5 多语言实践——通用模式下 OpenAPI 自举

做这个多语言实践的目的,是实现流水线的自检。整个 Serverless 链路涉及的环节较多,要是能让系统 “自己运行自己”,再通过自动化测试验证功能,就能快速确认整个链路是否正常。而这个过程,本身也成了我们多语言适配的一次实践。

具体的适配做法并不复杂,首先在 Go 语言代码中通过import "C"引入 C 语言交互支持,再 export 对应的函数,然后用go build命令加上-buildmode=c-shared参数编译,就能生成libneptune.h头文件和libneptune.so共享库,这两个文件正是编译 C++ 侧 Neptune_bundle 所需的依赖文件。

到了 C++ 侧,基于脚手架生成的框架做了简单调整,把原本的业务入口 Run 函数,替换成 Go 侧导出的NeptuneRun;对应的退出函数,则修改为 Go 侧的NeptuneStop。从代码里的 NeptuneBundle 来看,RunUntilAskedToQuit方法中,要么启动线程执行NeptuneRun,要么直接调用该函数;退出时调用NeptuneStop并处理线程资源,这样就完成了 其他语言与 C++ 的多语言协同,也实现了 OpenAPI 的自举运行。

3.6 WASM + Rust 进程内资源隔离的探索实践

此外,C++ 技术栈里有个头疼的痛点是 CoreDump 问题,之前分享 Service Mesh 方案时也提到过,我们是靠 Istio 的虚拟分组、流量调控来规避有问题的流量避免 CoreDump,这更多是从高可用角度减少故障影响,但节点层面怎么彻底解决这类问题,还得找更直接的办法,这也是我们探索 WASM + Rust 的出发点。

作为 Layotto Committer 和 Layotto Wasm Maintainer 之前在 Layotto 里用过 Wasmtime,当时是用它加载多个 WasmFunc 并实现相互调用。虽然 CPU 资源限制比较难实现,这不是 Wasmtime 的问题,而是能力问题,这点之前在 QCon 大会讲师晚宴上也跟 Rust 和 WasmEdge 社区的老师沟通过,后续会借助社区去推进。但内存隔离和安全管控是能实现的,所以准备在 C++ 中去实践。不过,实际将 Wasmtime 应用到 C++ 中发现,虽然 Wasm 执行栈与线性内存是分离的,不像 C++ 执行栈与线性内存一起位于内存中可以用指针修改,C++ 编译的 Wasm CoreDump 大部分情况不影响主进程,但个别时候依然会有影响。

这时候我们想到了 Rust,它主打性能和安全性,编译期的内存、并发检查很完备,同时性能和 C++ 相当。所以现在的方案是,把容易出内存问题的业务逻辑用 Rust 编写,编译成 Wasm 模块,再通过 Wasmtime 加载到 Service Host 里。这样既靠 Rust 的编译检查提前规避了内存安全问题,又借助 Wasm 的沙箱实现了进程内资源隔离,能避免这些业务影响主进程。从架构图也能看到,我们的 Wasm 层支持 Rust、C++、Python 等多语言的 WasmFunc,兼顾了安全和业务开发的灵活性。

4 技术规划与未来展望

在 Serverless 架构落地并验证收益后,后续除了 Serverless 云平台和 BaaS 底座本身的能力提升诸如完善 WASM 沙箱的 CPU 资源限制、低延迟调用等特性,强化进程内隔离稳定性;优化多集群智能调度,结合流量预测减少弹性伸缩延迟;给 BaaS 底座新增分布式缓存、流处理等组件,适配更多业务场景之外,也将围绕如下几个重点方向做持续演进。

4.1 融合 Mesh / MultiRuntime / Serverless 的灵活易用架构

Serverless 方案前期主要在垂直站点落地,当时为了避免额外的不确定性,把服务注册发现、分布式组件调用这些能力都封装在了 Node 上的 BaaS 底座里。而 Service Mesh 其实已经在主站完成了全量部署,现在这两项技术都通过了落地验证,接下来计划把 Mesh Node 架构融入 Serverless 中,这样既能把和业务无关的基础能力剥离出来,也能借助 Service Mesh 的特性增强流量调控能力。

不过当前存在技术适配的问题,BaaS 底座的多数能力是用 C++ 实现的,而 Service Mesh 的数据面采用的是 MOSN,部分 C++ 业务场景 Golang 性能跟不上。所以准备把 Service Mesh 升级为 MoE(MOSN on Envoy)架构,这个架构一方面能提升性能,另一方面支持 Golang 和 C++ 双语言拓展组件;同时 C++ 技术栈里积累的 DPDK、RDMA 相关能力,也可以反哺到 Service Mesh 中,进一步强化 Service Mesh,更好地支撑业务场景。

对于 BaaS 底座里的分布式组件调用部分,计划融合 Layotto(类似 Dapr 的分布式运行时)来增强这部分能力。因为 Layotto 的底层依赖正是 MOSN,结合前面的 MoE 架构,就能形成 “Layotto in Mosn on Envoy” 的组合架构,让整个 BaaS 底座的分布式能力更灵活、更强大。

4.2 基于 GraphEngine 进行更细粒度的 FaaS 调度编排

做过机器学习和深度学习的都知道,TensorFlow 提供了基于 OP 的计算图,Tensor 在图中流转,经过各个 OP 的计算,完成模型的训练与推理。我们把这个思路延伸到了业务侧,除了纯模型计算,像特征服务、预估服务、融合排序、索引召回这些非模型计算和业务逻辑,也采用了类似的图式架构。

具体来说,把业务逻辑抽象成图里的 Data 节点,业务逻辑之间的组织关系则对应图节点的依赖边。业务流程的处理,就是数据在这些节点和依赖边构成的图上流转的过程。从示意图能看到,Data 节点通过 Dependency、Emitter 等关系串联,就能完成整个业务链路的协作。

所以现在想基于这个图引擎(GraphEngine),对多子图中的 OP 算子组合、或是单个图的算子做更细粒度的调度编排,以实现进一步的性能提升和资源利用,最终实现真正的图级或算子级 FaaS,让每个业务逻辑单元都能被精准管控和调度。

4.3 LLM PD 分离智能调度基础上实现 AI Native

众所周知, K8s 原生调度编排体系基于亲和性 / 反亲和性规则及节点打分机制实现资源分配,但面对复杂业务场景的定制化调度诉求,需依托 Scheduling Framework 扩展调度上下文的节点选择逻辑,构建业务适配的调度策略。当前 AI 负载占比持续提升,尤其是大语言模型(LLM)的规模化落地,其推理服务因输入输出长度、请求频率及流量分布的差异化,呈现出负载多样性特征;同时 LLM 推理不同计算阶段对 GPU 算力的需求存在显著异构性,且 GPU 节点配套的 CPU、内存资源常处于非饱和利用状态,导致集群资源整体利用率偏低且分配缺乏精细化统筹。

针对 LLM 在线推理中 Prefill 阶段的计算密集型特征与 Decoding 阶段的访存密集型特征,前期通过扩展 K8s Scheduler 以支持 Cache-aware、Load-aware 等业务感知调度能力,并扩展自定义 Controller 实现热点缓存迁移等管控逻辑,构建了 Prefill-Decoding(PD)分离的资源调度架构。该架构可充分盘活 GPU 节点配套的 CPU、内存资源,实现 GPU 算力的集约化利用,达成集群资源的精细化统筹与高效分配;同时结合 Shared Prefix KVCache、RDMA 高速数据传输等技术,在一些场景下有效降低了 LLM 推理的首 Token 生成时间(TTFT)与 Token 间隔时间(TBT)。

后续,将持续深化 AI 负载的调度与管控能力。针对多业务场景的异构负载特征,构建更精准的业务适配型精细化调度与管控策略;并同步融合社区最新的 Gang Scheduling、工作负载感知调度等技术,实现 AI/ML、批处理、HPC 及超级智能体等异构工作负载的协同编排,支撑更大规模集群的智能调度与全局资源最优配置,实现 AI Native 智能化驱动下的极致性能与高效交付。

结语

前文详细阐述了我主导设计与实践的通用云原生 Serverless 架构,该架构最初面向搜推广等多分片数据密集型应用,如今已突破业务边界,实现了更广泛的场景适配。回顾落地全程,我们既完成了独立 K8s 集群、自定义 CRI/CNI、BaaS-FaaS 协同等组件的自研,也结合业务环境与历史架构特性,实现了多场景兼容适配。这一过程让我深刻体会到,对技术工程师而言,“一切皆应可编码” 的信念至关重要。在此与各位同行共勉。期望本文分享的实践经验,能为大家的云原生架构落地提供有价值的参考。受限于篇幅,架构的部分设计细节与实现逻辑未能逐一拆解,若有任何问题或见解,欢迎通过邮件交流:rayo.wangzl@gmail.com。

作者简介

王志龙,云原生技术专家,具有在腾讯、阿里、京东等公司的多年一线架构与研发经验,长期深耕云原生领域,擅长性能与稳定性的极限优化。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言:
  • 1 云原生溯源与架构选型背景
    • 1.1 云原生溯源和实践理念综述
    • 1.2 从云原生 OS 中看声明式 API 和 CRD
    • 1.3 云原生堆栈——分层架构解析与全景技术图谱概览
    • 1.4 Service Mesh——平衡微服务能力与分布式压力
    • 1.5 业务开发和运维面临的伸缩性、灵活性与弹性问题
    • 1.6 Serverless——云原生时代应用的“高级语言”
  • 2 技术挑战及应对策略
    • 2.1 搜推广业务的应用特点及落地挑战
    • 2.2 业务集群外独立 K8s 集群解锁控制面完整能力
    • 2.3 轻量 Kubelet 代理与容器运行时优化(Containerd+Shim V2)
    • 2.4 自定义 Containerd Shim V2 实现与 CNI 网络适配
    • 2.5 参考 StatefulSetPlus 实现自定义分批灰度能力
    • 2.6 基于自研 CRD + Operator 支持多分片与多集群管理
    • 2.7 平台能力通过 OpenAPI 集成到现有 PaaS 平台
  • 3 Serverless 架构设计与实践
    • 3.1 Serverless 云平台整体架构和 BaaS 底座设计
    • 3.2 应用管理架构及解耦框架支持通用模式
    • 3.3 数据管理标准化设计和自定义扩展支持
    • 3.4 FuncGroup 开发效率之外的业务收益
    • 3.5 多语言实践——通用模式下 OpenAPI 自举
    • 3.6 WASM + Rust 进程内资源隔离的探索实践
  • 4 技术规划与未来展望
    • 4.1 融合 Mesh / MultiRuntime / Serverless 的灵活易用架构
    • 4.2 基于 GraphEngine 进行更细粒度的 FaaS 调度编排
    • 4.3 LLM PD 分离智能调度基础上实现 AI Native
    • 结语
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档