大数跨境

SpringBoot3+Kubernetes 微服务部署:把 RuoYi Office 的28个后端服务组织成可验证的系统

SpringBoot3+Kubernetes 微服务部署:把 RuoYi Office 的28个后端服务组织成可验证的系统 企业软件源码
2026-11-04
10
导读:多服务上Kubernetes,怎样确认部署的是一套能协作的系统?结合RuoYi Office的28个后端,逐项核对启动入口、Jar与镜像、配置挂载、Service和Nacos注册,再以网关、BPM读取

SpringBoot3+Kubernetes 微服务部署:把 RuoYi Office 的28个后端服务组织成可验证的系统

当一个Java工程拆成几十个进程以后,部署工作就不只是“把所有Jar启动起来”。网关要找到正确的服务,服务要读到正确的配置,业务调用要能跨进程完成,发布结果还需要有清楚的验证范围。

RuoYi Office的这套全功能微服务环境,包含28个后端与一个前端。本文沿着服务清单→构建产物→运行配置→多服务发布→调用与验收展开,让每一项结果都能找到对应证据。

你可以把这里的方法用于自己的Spring Boot多模块项目:先明确哪些模块真的是服务,再把构建、运行和验证清单连接起来。数量本身不是目标,目标是能解释每个进程为什么在这里、如何被访问、怎样判断可用。

▲ 28是后端启动入口口径;29包含前端;27是需要Nacos发现的后端预期,IoT协议网关单独验收。

阅读提示:本文文字与命令中的资源前缀统一显示为 ruoyioffice;截图保留实测环境。执行示例前,请将资源名称替换为自己环境中的实际名称。

一、先确认哪些模块需要独立运行

Maven项目中有POM、有*-api目录,或者生成了一个Jar,都不足以证明它应该被当作独立服务发布。

本案例以固定源码快照为起点,核对启动主类、所在Maven模块、聚合关系与最终产物。当前目录中有28个后端启动入口;本轮还对照实际构建来源的已校验源码归档,确认这28个入口均存在并包含main方法。

按本文阅读需要,可将它们分为六组。分组用于理解职责,不表示Kubernetes按这些组自动编排:

  • 访问入口,1个
    :gateway。
  • 基础能力,2个
    :system、infra。
  • 协同业务,6个
    :bpm、oa、hrm、im、weiwork、mdm。
  • 企业经营,10个
    :asset、contract、crm、erp、finance、project、report、statistics、wms、mes。
  • 交易与内容,6个
    :member、product、promotion、trade、pay、mp。
  • AI与设备,3个
    :ai、iot-server、iot-gateway。

这里使用逻辑简称,实际artifact以服务清单为准。例如网关artifact是ruoyioffice-gateway,BPM是ruoyioffice-module-bpm-server。*-api模块提供契约或类型,不能因为其他模块依赖它,就另起一个“API服务”进程。

AgentOS等独立扩展不在这28个后端口径中,也不能把单体启动入口再加进同一微服务环境凑数量。

一项服务,至少保留五个名称

以BPM为例:

Maven artifact:ruoyioffice-module-bpm-server启动主类:BpmServerApplicationspring.application.name / Nacos服务名:bpm-serverDeployment / Kubernetes Service:ruoyioffice-module-bpm-serverPod:Deployment生成的具体实例,名称带后缀

这些名称不必都相同,但映射必须明确。Gateway路由使用应用服务名,Kubernetes Service使用自己的名称,镜像仓库又可能按artifact组织。

遇到“服务找不到”时,先核对对应配置层的命名,而不是在公共发现逻辑中临时加别名,把错误映射隐藏起来。

二、从28个可执行Jar,走到28个固定镜像

已有后端流水线使用cloud构建路径,并排除单体启动模块。核心命令按原参数整理如下:

