ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Nacos启动失败排查:嵌入式Tomcat报错与安全加固

Nacos启动失败排查:嵌入式Tomcat报错与安全加固 Nacos 启动直接给你抛出一句 “Unable to start embedded Tomcat”紧接着一个APPLICATION FAILED TO START那一刻的心情我是完全理解的。这种报错在 Nacos 1.x 时代几乎是必踩的坑凡是自己装过单机版、或者用 IDEA 跑过源码的人十有八九都见过它。这个报错表面上说的是“内嵌 Tomcat 启动不了”但背后真正的原因五花八门——端口被占、环境变量捣乱、JDK 版本不匹配、甚至磁盘满了都能把它触发出来。这篇文章我就结合自己这些年踩过的坑把这句报错的来龙去脉、排查思路、修复方案一次性讲清楚顺便把 Nacos 装好之后必须做的安全加固也一起交代了。1. 先看懂报错Tomcat 启动失败的底层逻辑1.1 报错信息里到底藏了什么很多人第一次看到这个报错下意识就跑去查“embedded Tomcat”怎么修其实方向就偏了。那一大段红色日志里真正有用的信息往往藏在最后的Action或Caused by部分。我贴一个最典型的完整报错栈*************************** APPLICATION FAILED TO START *************************** Description: Unable to start embedded Tomcat Action: Port 8848 was already in use.如果你看到的日志跟我这个类似那基本不用怀疑就是端口冲突。但如果你只看到Unable to start embedded Tomcat后面没有Port ... was already in use那问题就复杂了可能是连接器初始化失败、临时目录写不进去、或者某个 Bean 加载异常导致上下文启动中断。所以第一步永远是把日志往下翻找到 Caused by 那几行而不是停在第一句报错上。1.2 为什么 Nacos 要内嵌 Tomcat要理解这个报错先得搞清楚 Nacos 和 Tomcat 的关系。Nacos 1.x 版本本质上是一个 Spring Boot 应用而 Spring Boot 默认的 Web 容器就是内嵌 Tomcat。也就是说你启动 Nacos 的时候它并没有单独依赖一个外部 Tomcat而是把 Tomcat 的 jar 包直接打包进了自身进程里。Nacos 2.x 在正式版里换成了自研的 HTTP 服务很多老项目却还在用 1.4.x 或 2.0.x 系列所以这个报错在社区里一直没断过。理解了这层关系你就能明白这句报错其实是 Spring Boot 的通用报错不是 Nacos 的专属 bug排查思路完全可以借鉴到其他 Spring Boot 应用中。2. 最高频原因端口被占用2.1 怎么快速验证 8848 端口被占用端口占用在 Windows、Linux、macOS 上我都遇到过排查命令略有差异。先看你所在的系统然后直接执行对应的检查命令Linux 系统用下面的命令# 查看 8848 端口被哪个进程占用 lsof -i:8848 # 如果没有 lsof用 netstat 也一样 netstat -tunlp | grep 8848Windows 系统用这个# 查看 8848 端口被哪个进程占用 netstat -ano | findstr 8848 # 根据上面显示的 PID 去查具体进程名 tasklist | findstr PIDmacOS 和 Linux 类似但 lsof 的参数稍微不同lsof -iTCP:8848 -sTCP:LISTEN如果你查出来端口已经被一个 Java 进程占住了大概率就是之前启动的 Nacos 没有关干净。在 Linux 上我见过太多次这种情况nohup启动 Nacos 后直接关掉终端窗口然后重新启动结果第一次的进程还活着白白占着端口。这时候用kill -9 PID把旧进程杀掉再启动就行。2.2 端口没被占用但依然报端口冲突还有一种更隐蔽的情况——你查 8848 端口明明没被占用重启 Nacos 却依然报 “Port 8848 was already in use”。我第一次遇到时也愣了挺久后来排查发现是Spring Boot 读取的端口根本不是 application.properties 里写的那个 8848。比如你在 IDEA 里启动 Nacos 源码时IDEA 的运行配置里有一个Environment variables里面可能设置了SERVER_PORT8080或者其他值。环境变量在 Spring Boot 的配置优先级里比 application.properties 高所以 Nacos 实际绑定的是 8080而 8080 恰好被你后端的其他服务占着报错就来了。2.3 换端口启动是不是好办法有些教程会告诉你直接把端口改成 8849 绕过冲突这只能应急不推荐作为长期方案。因为注册到 Nacos 的客户端配置里默认写的是 8848你改了服务端端口客户端全都要跟着改改漏一个就是“服务注册不上”的诡异问题。正确的做法是揪出占用端口的进程解决冲突源头。如果确实因为业务需要必须换端口记得同时修改application.properties里的server.port、客户端application.yml里的server-addr还有 Nacos 控制台访问地址三个地方保持一致。3. 环境变量和 JVM 参数看不见的黑手3.1 server.port 真正的读取顺序Nacos 的端口配置Spring Boot 有一套严谨的优先级顺序从上到下依次是命令行参数 环境变量 application.yml/properties 默认配置。这意味着你明明在 application.properties 里写了server.port8848但如果系统环境变量里有SERVER_PORT8849那么 Nacos 实际启动时会用 8849 而不是 8848。很多人在 Linux 服务器上搞了半天端口冲突最后发现是/etc/profile或者.bashrc里不知道什么时候设过SERVER_PORT这种变量一查一个准。排查方法也简单启动前先打印一下环境变量env | grep -i server如果有输出而且不是预期值就是问题所在。解决方式是启动时显式指定# Linux 启动时强制指定端口忽略环境变量 export SERVER_PORT sh startup.sh -m standalone或者在 IDEA 的 VM options 里加上-Dserver.port88483.2 JVM 内存参数过小也会打不开 Tomcat还有一种情况报错日志里出现了Unable to start embedded Tomcat背后原因居然是 OOM。Nacos 默认的startup.sh里设置了-Xms和-Xmx1.x 版本默认给到 2G。如果机器物理内存比较小或者你在 IDEA 里跑源码时把 VM options 里的-Xmx调成了 256MTomcat 初始化线程池的时候就可能直接内存溢出。这种情况看日志结尾会有OutOfMemoryError或者unable to create native thread的关键字。解决方式很简单给足内存。单机测试给 1G 就够但如果你在本地同时跑好几个微服务建议单独给 Nacos 开 2Gjava -Xms2g -Xmx2g -Dnacos.standalonetrue -jar nacos-server.jar注意不要盲目调大-Xmx要结合服务器实际内存来。本来只有 2G 内存的云服务器你给它 2G 堆外挂一堆线程系统直接卡死。3.3 时区设置和字符集也会导致启动失败这个坑比较冷门但确实遇到过。有台服务器时区没设好Nacos 启动时做时间相关初始化然后抛了异常连带 Tomcat 一起启动失败。Linux 上用date看下时间顺便检查locale字符集是不是UTF-8。如果系统默认不是 UTF-8Spring Boot 读取配置文件时可能出现乱码导致解析失败。这个解决方法不复杂在startup.sh开头手动指定export TZAsia/Shanghai export LANGzh_CN.UTF-84. 总是被忽略的隐蔽原因4.1 磁盘写满或者权限不够Tomcat 启动的时候要在临时目录写文件和日志如果磁盘满了或者运行 Nacos 的用户对logs、temp、work目录没有写权限也会报这个错。我遇到过一次挺尴尬的情况服务器上一个大文件把根分区占满了df -h一看使用率 100%所有 Java 服务全启动不了报错五花八门其中一个就是Unable to start embedded Tomcat。排查和解决# 查看磁盘使用情况 df -h # 给 Nacos 目录正确的写权限 chown -R nacos:nacos /opt/nacos chmod -R 755 /opt/nacos/bin启动 Nacos 的用户也很重要。如果你是 root就用 root 启动不要用普通用户去启动然后遇到权限错误。生产环境建议单独建一个nacos用户把 Nacos 目录 owner 设置成它这样既避免 root 权限过大也防止日志文件权限混乱。4.2 JDK 版本不匹配Nacos 1.x 理论上要求 JDK 8 以上2.x 建议 JDK 8 或 11。如果你用的是 JDK 17 甚至更高版本某些老版本 Nacos 编译产物里依赖的反射逻辑在新 JDK 下可能出问题。我记得有阵子 JDK 17 刚流行的时候很多人启动 Nacos 报类似错误最后把 JDK 切回 8 就正常了。判断 JDK 版本很简单java -version如果版本太高强烈建议换 JDK 8。Nacos 本身不是追求极致性能的应用用稳定版本比追新更重要。我在生产环境一直跑的是 JDK 8配合 Nacos 1.4.x非常稳。4.3 二进制包文件损坏另一类情况不常见但真实存在Nacos 压缩包下载不完整解压后启动就异常。检查方法很直接对比官方 sha 校验值或者直接重新下载一次。这种事情在网速不稳定或者代理工具截断下载时偶发。还见过有人直接把 Nacos 源码构建到一半停掉然后拿一个残缺的 target 目录去跑这种问题排查起来最费时间因为你根本不知道哪个 class 有问题。5. 完整的排查手册与一键定位思路5.1 照着这个顺序排基本都能解决我自己在实际排障时有一个固定顺序处理这个报错非常高效步骤排查项目命令或位置1报错根因logs/start.out和logs/nacos.log末尾的 Caused by2端口占用lsof -i:8848或 netstat -ano3环境变量env4内存与 JDKjava -version、启动参数里的 Xmx5磁盘与权限df -h、Nacos 目录属主与写权限6配置文件application.properties端口、鉴权、上下文路径我强烈建议先用第一步看 Caused by很多人跳过了这一步直接改配置结果方向错了折腾半天。日志是机器留给你的答案先信日志。5.2 Linux 下的快速定位脚本这里分享一个我常用的脚本片段。当你快速重启 Nacos 且不想重复敲命令时可以把它存成一个restart_nacos.sh#!/bin/bash # 强制释放 8848 端口 PID$(lsof -ti:8848) if [ -n $PID ]; then echo kill old process: $PID kill -9 $PID fi # 启动 Nacos 单机版 /opt/nacos/bin/startup.sh -m standalone # 等待并查看启动日志 tail -f /opt/nacos/logs/start.out注意lsof -ti:8848这个参数直接只输出 PID不用再用awk去抓了非常方便。杀旧进程之前先确认一下 PID 对应的进程确实是 Nacos别误杀别的 Java 服务。5.3 Docker 部署时的特殊排查用 Docker Compose 部署 Nacos 时问题形态又不太一样。容器里报Unable to start embedded Tomcat往往是端口映射冲突或者容器内存限制太小。Docker 场景下先看容器状态# 查看容器日志 docker logs nacos # 查看容器内存限制 docker inspect nacos | grep -i memoryCompose 配置里常见的问题是把8848和9848的映射搞反了。Nacos 2.x 的 gRPC 端口是 8848 1000 9848映射的时候必须两个都映射出去不然客户端连不上服务端服务端自己日志也怪。但如果你用的是 Nacos 1.x那就只有 8848没有 9848别配多了。还有一种常见情况是容器内NACOS_AUTH_ENABLEtrue被设置了但密钥没配启动时鉴权模块抛异常也表现为启动失败。6. 解决启动之后的加固别让 Nacos 裸奔6.1 未授权访问不是危言耸听热搜词里有一条 “nacos namespaces 未授权访问漏洞【原理扫描】”这绝对值得你重视。Nacos 1.x 的旧版本默认不开启鉴权也就是说只要 8848 端口暴露到公网任何人都能通过访问控制台管理你的服务列表和配置。轻则被乱改配置重则配置里有数据库账号密码直接就被脱库。这些攻击案例在网络安全通报里屡见不鲜。所以在 Nacos 能正常启动之后第一件事就是开鉴权。Nacos 1.4.x 开启鉴权的方式是修改application.properties# 开启鉴权 nacos.core.auth.enabledtrue # 设置密钥不能使用默认值 nacos.core.auth.plugin.nacos.token.secret.key你的自定义Base64密钥 # 开启服务身份标识 nacos.core.auth.server.identity.keyyour_identity_key nacos.core.auth.server.identity.valueyour_identity_value这个密钥不是随便填个字符串就行需要是 Base64 格式而且长度要够。你可以用以下命令生成echo -n 你的任意长度的密钥 | base64生成出来的一串字符粘贴到配置文件里。这个步骤做完重启 Nacos再访问控制台就会要求登录了。默认账号是nacos/nacos登录进去以后建议立刻改掉。6.2 生产环境推荐的几个配置针对刚刚解决完启动问题的场景我再多啰嗦几句生产环境的最佳实践。首先是启动模式单机测试用-m standalone生产环境多节点建议用集群模式不要为了省事把单机当生产用。其次是配置文件里的几个关键项# 开启自动注册与健康检查默认就开着 nacos.naming.autoRegister.enabledtrue # 日志保留时间与大小避免磁盘撑爆 server.tomcat.accesslog.enabledtrue server.tomcat.accesslog.max-days7 # 限制控制台来源 IP尽量只允许内网访问落地上最实在的两条是防火墙只放行内网 IP 访问 8848以及业务服务器只暴露业务端口不暴露 Nacos 管理端口。如果你用云服务器安全组规则里把 8848 的来源限制成自己公司的出口 IP 段即可。6.3 健康检查与自愈启动问题解决后配合一个简单的健康检查脚本能让运维省心很多。Nacos 启动成功之后会暴露一个健康检查接口。通过 HTTP 请求就能判断应用是否活着# 返回 200 说明健康 curl -s -o /dev/null -w %{http_code} http://127.0.0.1:8848/nacos/v1/console/health/readiness你可以把这个请求写进定时任务如果连续几次返回非 200就自动重启 Nacos。这种方式在裸机部署场景下很实用不需要额外引入监控平台。生产环境我也见过配合 Prometheus 加告警规则的方案但如果你只有三五台机器手写一个 20 行的 shell 脚本足够了。我踩过几次坑之后的小结说实话“Unable to start embedded Tomcat” 这个报错第一次遇到确实让人烦躁因为它看起来像是 Tomcat 本身出毛病实际上 90% 的情况是端口或者环境的问题。我现在排查这个报错的习惯已经固化成肌肉记忆了先看 Caused by再查端口然后看环境变量最后才怀疑配置。排序按概率来不会错。大家在一开始部署 Nacos 的时候最好就把生产环境的配置模板整理好把鉴权、端口、JVM 参数、日志路径一次配齐省得到处救火。最后再说一个实用小技巧——启动 Nacos 之后别急着关终端先看logs/start.out里有没有出现 “Nacos started successfully” 这句话它是真正意义上的启动成功标志比看 Tomcat 日志靠谱多了。
返回列表