ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Linux下Tomcat开机自启:systemd与init脚本实战指南

2026/9/16 2:50:59 拓冰建站 浏览量
Linux下Tomcat开机自启:systemd与init脚本实战指南 1. 方案选型Linux 下让 Tomcat 随系统启动的三条路先说说这个问题的来龙去脉。很多朋友在本地开发时习惯手动敲startup.sh启动 Tomcat但一旦上了生产服务器或者搭了一台长期跑测试环境的机器手动启动就不现实了。服务器重启一次不可能每次都搬个凳子坐终端前面敲命令也没人愿意半夜收到“服务没起来”的告警电话。所以把 Tomcat 做成开机自启是 Linux 运维里非常基础、也非常刚需的一步。在动手之前得先明确一个概念Linux 下实现“开机启动”的机制不止一种常见的方案有三条路分别是 systemd 服务、System V init 脚本、以及 crontab 的 reboot 技巧。这三条路没有绝对的好坏关键看你机器上的系统版本和初始化系统是什么。先简单说一下怎么判断你的系统用的是哪一套。执行ps -p 1 -o comm如果输出是systemd那你这台机器走的就是 systemd 路线这也是目前 CentOS 7、Ubuntu 16.04、Debian 8 等主流发行版的默认选择。如果输出是init或者sysvinit那就是传统的 System V init 风格多见于 CentOS 6 或更老的环境。顺带说一句现在网络上大量教程还在教 CentOS 6 时代的写法照着抄到新系统上大概率不生效这是很多新手掉坑的第一站。我的建议很直接新系统优先用 systemd老系统乖乖写 init 脚本。至于 crontab 的 reboot只适合临时救急或者跑一些不重要的辅助进程用它来托 Tomcat 这种核心服务体验比较粗糙依赖检查、崩溃重启、日志管理都跟不上。所以我把比重放在前两条路上第三条路作为备选方案简单提一下。2. 环境准备搞清楚 JDK 和 Tomcat 的“真实位置”配置开机启动之前你得先把环境信息摸清楚。这不是走形式因为后面写服务脚本时路径写错是导致启动失败的最常见原因。我见过有人把JAVA_HOME配错systemd 服务启动后一直报“找不到 java”折腾了半小时才发现是软链路径和实际路径对不上。2.1 确认 JDK 安装路径JDK 的路径不能靠猜要实际查。先用which java找到 java 命令的位置再用readlink -f一层层解开软链接拿到最真实的路径。比如which java /usr/bin/java readlink -f /usr/bin/java /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.262.b10-0.el7_8.x86_64/bin/java这时候JAVA_HOME就应该是/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.262.b10-0.el7_8.x86_64这是去掉末尾bin/java之后的目录。有些教程会告诉你可以写成/usr/lib/jvm/java-1.8.0如果你的机器上配置了 alternatives 机制这样做也没问题但最稳妥的还是把完整路径写死降低未来环境变动带来的风险。2.2 确认 Tomcat 目录结构与启动脚本Tomcat 的路径同样要确认。假设你解压后放在了/opt/tomcat9那标准的目录结构里必须能看到bin、conf、webapps这些子目录。重点检查bin/catalina.sh是否存在且有执行权限ls -l /opt/tomcat9/bin/catalina.sh -rwxr-xr-x 1 root root 11020 Mar 3 15:36 /opt/tomcat9/bin/catalina.sh如果没有执行权限先chmod x加上。另外我强烈建议你先把 Tomcat 手动启动一遍确认它本身是健康的再去做开机启动。如果在手动模式下服务都起不来配置完自启再去排查问题层面就混在一起了很难定位。2.3 独立账号运行一个被忽视的安全习惯还有一个容易忽略的点Tomcat 跑在哪个用户下。很多人的做法是直接root启动图省事。但如果服务器被扫到漏洞Tomcat 进程权限是 root攻击者一旦拿下 webshell 就等同于拿下整台机器。安全一点的常规做法是单独建一个系统账号比如tomcat然后让 Tomcat 跑在这个低权限账号下。这一步在配置自启时顺手就能做值得养成习惯。useradd -r -s /sbin/nologin tomcat chown -R tomcat:tomcat /opt/tomcat9-r表示创建系统账号-s /sbin/nologin表示这个账号不能用于登录 Shell只能作为进程运行身份。这个做法在写 init 脚本时尤其有用因为脚本里可以用su - tomcat来切换身份执行。3. 核心实操systemd 方式配置 Tomcat 开机自启如果你用的是 CentOS 7、Ubuntu 18.04、Debian 10 这类现代发行版systemd 就是最正规、最推荐的做法。它的好处在于能声明服务的依赖关系比如等网络就绪后再启动 Tomcat能自动处理进程崩溃后的重启逻辑还能把 Tomcat 的标准输出交给 journald 统一管理排查日志特别方便。3.1 编写 unit 文件逐行拆解关键参数在/etc/systemd/system/目录下新建一个名为tomcat.service的文件。这里直接给出一份我实测可用的配置模板vi /etc/systemd/system/tomcat.service文件内容如下注意每个参数的含义我都会在后面说明[Unit] DescriptionApache Tomcat 9 Web Application Container Afternetwork.target Wantsnetwork.target [Service] Typeforking EnvironmentJAVA_HOME/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.262.b10-0.el7_8.x86_64 EnvironmentCATALINA_HOME/opt/tomcat9 EnvironmentCATALINA_BASE/opt/tomcat9 EnvironmentCATALINA_PID/opt/tomcat9/tomcat.pid EnvironmentCATALINA_OPTS-Xms512M -Xmx1024M -server ExecStart/opt/tomcat9/bin/startup.sh ExecStop/opt/tomcat9/bin/shutdown.sh Usertomcat Grouptomcat Restarton-failure RestartSec10 [Install] WantedBymulti-user.target下面对关键配置项做一个拆解这是很多教程一笔带过但实际最容易出问题的地方。Typeforking是整个配置的核心。它告诉 systemd这个服务会通过 fork 的方式在后台运行启动命令startup.sh执行后立刻返回真正的 Tomcat 进程是它的子进程而且父进程会退出。systemd 会监听并记住这个 fork 出来的主进程 PID后续的停止、状态查询都以这个 PID 为准。如果你设成Typesimplesystemd 会认为startup.sh本身就是要常驻的前台进程但 Tomcat 的startup.sh实际是 fork 后立即退出的这样 systemd 会觉得服务已经挂了就会反复重启或者报错。这个坑非常常见网上大量教程都没说清楚。CATALINA_PID很关键但是很多人不写。它指定了 PID 文件的路径。Tomcat 的catalina.sh会根据这个环境变量在启动时把主进程 PID 写入指定文件。systemd 的 kill 逻辑和状态监控都要依赖这个 PID 文件没有它的话systemctl stop时可能找不到精确的进程会导致残留。Usertomcat和Grouptomcat是身份切换的关键。systemd 会在执行 ExecStart 前自动把进程身份切换到这个账号脚本内就不需要再手写su了。这也简化了 unit 文件的复杂度。Restarton-failure和RestartSec10是可靠性配置。意思是如果服务以非零状态码退出systemd 会在 10 秒后自动重新拉起它。这个参数在服务器上非常有用进程被 OOM killer 误杀或者 JVM 崩溃时能做到秒级自愈。WantedBymulti-user.target决定了服务在哪个运行级别下自启。multi-user.target 对应传统的运行级别 3即多用户命令行模式这是服务器最常见的运行模式没有图形界面也属于这一层。3.2 加载配置并验证语法写好文件后先执行daemon-reload让 systemd 重新加载配置。然后建议先start启动一次观察状态。完整的命令序列是systemctl daemon-reload systemctl start tomcat systemctl status tomcat第一次启动时我建议盯着状态输出看几秒。如果看到状态是active (running)并且能查到主进程 PID说明配置成功了一大半。如果有报错journal 日志里会有具体原因排查思路我会在后面的常见问题里详细写。3.3 设置开机自启并验证确认能正常启动和停止后执行systemctl enable tomcat这条命令会在/etc/systemd/system/multi-user.target.wants/目录下创建一个软链接指向你的 tomcat.service 文件。系统进入 multi-user.target 时systemd 会自动读取这个目录拉起服务。验证一下自启是否真的生效可以执行systemctl is-enabled tomcat输出enabled就说明配置到位了。如果你想检验完整链路可以reboot重启机器然后用systemctl status tomcat确认服务状态。这里有个小建议重启前先看一眼journalctl -u tomcat的启动日志排除潜在问题避免重启后发现服务异常又得二次排查。3.4 systemd 方式的优势复盘用 systemd 方式做开机自启最直观的好处是管理命令高度统一。你不再需要记startup.sh和shutdown.sh只需要记systemctl start tomcat、systemctl stop tomcat、systemctl restart tomcat。而且服务崩了能自愈这比手动启动要省心得多。另外日志管理也方便。Tomcat 的标准输出会通过 journald 汇集执行journalctl -u tomcat -f就能实时跟踪日志还可以用journalctl -u tomcat --since 10 minutes ago按时间过滤这对排错是实打实的效率提升。4. 折中方案System V init 脚本写法老系统专用虽然现在绝大多数新机器都是 systemd但你还是可能会碰到 CentOS 6、Ubuntu 14.04 这类老环境或者在某个异构机房里有一批 legacy 服务器。这时候就得写 System V init 脚本把它放到/etc/init.d/tomcat并借助chkconfig来管理自启。4.1 一个简洁可用的 init 脚本模板老牌 init 脚本的基本套路是脚本接收 start、stop、restart、status 参数然后通过start-stop-daemon或直接调用catalina.sh来管理进程。这里给一个我在 CentOS 6 上验证过的模板vi /etc/init.d/tomcat#!/bin/bash # # tomcat: Apache Tomcat startup script # chkconfig: 2345 10 90 # description: Apache Tomcat web application container JAVA_HOME/usr/lib/jvm/java-1.8.0-openjdk CATALINA_HOME/opt/tomcat9 CATALINA_BASE/opt/tomcat9 TOMCAT_USERtomcat TOMCAT_PID/opt/tomcat9/tomcat.pid export JAVA_HOME CATALINA_HOME CATALINA_BASE case $1 in start) echo Starting Tomcat... if [ -f $TOMCAT_PID ]; then echo Tomcat is already running (PID $(cat $TOMCAT_PID)). else su -s /bin/bash $TOMCAT_USER -c $CATALINA_HOME/bin/startup.sh fi ;; stop) echo Stopping Tomcat... if [ -f $TOMCAT_PID ]; then su -s /bin/bash $TOMCAT_USER -c $CATALINA_HOME/bin/shutdown.sh else echo Tomcat is not running. fi ;; restart) $0 stop sleep 2 $0 start ;; status) if [ -f $TOMCAT_PID ]; then echo Tomcat running with PID $(cat $TOMCAT_PID). else echo Tomcat is not running. fi ;; *) echo Usage: $0 {start|stop|restart|status} exit 1 ;; esac exit 0写完别忘了加执行权限chmod x /etc/init.d/tomcat4.2 理解脚本里的三个关键细节第一个细节是# chkconfig: 2345 10 90这行注释。它的意思是运行级别 2、3、4、5 下启用这个服务启动顺序号是 10停止顺序号是 90。顺序号决定了系统启动时服务被拉起的前后次序数字小先启动数字大后启动。Tomcat 依赖网络所以启动顺序不能太靠前10 这个数字是合理的停止时顺序靠后确保业务先停干净。第二个细节是su -s /bin/bash $TOMCAT_USER -c ...。因为系统账号限制了 shell直接su tomcat会失败用-s /bin/bash显式指定 shell 就能绕过这个限制。另外要强调别用su $TOMCAT_USER却不加-少一个横线环境和当前用户完全不一样容易出怪问题。第三个细节是 PID 文件判断。start 前检查 PID 文件是否存在能避免重复启动导致端口冲突stop 时同样靠 PID 文件做判断。对于老 init 风格来说这种简单的状态管理已经足够了。4.3 用 chkconfig 注册自启在 CentOS 6 或类似的 SysV 风格系统上执行chkconfig --add tomcat chkconfig --level 2345 tomcat on第一条是注册服务第二条是设置指定运行级别下的自启开关。验证可以执行chkconfig --list tomcat输出里 2、3、4、5 状态为 on就说明配置成功。Ubuntu 14.04 那种用 upstart 或者 sysvinit 的环境注册命令略有差异部分版本是update-rc.d tomcat defaults本质逻辑相同按发行版习惯来就行。4.4 init 脚本方式的适用边界我必须说清楚System V init 脚本的方式在新系统上不是不能用但优先级应该排在 systemd 之后。原因很简单新系统上 systemd 对进程生命周期管理更严格init 脚本里那套 PID 判断逻辑其实很脆弱如果 PID 文件残留或者进程僵死容易造成误判。但在老系统上它仍然是唯一正规的自启方式所以这个技能还是值得掌握的。5. 备用技巧crontab reboot 实现简单自启如果只是临时救急或者你的服务对启动时机、崩溃恢复不太敏感可以借 crontab 的 reboot 机制来实现自启。在 root 用户的 crontab 里加一行即可crontab -ereboot /opt/tomcat9/bin/startup.sh这样系统每次启动后cron 守护进程会自动执行这行命令。如果跑这个服务的用户是 tomcat也可以用reboot su -s /bin/bash tomcat -c ...的形式。这个方案的最大问题是没有服务管理概念不记录 PID 状态崩溃后不会自动拉起日志和 systemd 也不在一个体系里。所以我的定位很清楚应急可以用长期不推荐。6. 常见问题与排查实录配置开机自启的过程中我几乎每一次都能碰到几个固定问题。这里我把出现频率最高的几个整理出来并附上排查思路帮你减少踩坑时间。现象可能原因排查与解决startup.sh 无法执行脚本没有执行权限chmod x /opt/tomcat9/bin/*.sh服务启动后瞬间消失Type 设置不正确确认 unit 中 Typeforking且设置了 CATALINA_PID报错找不到 JAVA_HOME环境变量未写入服务脚本在 unit 文件或 init 脚本中显式 export JAVA_HOMEsystemctl enable 提示文件不存在unit 文件路径写错确认放在 /etc/systemd/system/ 下文件名以 .service 结尾enable 后重启未生效未创建多用户目标关联检查systemctl is-enabled必要时手动 link端口 8080 被占用手动启动的实例未关闭ps -ef服务启动但访问 404webapps 目录或部署包异常检查 /opt/tomcat9/webapps 内容和 catalina.out 日志6.1 systemd 服务启动失败并提示“找不到 java”这个问题的根源是 systemd 的 unit 文件属于系统服务级别环境变量不会继承你在/etc/profile里设置的 JAVA_HOME。Tomcat 的catalina.sh脚本会检查 JAVA_HOME拿不到就报错。对策就是我在 3.1 节里展示的那样把 Environment 写进 unit 文件里让 systemd 启动环境里自带这些变量不依赖用户 shell 环境。6.2 enable 成功但重启后服务没有自动拉起这种情况多半是 WantedBy 配置有误或者目标目录关系没建立。先用systemctl is-enabled tomcat确认状态如果是 disabled重新执行systemctl enable tomcat。如果是 enabled 但重启不生效检查/etc/systemd/system/multi-user.target.wants/tomcat.service这个软链接是否存在。某些情况下手动编辑文件时路径写错会导致链接指向无效位置删掉重建就行。6.3 Tomcat 停止时进程残留这个问题在 systemd 下其实比较少见因为 systemd 有完整的 cgroup 管理但如果你用 init 脚本残留概率会明显提升。我的经验是停止时先看 PID 文件是否真的被删除如果shutdown.sh执行后进程还在最直接的办法是用kill精确指定 PID。严格的业务场景下建议在 init 脚本的 stop 分支加一层超时判断超时后强制 kill避免进程常驻。6.4 端口被占用导致启动失败这是我见过最多的问题通常是你手动启动过 Tomcat然后又执行systemctl start新旧实例抢同一个端口。排查时用ps -ef | grep tomcat先看进程再确认端口占用netstat -tlnp | grep 8080如果确认是残留的 Java 进程占用先杀掉再启动服务。这个细节虽然简单但实际运维中很多人会卡在这一步因为报错信息不够直观。7. 最后一点心得配置文件备份与异常演练关于 Tomcat 开机启动我个人在实际操作中有一个习惯每配完一台机器会把 unit 文件或 init 脚本复制一份到机器外备份比如 Git 仓库或者跳板机目录。别以为这个动作多余“这台机器是从另一台机器克隆配置的”这种话我听太多次了结果真到新机器上路径和账号一对不上全是坑。有备份意味着你能快速 diff定位差异。同时我还建议配完之后做一次重启演练。找一个维护窗口执行reboot然后等机器起来验证 Tomcat 状态是否是 active。这比在生产上第一次重启时才发现问题要好得多。实际经历中我有一次就是自信满满以为配置没问题结果重启后服务没起最后发现是 PID 目录没写权限这种细节不演练根本暴露不了。另外补充一点如果你的服务器上不止一个 Java 服务建议仿照 Tomcat 的思路为每个服务单独建 unit命名可以叫app-xxx.service不要全堆在一个脚本里。否则重启时启动顺序一乱服务之间互相依赖但彼此没等齐就会产生一堆奇奇怪怪的问题。一句话总结就是自启配置不是写一个文件那么简单它是一整套“环境、权限、依赖、验证”的组合拳每一步都稳扎稳打才能换来长期省心。