mvn -B -ntp -nsu -Pcloud -pl '!ruoyioffice-server' \  -Dmaven.repo.local=/maven-cache/repository \  -Duser.home=/home/jenkins/agent \  -Dmaven.test.skip=true package > ../maven-build.log 2>&1

来源:ci/Jenkinsfile.backend-cloud。本文读取已有成功构建,没有重新执行。这里明确跳过测试,不能把构建成功写成单元测试已经通过。

流水线再按预期Jar清单检查文件,核对可执行包中的BOOT-INF/classes/和应用配置,记录Jar哈希,之后构建并发布镜像。

这一步有两个意义:依赖库Jar不能混入运行服务清单;打包数量也不能只靠扫描所有target/*.jar后数一遍,因为聚合工程可能同时生成普通库和可执行包。

▲ 固定源码、预期Jar、Jar摘要与镜像digest分别保留。归档列表属于既有构建,不是本轮新构建的结果。

发布时使用镜像digest,而不是只凭一个会移动的tag判断版本。本轮重新读取集群,28个后端Deployment的镜像均与接受的候选清单digest一致;前端另按自己的产物来源核对。

还要保留一个容易遗漏的区别:候选源码目标与镜像实际构建来源不是同一个字段。 当前候选记录复用了28个既有后端镜像,目标源码记录与这些镜像实际构建提交不同。不能把候选提交直接填成所有镜像的构建来源。

因此版本账至少包含候选目标、源码归档哈希、每个artifact的实际构建来源、镜像digest和运行对象。镜像对账通过,只证明部署身份一致;复用产物的兼容性还需相应门禁,不能仅靠改标签证明。

三、先准备配置与中间件,再谈启动业务进程

这套环境将MySQL、Redis、Nacos等中间件放在集群外的infra VM,业务服务运行在Kubernetes。应用配置主要通过每服务的运行Secret挂载到/config,应用启用lab profile,并附加读取该目录。

BPM Deployment中的相关字段可以摘出为:

containers:  - name: app    env:      - name: SPRING_PROFILES_ACTIVE        value: lab      - name: SPRING_CONFIG_ADDITIONAL_LOCATION        value: file:/config/      - name: SERVER_PORT        value: "8080"    volumeMounts:      - name: config        mountPath: /config        readOnly: truevolumes:  - name: config    secret:      secretName: ruoyioffice-module-bpm-server-config      defaultMode: 0440

来源:当前BPM Deployment选定字段,省略镜像、其他环境变量、信任库初始化与其余卷,不是完整可直接部署的Pod清单。未展示Secret内容。

每个服务都需要核对自己真正使用的数据库、Redis逻辑库、Nacos环境与其他依赖。不能因为所有服务共享一台infra VM,就让它们无差别使用同一套数据库与凭据。

配置中心也要区分职责:本案例使用Nacos注册发现,但不能据此声称全部业务配置已经从Nacos动态加载。挂载配置、环境变量、远端配置各自是否生效,应按应用实际配置源检查。

私有CA是另一项启动前置。当前Pod先用初始化容器准备Java信任库,再交给应用只读使用。若Pod停在初始化阶段,应先看初始化状态与日志;此时应用还未启动,盲目调整业务接口或Nacos服务名不会解决问题。

▲ 先区分初始化未完成、应用启动失败和启动后调用失败,再选择排查位置。

四、让每个Deployment和Service成对核对

多个后端都监听8080,并不产生宿主端口冲突,因为它们使用不同Pod网络地址;端口的作用域要和网络对象一起理解。

BPM的Service通过标签选择对应Pod:

apiVersion: v1kind: Servicemetadata:  name: ruoyioffice-module-bpm-server  namespace: ro-all-cloudspec:  selector:    app: ruoyioffice-module-bpm-server  ports:    - name: http      port: 8080      targetPort: 8080

来源:当前Service选定字段,省略由集群分配的ClusterIP等值。

核对时同时看Service、selector、EndpointSlice和Pod端口:Service存在但没有就绪端点,排查方向与“端点存在却调用超时”不同;服务名相似也不能替代标签检查。

▲ 虚拟服务地址与具体Pod地址分别显示。两者应能对应同一份服务目录。

当前BPM实例的资源初始值是request 768Mi、limit 2Gi,和不少其他业务服务的384Mi/1Gi不同。资源配置应随模块启动特征调整,不能把同一份模板的数字机械复制到所有服务。

探针方面,当前BPM启动与就绪检查都是TCP8080:

startupProbe:  tcpSocket: {port: 8080}  periodSeconds: 10  failureThreshold: 60readinessProbe:  tcpSocket: {port: 8080}  periodSeconds: 10  failureThreshold: 6

来源:当前Deployment。它检查监听可达,不验证权限、数据库查询、流程引擎或Feign调用。

Pod还配置了单副本、maxSurge: 0、maxUnavailable: 1。这种更新策略允许替换间隙;不能因为策略名叫RollingUpdate,就承诺零停机。探针与滚动策略的完整取舍会在容器化专题展开。

五、把部署步骤分层,不把apply当作依赖编排器

对于新环境,建议按以下层次组织准备和检查:

  1. 运行底座
    :节点、DNS、存储、镜像拉取、网络与证书。
  2. 基础依赖
    :数据库、Redis、Nacos及环境专用配置。
  3. 基础应用
    :系统、基础设施能力,以及需要它们的网关和业务入口。
  4. 业务域
    :BPM、OA、人力等模块,结合实际RPC与数据依赖逐批检查。
  5. 协议与外部集成
    :IoT协议网关、任务回调、文件存储和其他专项链路。
  6. 访问与验收
    :前端、入口、认证API、代表性业务场景及观测记录。

这是一份准备与验收顺序建议。当前全功能发布流水线对固定manifest执行一次apply,再等待运行对象就绪,并没有实现上述业务依赖DAG的自动分批启动。

Kubernetes可以创建这些对象,但不会因为清单文件里把system排在BPM前面,就保证system已经能处理业务,才启动BPM。应用启动重试、配置可用性、依赖连接与后续门禁仍要单独处理。

实际发布过程还区分“准备配置/RBAC”和“部署工作负载”:准备脚本提供受控输入,应用工作负载由Jenkins发布。不要把包含凭据初始化、基线导入等动作的Prepare脚本当成日常巡检反复运行。

▲ 本轮采样29个Deployment和29个Pod均就绪。它是当前观察结果,不代表所有业务功能均完成验收。

六、28个后端,为什么只预期27个Nacos服务

服务发现清单应按应用依赖确定,而不是直接等于镜像数量。

本轮对ro-all-cloud命名空间读取Nacos:27个预期发现服务均有健康、启用的实例。按DEFAULT_GROUP查询得到27项;控制台跨组清单还显示SERVERLAB_CHECKS中的registry-probe,合计28项。分组、预期集合与统计范围都应写清,不能只拿一个总数判断成功。

▲ 比较预期服务名与实例,不用总数相等代替逐项检查。

没有纳入这27项的,是独立IoT协议网关。对应锁定源码的POM没有Nacos discovery starter,它通过协议能力和配置的直接HTTP RPC与IoT服务配合。

当前协议网关发布了MQTT1883和设备HTTP8092,其探针检查1883。它不出现在上述发现集合中,不应自动认定为注册失败;反过来,1883就绪也不能说明设备鉴权、上报、消息总线与IoT业务闭环都已通过。

▲ 网关也是后端启动入口,但它的运行与验收契约不同于普通HTTP业务服务。

对BPM,则应能把Nacos实例IP与实际Pod对应起来,并核对端口和环境:

▲ 同一服务的Kubernetes对象与应用注册信息,应回到同一份目录逐项对账。

七、沿一条读取链路检查网关和跨服务依赖

以读取待办任务为例,入口经过前端同源代理与Gateway,再由BPM处理请求。锁定源码中的BPM路由为:

- id: bpm-admin-api  uri: grayLb://bpm-server  predicates:    - Path=/admin-api/bpm/**  filters:    - RewritePath=/admin-api/bpm/v3/api-docs, /v3/api-docs

来源:实际镜像构建来源归档中的Gateway配置。grayLb是项目的自定义负载均衡协议;路由存在不代表本次做过灰度发布验证。文档路径重写也不是把所有业务路径前缀删除。

BPM待办读取有一个重要分支:

PageResult<Task> pageResult = taskService.getTaskTodoPage(getLoginUserId(), pageVO);if (CollUtil.isEmpty(pageResult.getList())) {    return success(PageResult.empty());}Map<String, ProcessInstance> processInstanceMap = processInstanceService.getProcessInstanceMap(        convertSet(pageResult.getList(), Task::getProcessInstanceId));Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(        convertSet(processInstanceMap.values(), instance -> Long.valueOf(instance.getStartUserId())));

来源:同一锁定归档中的BpmTaskController.getTaskTodoPage,截取前半段,不包含响应组装等完整逻辑。

在非空待办分支中,BPM需要查询流程实例与发起人信息。AdminUserApi是Feign契约,批量用户查询走system服务的RPC接口,再转换成用户映射。

▲ 虚线表示非空待办时的源码依赖。本轮返回空列表,没有把这条非空分支写成已实测。

本轮通过真实入口执行了带认证的待办GET:HTTP200、应用code0、返回行数0。这能证明本次认证读取与路由成功,但由于源码在空列表时提前返回,不能据此证明上面的用户信息Feign调用已经实际发生。

后续若要验收这条协作链,需要准备有真实发起人与待办数据的受控场景,观察返回字段和相应服务调用,再验证任务处理后的业务结果。本文没有为了取证创建流程或提交业务单据。

Loki中还能定位当前BPM Pod的既有启动日志:

▲ 日志中的启动日期属于既有运行记录。它用于定位应用启动,不替代当前就绪、认证API或业务验收。

八、用四份结果交付部署,而不是一个“全部通过”

1. 产物与运行身份

核对预期28个后端是否都有镜像,运行digest是否与接受的清单一致,实际构建来源是否保留。本轮28个后端digest全部匹配,前端作为独立产物处理。

2. 同时就绪与服务发现

不能先看到A就绪,过一会儿看到B就绪,就说整个系统同时就绪。现有发布门禁在Deployment可用后,继续检查预期Pod总数、Running与容器Ready,要求连续六轮整体满足。

门禁循环的关键判断为:

if awk 'NF != 2 || $1 != "Running" || $2 != "true" {bad=1}END {if (NR != @@WORKLOAD_COUNT@@ || bad) exit 1}' pod-readiness.txt; then  stable=stable" -ge 6; then break; fisleep 10

来源:ci/Jenkinsfile.deploy-all-cloud循环内摘录。占位总数由实际清单生成;现有判断按当前每Pod一个常规应用容器的输出结构编写,增加sidecar后要同步调整,不能当万能通用脚本。

这是一段短窗口传输层门禁,不是长期稳定性测试。服务发现另按27个预期名称、健康实例与当前Pod检查,协议网关另列。

3. 认证、路由与只读业务入口

本轮还重新执行了全功能环境现有的只读验收:TLS与4个同源静态资源通过、专用实验室登录成功、权限信息可读、匿名访问受保护接口被拒;24个代表性分页接口通过,0失败,2项未覆盖。

未覆盖的是statistics和report:现有选择规则找不到适合的简单只读分页映射,需要专门场景。Gateway和IoT协议网关也不是靠同一套业务分页规则计分,而在入口与协议范围分别检查。

24是代表性接口覆盖数,不能写成所有接口、所有模块内部逻辑或全部业务流程通过。空数据的分页成功,还应明确它没有覆盖哪些非空分支。

4. 业务闭环与专项依赖

审批提交与完成回写、通知落库与实时送达、文件上传与回调、任务触发与执行结果、设备接入与消息处理、真实模型调用,都需要自己的场景与证据。

把未验项留下来,会让下一次测试更准确。不能因为某个容器存在,或者某个GET返回code0,就把支付回调、MQ、设备、第三方账号与模型服务一并计为已接通。

九、读者落地时可以怎样对账与排查

先准备三份彼此可关联的清单:服务/契约清单、产物/配置清单、验证/缺项清单。每个artifact都能追到镜像、Deployment、Service、注册名与验收项。

只读巡检可以从这些命令开始:


kubectl -n ro-all-cloud get deployments,pods -o widekubectl -n ro-all-cloud get serviceskubectl -n ro-all-cloud get endpointslices \  -l kubernetes.io/service-name=ruoyioffice-module-bpm-serverkubectl -n ro-all-cloud describe deployment ruoyioffice-module-bpm-serverkubectl -n ro-all-cloud get events --sort-by=.lastTimestamp

发布失败时沿层次定位:镜像拉取失败先看仓库与身份;Init失败先看CA或前置准备;应用启动失败看配置、依赖和日志;Service无端点看selector与Ready;Nacos缺实例看实际服务名与环境;API失败再区分网关、认证、RPC和业务依赖。

配置挂载和Secret内容应在受控终端内检查,记录只保留引用与必要结论,避免把凭据贴进日志、截图或文章。修改后重新采集对应层证据,不用重跑数据库初始化或全量部署来代替定位。

常见问题(FAQ)

28个后端是否意味着28个业务模块?

不是。它包含入口、基础服务、业务域和独立协议网关。一个业务域也可能对应多个部署服务,计数应以固定源码和产物清单为准。

多个服务都监听8080,会不会端口冲突?

普通Pod使用各自网络地址,内部端口可以相同。是否冲突要看Pod网络、hostNetwork、hostPort及外部端口映射等实际配置。

Nacos实例数量少于后端数量,一定是部署失败吗?

应先核对预期发现集合。本案例独立IoT协议网关不纳入Nacos发现预期,其他服务仍须逐个核对健康实例。

所有Pod都Ready,是否代表BPM审批能完整运行?

还需要认证、数据库、流程数据、跨服务调用和业务回写验收。当前TCP探针不能代替审批场景。

候选版本已经更新,镜像是否都来自新提交?

不一定。若复用既有产物,应保留每个镜像的实际构建来源,不能把候选目标当成重新构建证明;兼容性与运行验收也要分别保留。

让28个进程回到一份能解释的系统清单

多服务部署的难点,是在不同工具之间保持同一份事实:哪个启动入口产生哪个镜像,哪个配置交给哪个进程,哪个注册名对应哪个实例,以及哪项验收真正覆盖了协作链路。

这套环境的当前结果可以明确表达为:29个Deployment就绪、28后端镜像对账、27个预期发现服务健康、24个代表性分页接口通过,另外保留专用场景与业务闭环缺项。这样的结果比一个笼统的“微服务部署成功”更容易复核,也更方便下一次交付继续推进。

我们会持续分享企业软件开发实践与 RuoYi Office 产品进展。

【声明】内容源于网络
0
0
企业软件源码
RuoyiOffice 是一套基于 Spring Boot + Vue3 +Uniapp 的企业一体化管理平台,集 OA、CRM、ERP、工作流、HR、资产、合同、项目、AI应用等业务于一体,帮助企业用一个系统协同管理多类核心业务。
内容 182
粉丝 0
企业软件源码 RuoyiOffice 是一套基于 Spring Boot + Vue3 +Uniapp 的企业一体化管理平台,集 OA、CRM、ERP、工作流、HR、资产、合同、项目、AI应用等业务于一体,帮助企业用一个系统协同管理多类核心业务。
总阅读4.8k
粉丝0
内容182