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: appenv:- name: SPRING_PROFILES_ACTIVEvalue: lab- name: SPRING_CONFIG_ADDITIONAL_LOCATIONvalue: file:/config/- name: SERVER_PORTvalue: "8080"volumeMounts:- name: configmountPath: /configreadOnly: truevolumes:- name: configsecret:secretName: ruoyioffice-module-bpm-server-configdefaultMode: 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-servernamespace: ro-all-cloudspec:selector:app: ruoyioffice-module-bpm-serverports:- name: httpport: 8080targetPort: 8080
来源:当前Service选定字段,省略由集群分配的ClusterIP等值。
核对时同时看Service、selector、EndpointSlice和Pod端口:Service存在但没有就绪端点,排查方向与“端点存在却调用超时”不同;服务名相似也不能替代标签检查。
▲ 虚拟服务地址与具体Pod地址分别显示。两者应能对应同一份服务目录。
当前BPM实例的资源初始值是request 768Mi、limit 2Gi,和不少其他业务服务的384Mi/1Gi不同。资源配置应随模块启动特征调整,不能把同一份模板的数字机械复制到所有服务。
探针方面,当前BPM启动与就绪检查都是TCP8080:
startupProbe:tcpSocket: {port: 8080}periodSeconds: 10failureThreshold: 60readinessProbe:tcpSocket: {port: 8080}periodSeconds: 10failureThreshold: 6
来源:当前Deployment。它检查监听可达,不验证权限、数据库查询、流程引擎或Feign调用。
Pod还配置了单副本、maxSurge: 0、maxUnavailable: 1。这种更新策略允许替换间隙;不能因为策略名叫RollingUpdate,就承诺零停机。探针与滚动策略的完整取舍会在容器化专题展开。
五、把部署步骤分层,不把apply当作依赖编排器
对于新环境,建议按以下层次组织准备和检查:
- 运行底座
:节点、DNS、存储、镜像拉取、网络与证书。 - 基础依赖
:数据库、Redis、Nacos及环境专用配置。 - 基础应用
:系统、基础设施能力,以及需要它们的网关和业务入口。 - 业务域
:BPM、OA、人力等模块,结合实际RPC与数据依赖逐批检查。 - 协议与外部集成
:IoT协议网关、任务回调、文件存储和其他专项链路。 - 访问与验收
:前端、入口、认证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-apiuri: grayLb://bpm-serverpredicates:- 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; thenstable=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 产品进展。

