最近刚帮客户做完一轮Nacos的信创改造,核心要求就一句话:把Nacos默认内嵌的Tomcat换掉,统一改成保兰德的Web中间件。这活儿看着简单,真落地的时候全是细节。Nacos自身是Spring Boot应用,默认用内嵌Tomcat对外提供HTTP服务,信创环境要求中间件必须是国产可控产品,于是Tomcat就成了第一个要“动刀”的地方。这篇文章我把整个改造过程、踩过的坑和验证思路完整记录下来,主要面向做信创适配的Java后端、运维和中间件工程师。整篇文章会围绕Nacos 2.5.4版本展开,替换目标以保兰德中间件为例,思路同样适用于同类型的国产应用服务器。
1. 改造前先理清思路:为什么是Nacos,为什么动Tomcat
1.1 Nacos默认架构里的Tomcat是什么角色
Nacos在信创项目中几乎是绕不开的组件,分布式系统里服务注册、配置管理都靠它。但很多人没细想过,Nacos本身并不只有一个“注册中心”的身份,它的控制台、开放API、配置推送通道,都是跑在一个Web容器里的。
默认情况下,这个Web容器就是Spring Boot内嵌的Tomcat。Nacos控制台的管理页面、/v1/ns、/v1/cs这类HTTP接口,全部由Tomcat处理。换句话说,你日常打开Nacos控制台、通过SDK调用它的Open API,流量其实都经过了内嵌Tomcat。
这里面有个关键点很多人容易忽略:Nacos 2.x开始,客户端和服务端之间的核心通信已经改成gRPC了,走的是Netty端口,默认是8848再加上1000偏移,也就是9848。这段链路从头到尾不经过Tomcat,是Nacos自己用Netty拉起来的。
所以严格说,把Nacos里的Tomcat替换成保兰德,替换的是“HTTP对外服务层”,不是“整个运行时”。这个边界如果不在一开始划清楚,后面做方案的时候就会被绕进去。
改造前的第一件事,就是梳理清楚哪些流量走Tomcat、哪些流量走Netty。以我这次基于Nacos 2.5.4的实测来看,走Tomcat的部分主要是控制台静态资源、登录鉴权接口、HTTP Open API、部分管理端点。而服务注册发现、配置监听推送这些核心链路,走的是gRPC。
1.2 保兰德中间件好在哪,替换的真正目标是什么
保兰德是国内比较常见的Java应用服务器中间件,在信创名录里经常能见到它。它面向的是企业级Java应用部署场景,和Tomcat这种“嵌入式容器”定位不同,它更像一个完整的应用服务器,自带管理控制台、集群管理、JVM监控、部署发布这类能力。
替换的真正目标并不是“把Tomcat干掉”这么简单,而是把Web容器这个底座换成一个符合信创要求、可统一管控、有服务化运维能力的中间件。
打个比方,Tomcat像是一个随应用一起运行的嵌入式引擎,启动方便但分散在各应用里,每套应用都得单独维护。保兰德这类中间件像一个“公共机房”,多个应用可以部署在同一个中间件实例下,由统一的控制台去管理,监控、日志、启停都在一个地方。
这在信创大环境里很重要。等保合规、国产化验收的时候,中间件清单里写的是保兰德,而不是一堆分散的Tomcat,审计上少了很多麻烦。
但这里也要泼一盆冷水:替换容器不能只停留在“能跑起来”,必须保证原有功能完全兼容,还要考虑部署形态变化带来的连锁问题。比如Nacos原本是单进程直接启动的,现在变成war包扔到中间件里,进程管理、日志路径、环境变量、端口占用就全都变了。
1.3 替换前需要明确的边界
结合我这次实操,建议在动手前先把“改什么”和“不改什么”列成一张表,免得改着改着把手伸到不该碰的地方。
不改的部分很明确:Nacos的核心逻辑、服务发现算法、配置管理模型、gRPC通信层统统不动。这些是Nacos之所以是Nacos的东西,改了他们等于自己重写了一个注册中心,维护成本直接失控。
必须改的部分主要有四个方面:
一是打包形式,Nacos默认是Spring Boot的fat jar,要部署到保兰德浏览器里,必须改成war包,并且把内嵌Tomcat依赖的scope调成provided。
二是部署方式,从“直接执行startup.sh启动一个进程”变成“上传war包到中间件管理控制台发布”,启动方式、启停命令、开机自启逻辑都要重新适配。
三是端口和会话,8848这个HTTP端口原来由Tomcat监听,替换后要交给保兰德接管,9848的gRPC端口还是Nacos自己监听,两项要分开考虑。控制台登录态的管理也随着容器切换发生变化,保兰德有自己的会话机制。
四是安全和存储,信创环境对数据库国产化有要求,很多项目会顺手把后端从MySQL换成达梦;同时Nacos默认不开鉴权,这种“裸奔状态”在等保环境下是肯定过不去的,必须借助这次改造一并处理。
这些边界想清楚之后,整体替换方案基本就有了雏形。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 核心改造实操:从内嵌Tomcat切到保兰德
2.1 整体方案选型
先说结论,目前把Nacos迁到保兰德,实际落地主要有两条路。
方案A是“war包化后部署到保兰德”,也就是把Nacos Console打成war包,像部署普通Web应用一样发布到中间件里。这条路对Spring Boot技术栈的人来说比较好理解,本质上是切换Servlet容器,把内嵌容器改成外部容器。
方案B是“保留内嵌Tomcat,仅用保兰德做前置流量代理”,也就是Nacos还是老样子跑,外面套一层保兰德做负载和转发。
两条路对比如下:
| 对比项 | 方案A:war包部署 | 方案B:前置代理 |
|---|---|---|
| 是否真正移除Tomcat | 是 | 否 |
| 信创验收认可度 | 高 | 一般 |
| 改造工作量 | 中等 | 较低 |
| 运维统一性 | 高 | 较低 |
| 核心链路影响 | 仅HTTP层 | 无侵入 |
| 长期维护成本 | 低 | 偏高 |
我这次实际选的是方案A。原因很简单,客户信创验收的标准就是“中间件清单里不能出现Tomcat”,方案B本质上还是Tomcat在跑,审查的时候解释成本非常高。而且从长期运维看,war包部署到保兰德之后,应用生命周期和中间件生命周期是统一的,出问题排查也方便。
2.2 把Nacos改造成war包的详细步骤
先说明,war化Nacos不是官方开箱即用的功能,需要基于源码做一些调整。如果你们公司连的是Nacos官方release包,那得先把源码拉下来重新构建。
我这边操作基于Nacos 2.5.4,代码结构调整不大。核心就三步。
第一步,修改console模块(Nacos控制台模块)的pom.xml,把打包方式从jar改成war:
xml复制<packaging>war</packaging>
第二步,把内嵌Tomcat的依赖scope改成provided,这样打包的时候不会把Tomcat打进去:
xml复制<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
第三步,让启动类继承SpringBootServletInitializer并实现configure方法。这个操作很关键,外部容器启动Web应用时,不是执行main方法,而是通过ServletContainerInitializer机制找SpringApplication的入口:
java复制@SpringBootApplication
public class ConsoleApplication extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(ConsoleApplication.class);
}
public static void main(String[] args) {
SpringApplication.run(ConsoleApplication.class, args);
}
}
需要注意的是,Nacos 2.5.4的console模块目录里有多个Application类,比如ConsoleApplication只是其中启动入口之一,改造时一定要找到真正标记了@SpringBootApplication的那个类,别改错文件。
改完后执行Maven构建:
bash复制mvn -Prelease-nacos -DskipTests clean install
构建产物会在distribution目录下面,找到console模块对应的war文件就行。如果你们不需要自定义源码改动,只想做验证,也可以只在源码里做最小改动然后构建,不要动核心模块代码。
2.3 保兰德中间件上部署与配置
war构建出来之后,剩下就是部署到保兰德中间件。
先说部署路径。保兰德中间件的管理控制台里通常有“应用部署”功能,上传war包后指定应用名和上下文路径。我这边建议上下文路径直接设为/nacos,这样访问方式和原来保持一致,客户端SDK不用改任何配置。
上传war包后,中间件会自动解压并启动应用。这时候要和团队对一遍端口规划:
- 8848端口:原来由Nacos内嵌Tomcat监听,现在由保兰德中间件接管。需要在保兰德的连接器配置里对外暴露8848。
- 9848端口:Nacos的gRPC端口,由Nacos自身Netty监听,和Tomcat无关。部署到保兰德之后,它依然由Nacos进程内部拉起,中间件不需要管,但防火墙、SLB的端口放行规则要考虑进去。
- 9849端口:集群间gRPC通信端口,同样由Netty负责。
端口混淆这个坑,我见过很多团队踩过。他们只把8848做了映射,没放9848,结果服务注册不上去,控制台看起来又一切正常,排查半天才发现是gRPC长连接被防火墙挡了。
JVM参数也不能沿用原来的默认值。war跑在保兰德里,JVM是由中间件管理的,不再受Nacos的startup.sh控制。需要在保兰德的应用配置里手动设置-Xms、-Xmx、JVM调优参数,否则默认堆上限制可能只有中间件实例的统一配置那么大,Nacos运行一段时间就出内存问题。
保兰德管理控制台里最好把以下内容一并配置好:
- 应用日志输出路径,改到中间件的集中日志目录,别让Nacos自己写到随机位置。
- 会话超时时间,Nacos控制台登录态默认走容器Session,建议保持中间件默认值,不要设置得太短,避免频繁重新登录。
- 部署模式选择“独立部署”而不是“共享部署”,避免Nacos和其他应用抢类加载器。
2.4 数据库层适配:达梦数据库接入
数据库这块是信创改造的另一个重头。很多项目在换中间件的同时,也会把后端数据库从MySQL换成达梦,Nacos的数据源适配就得同步处理。
Nacos从2.2版本以后支持数据源插件机制,默认支持MySQL和Derby。想接入达梦,一般有两种做法:
一种是达梦开启MySQL兼容模式,这样Nacos还是按MySQL数据源方式连接,SQL语句基本不用改。这个方案实施最快,但对达梦的兼容模式配置有一定要求,而且某些特殊SQL还是需要人工确认。
另一种是使用社区适配过的达梦数据源插件或自定义DataSourceProvider,完全按达梦原生语法来适配。这种方案更彻底,但工作量和测试成本都比第一种高不少。
我这次的客户需求相对明确,用的就是达梦,我选择了兼容MySQL模式的方案,改三处配置:
properties复制spring.datasource.platform=mysql
spring.datasource.driver-class-name=com.dameng.jdbc.DmDriver
spring.datasource.url=jdbc:dm://127.0.0.1:5236/nacos_config?useUnicode=true&characterEncoding=utf8
spring.datasource.username=xxx
spring.datasource.password=xxx
db.num=1
db.url.0=jdbc:dm://127.0.0.1:5236/nacos_config?useUnicode=true&characterEncoding=utf8
db.user.0=xxx
db.password.0=xxx
这里提醒一下,Nacos启动的时候需要初始化表结构,官方自带的是mysql-schema.sql。如果达梦开着MySQL兼容模式,大部分建表语句可以直接执行,但有几类SQL要特别确认:
- 字符集相关语法,达梦对
utf8mb4这类字符集名的兼容性不一定和MySQL完全一致。 - 自增列的写法,MySQL是
AUTO_INCREMENT,达梦在兼容模式下通常能处理,但如果遇到主键自增建表失败,需要手工改成达梦的IDENTITY。 - 分页SQL,Nacos内部很多管理查询用
LIMIT分页,达梦兼容模式下一般可用,但不兼容时得改写为ROWNUM。
还有一点很容易忽略:Nacos集群模式下所有节点必须连同一个数据库,达梦也一样。如果三个Nacos节点各连一套达梦,配置数据就不一致了,控制台里你会看到服务列表时有时无,配置发布之后不同节点读到不同值,这种问题排查起来特别头疼。
3. 安全加固与集群化:信创环境的硬性要求
3.1 没开鉴权的Nacos等于裸奔
Nacos 2.5.4的默认配置里,鉴权是关闭的。也就是说,只要部署了Nacos,任何能访问到8848端口的人,都可以直接调Open API拉取所有服务列表、读写配置,甚至创建一个恶意配置把整个系统的运行参数改掉。这就是社区里经常说的“Nacos namespaces未授权访问漏洞”。
信创改造过程中,这个问题大概率会被等保测评直接揪出来。我遇到不止一个项目,配置中心还没上线就被安全扫描报告打了红。
开启鉴权很简单,在Nacos的application.properties里加几行配置:
properties复制nacos.core.auth.enabled=true
nacos.core.auth.plugin.nacos.token.secret.key=这里填一个至少32字节的Base64编码密钥
密钥不要用默认值,更不要用网上教程里公开的那串字符串。这个密钥是用来签发和校验JWT Token的,一旦泄露,攻击者可以伪造管理员身份。建议用系统随机数生成:
bash复制openssl rand -base64 32
还有一个很容易配错的地方:Nacos 2.5.4的鉴权配置项在不同版本里有调整,如果改完发现鉴权没生效,先查一下当前版本的application.properties里到底读取的是哪个配置键。旧版本可能用的是nacos.core.auth.token.secret.key,新版本换了命名空间之后配置路径会变化,直接把网上搜的配置贴进去不一定会生效。
控制台的默认账号密码nacos/nacos也要第一时间改掉。这个属于基础操作,但真的很多人忘。
保兰德中间件本身也提供了安全增强能力。我这次在保兰德里做了三层叠加:
- 应用级访问日志,记录所有来自Nacos控制台IP的请求。
- IP黑白名单,Nacos管理接口只允许运维网段访问。
- SSL终止,HTTPS在中间件层处理,Nacos自身不再单独配证书。
这层“容器侧安全”是以前Nacos内嵌Tomcat跑的时候不太好实现的,换成保兰德之后反而顺手了。
3.2 集群部署的实践方式
生产环境里Nacos必须是集群,信创环境下也不例外。
我的推荐拓扑是三节点集群加共享数据库。三个节点分别部署在保兰德中间件实例上,war包相同,配置有差异,共同连接同一个达梦数据库。客户端和服务端通过SLB或者VIP访问,SLB后面挂三个节点的8848和9848端口。
Nacos集群发现靠的是cluster.conf文件。每个节点在启动前,在Nacos的conf目录下创建一个cluster.conf,写入三个节点的IP和8848端口:
code复制192.168.1.11:8848
192.168.1.12:8848
192.168.1.13:8848
注意这里写的是8848,不是9848。很多文档提到集群配置要写偏移量,实际上节点间gRPC通信会自动在8848基础上加1000,配置文件里不需要显式写9848。
三台节点之间通过9849端口进行集群间通信,这个在之前防火墙放行时容易漏掉。如果9849被挡住了,节点之间无法同步心跳,控制台会显示服务实例状态异常,但看起来每个节点自己又是好的,很迷惑人。
保兰德中间件的集群能力在Nacos集群之上属于另一层概念。中间件集群负责Web应用层的高可用,比如某个中间件实例挂了,流量自动切换到其他实例;Nacos集群负责注册中心本身的数据一致性和服务容灾。两者不冲突,但规划的时候要分开考虑。
我这边的做法是,每台物理机器上部署一个保兰德中间件实例,每个中间件实例上部署一套Nacos war应用,这样Web容器和注册中心的高可用边界对齐,出了问题也好定位。
3.3 配置与数据同步的验证方式
集群搭建完,不能只看控制台右上角是不是三个节点都是UP,还要做几个功能验证:
先验证配置发布同步。在控制台发布一个新配置,然后分别登录三个节点的控制台,看同一份配置是否都能读到,并且内容一致。达梦共用数据库的情况下,这个验证大概率是过的,但还是要测,尤其达梦兼容模式有SQL差异的时候,某条写入语句可能在一个节点上成功,在另一个节点上报错,这是真实发生过的。
再验证服务注册与发现。用客户端SDK注册一个临时服务,观察三个节点上服务列表是否都能看到这个实例,并且健康检查状态正常。临时实例走的是gRPC心跳,如果9848端口放行有疏漏,这个验证立刻就能暴露问题。
最后做个故障演练,停掉其中一个节点,看剩余两个节点能否继续正常注册和发现新服务,已经注册的服务会不会被误判为不健康。
这些验证不是可选项,信创项目验收的时候都会被问到。
4. 常见问题与排查技巧实录
4.1 高频问题速查表
改造周期里我整理了一张问题速查表,列一下,对做同类项目的同学应该有帮助:
| 现象 | 常见原因 | 处理方式 |
|---|---|---|
| 控制台页面样式全丢,只有文字没有布局 | war包静态资源路径和外部容器上下文路径不一致 | 检查上下文路径是否设置为/nacos,并确认中间件静态资源mapping没有被覆盖 |
| 服务注册不上,日志一直报连接异常 | 9848端口没放行 | 防火墙和SLB都检查,别只放8848 |
| 服务列表能看到实例,但健康检查一直失败 | gRPC心跳端口和集群通信端口不通 | 检查9848和9849端口连通性,用telnet验证 |
| 配置发布后客户端拿不到新配置 | 客户端只连了HTTP端口,长连接被中断 | 检查客户端gRPC端口配置,确认9848在Nacos地址里可访问 |
| 启动后日志出现大量ClassNotFoundException | 外部容器的类加载和Nacos依赖冲突 | 检查双亲委派设置,保兰德里调整应用的类加载顺序 |
| 达梦建表报语法错误 | SQL语法和MySQL不兼容 | 改用达梦原生建表脚本,或检查兼容模式参数 |
| 控制台登录后在集群间跳转掉线 | 会话未在中间件层共享 | 配置中间件集群会话复制,或使用统一鉴权入口 |
| Tomcat的线程池相关参数失效 | 现在跑在保兰德上,Tomcat线程参数不再生效 | 改用保兰德连接器配置管理HTTP线程池 |
这张表里的问题,前四个出现的概率最高,几乎每个项目都会碰到,尤其是端口放行问题,排查的时候一定要把“HTTP端口放行了但gRPC端口没放”这个组合记在脑子里。
4.2 改造中我自己踩过的坑
这次改造里印象最深的一个坑,是war部署上去之后控制台能打开,但登录接口正常,页面上的静态资源全部404。检查半天发现是保兰德中间件默认的Servlet处理路径和Nacos期望的不一致,静态资源被中间件安全过滤器拦掉了。最后是在中间件侧调整了静态资源映射规则,把/nacos/前缀下的静态文件排除出安全拦截范围才解决。
这类问题在Tomcat里几乎不会遇到,因为Spring Boot内嵌Tomcat对自己的静态资源做了默认映射,换到外部容器后,容器的全局配置就开始介入,环境差异就暴露出来了。
第二个坑是启动时间。war包部署到保兰德后,Nacos启动比原来慢了不少,快的时候要两分多钟,慢的时候三分钟还没起来。排查下来发现两方面叠加:一是war包首启要解压,中间件扫描时间比jar模式长;二是JDK的SecureRandom在Linux下初始阻塞,熵源不足时拖慢启动。
这里推荐一个初始化脚本里加JVM参数的办法:
bash复制-Djava.security.egd=file:/dev/./urandom
这个参数能显著缓解启动卡顿。顺手再给中间件把JVM的-Xloggc打开,第一次启动慢的时候至少能拿到日志判断到底卡在哪个阶段。
第三个坑在配置中心的使用上。我改造完后,客户端SDK配置没动,结果部分服务报“无权限访问配置”,排查才发现是鉴权开启后客户端没有配置用户名密码和Token。这个不算坑,但很容易被忽略,因为控制台登录已经配置好了,以为客户端就自动有权限了,实际上两边要分别配置。
4.3 信创验收前要做的回归清单
到验收阶段,我建议按下面这个清单做一次完整回归,别信“能打开控制台就算改造完成”这种话。
控制台功能回归:登录是否正常,命名空间是否正常显示(这个对应“Nacos命名空间一直为null”的问题),配置列表新建、编辑、删除、发布是否正常,服务列表注册、下线、详情查看是否正常。
客户端功能回归:至少用一套业务服务做注册、发现、订阅配置、配置变更推送的完整链路测试。最好在改造前后各跑一遍同样的脚本,对比结果,确认行为一致。
集群容灾回归:停掉一个Nacos节点,确认其他节点正常接管;恢复该节点,确认数据同步和集群状态自动收敛。
安全回归:检查鉴权是否开启,匿名访问Open API是否返回401或者403,默认密码是否已经修改,告警日志里有没有异常访问记录。
日志排查回归:把Nacos的系统日志、保兰德中间件的访问日志、JVM日志三个来源对齐,确认排查问题时能从一个入口找到完整的链路信息。这个对后续运维特别重要,以前内嵌Tomcat的时候日志集中在Nacos目录,现在分散在中间件和应用两层,不做归一化,出问题连翻日志都翻不完。
最后再分享一个实际操作中的体会
这次改造做完,我最深的感受是“换容器不是换壳”。虽然Nacos核心功能完全没动,war包跑在保兰德里表现也稳定,但部署形态一变,运维方式、排查思路、日志入口全变了。以前遇到问题先看Nacos日志,现在得先判断是应用层问题还是中间件层问题,再决定去哪翻日志。这个思维切换,比技术改动本身更需要时间来适应。
给即将做类似改造的团队一个建议:测试环境一定要先做一轮完整的“中间件替换+数据库切换+鉴权开启”组合验证,不要逐个上线。分开做每一项都挺顺利,合并一起做的时候问题才会暴露,而这些暴露的时机最好是测试阶段,而不是生产割接的时候。
