ARTICLE DETAIL

资讯详情

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

Linux下Tomcat开机自启动:init.d脚本与systemd方案深度对比与实践

Linux下Tomcat开机自启动:init.d脚本与systemd方案深度对比与实践 1. 项目概述与核心价值在Linux服务器运维的日常工作中确保关键服务在服务器重启后能自动恢复是一项基础但至关重要的任务。Tomcat作为广泛使用的Java Web应用服务器其开机自启动的配置是每个运维人员和开发者迟早要面对的问题。你可能遇到过这样的场景半夜服务器机房断电重启早上接到紧急电话说应用全部无法访问登录服务器一看Tomcat根本没起来。手动启动虽然简单但依赖人工操作就意味着存在风险点和运维盲区。因此实现Tomcat服务的开机自动启动是将运维工作从“救火”转向“防火”的关键一步。网上关于Linux下Tomcat开机自启动的方案很多但质量参差不齐。有的教程只给命令不讲原理照做可能出错有的方案过于复杂引入了不必要的依赖还有的方案缺乏健壮性在真实生产环境中可能掉链子。今天我将结合自己多年的运维经验为你深入剖析两种主流方法一种是基于systemd的现代标准方案另一种是更为经典和通用的init.d脚本配合chkconfig的方案。正如标题所建议的我更倾向于推荐第二种方法原因会在后续详细拆解。无论你使用的是CentOS/RHEL 7、Ubuntu 16.04等主流发行版还是国产化的麒麟、统信UOS等基于Linux的体系这篇文章提供的思路和脚本都具有很强的参考价值。我们将不仅关注“怎么做”更会深究“为什么这么做”以及在实际操作中会遇到哪些“坑”。2. 两种开机自启动方案深度对比与选型在动手之前我们必须搞清楚有哪些路可以走以及每条路的优缺点。这决定了我们方案的长期稳定性和可维护性。2.1 方案一使用Systemd服务单元现代方式systemd是当前绝大多数Linux发行版默认的初始化系统和服务管理器。它为管理服务提供了强大的功能如依赖关系、日志集成、资源控制等。2.1.1 Systemd方案的工作原理其核心是为Tomcat创建一个服务单元文件通常以.service结尾并将其放置在/etc/systemd/system/目录下。这个文件定义了服务的启动、停止、重启等行为以及运行时的环境、用户权限等。然后通过systemctl enable命令在系统启动流程的适当阶段创建符号链接从而实现开机自启。2.1.2 Systemd方案的优缺点分析优点功能强大可以方便地设置资源限制CPU、内存、依赖其他服务、自动重启失败的服务等。日志统一服务输出的日志会被journald捕获使用journalctl -u tomcat.service即可查看与系统日志整合度高。状态管理清晰systemctl status命令能提供非常详尽的服务状态信息。缺点兼容性依赖要求系统必须使用systemd。虽然现在很普遍但在一些老旧的系统或某些特定裁剪过的环境如某些Docker基础镜像、嵌入式系统中可能不存在。配置相对复杂单元文件的语法需要学习对于只想简单实现自启动的用户来说有一定门槛。对传统脚本的适应性如果Tomcat本身通过一个复杂的shell脚本启动里面设置了复杂的环境变量、JAVA_OPTS等需要将这些逻辑正确地迁移或封装到Service文件的ExecStart指令中。2.2 方案二使用Init.d脚本与Chkconfig经典通用方式这是Linux系统传统的服务管理方式通过Shell脚本实现服务的生命周期管理并利用chkconfig或update-rc.d工具来管理运行级别。2.2.2 Init.d方案的工作原理我们编写一个符合LSBLinux Standard Base规范的Shell脚本这个脚本必须能响应start、stop、restart、status等标准参数。将该脚本放置在/etc/init.d/目录下并赋予可执行权限。然后使用chkconfig --add命令将该脚本添加到服务管理列表并设置其在指定运行级别如3, 5下自动启动。2.2.2 Init.d方案的优缺点与推荐理由优点极高的兼容性从古老的SysV init到现代的systemd系统都兼容这种方式。systemd会兼容并模拟init.d脚本的行为。这意味着同一套脚本几乎可以在任何Linux发行版上运行迁移成本极低。灵活性极高脚本完全由你控制可以用Shell实现任何复杂的启动前准备、环境检查、依赖服务等待等逻辑。它与Tomcat原有的catalina.sh启动脚本能无缝结合。直观易懂脚本内容就是标准的Shell命令对于运维人员来说可读性和可调试性更强。国产化适配友好许多国产操作系统虽然内核和基础库在演进但为了保持对历史应用和运维习惯的兼容对init.d的支持非常完善。采用此方案在技术路线切换时服务启动层面几乎无需改动。缺点功能不如systemd原生需要自己实现日志轮转、服务崩溃自动重启等高级功能当然也可以通过脚本或结合其他工具实现。管理命令不统一在纯systemd系统上你需要使用service tomcat start或/etc/init.d/tomcat start来操作而不是纯粹的systemctl。为什么我推荐第二种方法核心在于“兼容性”和“控制力”。在企业级运维中服务器的操作系统版本可能五花八门从CentOS 6到CentOS 8从Ubuntu 14.04到22.04还有各种国产OS。一个通用的init.d脚本能让你用同一套方法论应对所有环境减少环境差异带来的麻烦。其次Tomcat的启动往往不是简单的一条命令它涉及到JAVA_HOME、CATALINA_HOME、CATALINA_BASE、内存参数、GC参数、字符集、临时目录等一系列环境变量的设置。将这些逻辑封装在一个独立的Shell脚本里清晰且便于维护。当需要调整启动参数时你只需要修改这一个脚本文件。因此除非你的环境100%确定是较新的systemd系统且需要用到systemd的独占性高级功能否则init.d脚本是更稳健、更通用的选择。3. 核心脚本解析与实操要点接下来我们聚焦于第二种方案深入解读如何打造一个生产级可用的Tomcat自启动脚本。我将提供一个经过大量实践检验的脚本模板并逐行解释其关键点。3.1 Init.d脚本模板深度剖析以下脚本假设你的Tomcat安装在/opt/tomcat目录运行用户是tomcat。请根据你的实际环境修改。#!/bin/bash # # chkconfig: 2345 80 20 # description: Apache Tomcat 9.x Web Application Server # processname: tomcat # pidfile: /opt/tomcat/temp/tomcat.pid ### BEGIN INIT INFO # Provides: tomcat # Required-Start: $network $syslog # Required-Stop: $network $syslog # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: Apache Tomcat # Description: Apache Tomcat Web Application Server ### END INIT INFO # 1. 环境变量定义 - 这是脚本的核心必须正确配置 export JAVA_HOME/usr/lib/jvm/java-11-openjdk # 根据你的JDK路径修改 export CATALINA_HOME/opt/tomcat # Tomcat安装目录 export CATALINA_BASE/opt/tomcat # 通常与CATALINA_HOME相同分布式部署时不同 export CATALINA_PID$CATALINA_BASE/temp/tomcat.pid # 指定PID文件位置用于status和stop export CATALINA_OUT$CATALINA_BASE/logs/catalina.out # 标准输出日志 # 可以在此添加应用特定的JVM参数例如内存设置 export JAVA_OPTS-server -Xms1024m -Xmx2048m -XX:MetaspaceSize256m -XX:MaxMetaspaceSize512m # 2. 脚本用到的命令路径 TOMCAT_USERtomcat START_SCRIPT$CATALINA_HOME/bin/startup.sh STOP_SCRIPT$CATALINA_HOME/bin/shutdown.sh PROCESS_NAMEjava.*$CATALINA_HOME # 用于ps查找进程 # 3. 源代码函数获取Tomcat进程PID get_pid() { # 第一种方式从指定的PID文件读取 if [ -f $CATALINA_PID ]; then PID$(cat $CATALINA_PID) # 验证PID是否确实存在且是Tomcat进程 if ps -p $PID /dev/null 21; then echo $PID return 0 else # PID文件存在但进程已死清理无效PID文件 rm -f $CATALINA_PID fi fi # 第二种方式如果PID文件无效或不存在则通过ps命令查找 # 使用pgrep更精确但需确保进程名匹配。这里用ps组合grep是更通用的方法 PID$(ps -ef | grep $PROCESS_NAME | grep -v grep | awk {print $2} | head -n 1) if [ -n $PID ]; then echo $PID return 0 fi echo return 1 } # 4. LSB标准动作函数 start() { echo -n Starting Tomcat: pid$(get_pid) if [ -n $pid ]; then echo Tomcat is already running (pid$pid). return 1 fi # 关键步骤检查必要目录和权限 if [ ! -d $CATALINA_HOME ]; then echo ERROR: CATALINA_HOME ($CATALINA_HOME) does not exist. return 1 fi if [ ! -x $START_SCRIPT ]; then echo ERROR: Startup script ($START_SCRIPT) is not executable. return 1 fi # 以指定用户身份启动Tomcat。使用su或sudo取决于你的权限配置。 # 方式A如果当前是root且tomcat用户有权限 if [ $(id -u) 0 ]; then su - $TOMCAT_USER -c $START_SCRIPT /dev/null 21 # 方式B如果脚本本身将以tomcat用户执行通过sudo或环境已配置好 else $START_SCRIPT /dev/null 21 fi # 等待一段时间确认进程是否成功启动 sleep 5 pid$(get_pid) if [ -n $pid ]; then echo OK (pid$pid). # 将PID写入文件方便后续管理 echo $pid $CATALINA_PID return 0 else echo FAILED. Please check logs: $CATALINA_OUT return 1 fi } stop() { echo -n Stopping Tomcat: pid$(get_pid) if [ -z $pid ]; then echo Tomcat is not running. # 即使没进程也清理可能残留的PID文件 rm -f $CATALINA_PID return 0 fi # 先尝试优雅关闭 if [ $(id -u) 0 ]; then su - $TOMCAT_USER -c $STOP_SCRIPT /dev/null 21 else $STOP_SCRIPT /dev/null 21 fi # 等待最多30秒让进程优雅退出 for i in {1..30}; do pid$(get_pid) if [ -z $pid ]; then echo OK. rm -f $CATALINA_PID return 0 fi sleep 1 done # 如果优雅关闭失败强制杀死进程 echo -n Failed to stop gracefully, forcing kill... kill -9 $pid /dev/null 21 sleep 2 pid$(get_pid) if [ -z $pid ]; then echo OK (forced). rm -f $CATALINA_PID return 0 else echo FAILED to kill process $pid. return 1 fi } restart() { stop sleep 2 start } status() { pid$(get_pid) if [ -n $pid ]; then echo Tomcat is running (pid$pid). # 可以附加更多信息如进程运行时间 ps -p $pid -o etime | awk {print Uptime:, $0} return 0 else echo Tomcat is stopped. return 3 # LSB规范中3表示服务未运行 fi } # 5. 主逻辑根据传入的参数调用对应的函数 case $1 in start) start ;; stop) stop ;; restart) restart ;; status) status ;; *) # 打印标准用法提示 echo Usage: $0 {start|stop|restart|status} exit 1 ;; esac exit $?3.2 脚本关键点与避坑指南chkconfig和BEGIN INIT INFO注释块这两部分不是给Shell执行的而是给chkconfig、update-rc.d或systemd等工具读取的元数据。它们定义了服务在哪些运行级别启动(2345)、启动顺序(80)和关闭顺序(20)以及依赖关系。务必保留且根据实际情况微调。例如如果你的Tomcat依赖MySQL可以在Required-Start中添加$mysql。环境变量设置这是脚本的灵魂。JAVA_HOME必须绝对正确。CATALINA_PID的指定至关重要它是脚本管理进程的生命线。我强烈建议显式设置而不是依赖Tomcat的默认位置可能在/tmp下可能被清理。用户权限问题生产环境绝不应该以root用户直接运行Tomcat这有严重的安全风险。脚本中通过su - $TOMCAT_USER -c来切换用户执行。你需要确保系统上存在tomcat用户或你指定的其他用户。该用户对CATALINA_HOME目录及其下的logs、temp、work、webapps等目录有读写权限。如果使用非root用户执行脚本可能需要配置sudo规则允许该用户无需密码执行service tomcat命令。get_pid函数的健壮性这个函数是status、stop、start检查是否已运行的基础。它采用了“PID文件优先ps查找兜底”的双重机制能有效处理PID文件残留或丢失的情况。ps -ef | grep的匹配模式“java.*$CATALINA_HOME”需要根据你的环境调整确保能唯一匹配到你的Tomcat进程避免误杀其他Java应用。停止逻辑的耐心与果断stop函数先尝试优雅关闭shutdown.sh等待30秒。如果失败再发送SIGKILL (kill -9)强制终止。这种“先礼后兵”的策略能保证应用有足够时间处理完当前请求和保存状态避免数据损坏。4. 实操部署与系统集成全流程有了脚本我们还需要正确地将其部署到系统中并完成开机自启动的配置。4.1 脚本部署与权限设置假设你将上述脚本内容保存为tomcat。复制脚本到init.d目录sudo cp tomcat /etc/init.d/设置脚本权限脚本必须可执行。sudo chmod x /etc/init.d/tomcat创建Tomcat专用用户如果不存在sudo groupadd tomcat sudo useradd -s /bin/false -g tomcat -d /opt/tomcat tomcat # 禁止登录shell修正目录所有权将Tomcat安装目录及其子目录的所有权赋予tomcat用户。sudo chown -R tomcat:tomcat /opt/tomcat sudo chmod -R urwX,grX,o-rwx /opt/tomcat # 设置合理的目录权限4.2 使用Chkconfig配置开机自启在RHEL/CentOS/OEL等使用chkconfig的系统上添加服务到chkconfig管理列表sudo chkconfig --add tomcat这个命令会读取脚本头部的# chkconfig:行将服务配置到相应的运行级别。设置服务开机自启sudo chkconfig tomcat onon表示在脚本头定义的默认运行级别2345下启用。你也可以用chkconfig --level 35 tomcat on只针对运行级别3和5启用。验证配置sudo chkconfig --list tomcat输出应类似tomcat 0:off 1:off 2:on 3:on 4:on 5:on 6:off这表明在运行级别2、3、4、5下Tomcat服务会自动启动。4.3 在Systemd系统上的兼容性操作如果你的系统是Ubuntu 16.04、CentOS 7等使用systemd的系统/etc/init.d/脚本仍然有效。systemd提供了兼容层。使用systemctl管理传统脚本 你可以直接使用systemctl命令来操作init.d脚本。sudo systemctl start tomcat # 启动 sudo systemctl stop tomcat # 停止 sudo systemctl status tomcat # 状态 sudo systemctl restart tomcat # 重启 sudo systemctl enable tomcat # 启用开机自启关键 sudo systemctl disable tomcat # 禁用开机自启执行systemctl enable tomcat后systemd会在/etc/systemd/system/下的相关target目录中创建指向/etc/init.d/tomcat的符号链接实现自启动。验证systemd的启用状态sudo systemctl is-enabled tomcat应返回enabled。重要提示在systemd系统上虽然可以用service tomcat start但更推荐统一使用systemctl命令因为它能提供更详细的状态信息和日志集成。systemctl enable是配置开机启动的关键命令。4.4 测试服务管理功能配置完成后务必进行完整测试手动启动测试sudo systemctl start tomcat # 或 sudo service tomcat start sudo systemctl status tomcat检查状态是否为active (running)并查看是否有错误日志。检查进程和端口ps -ef | grep java sudo netstat -tlnp | grep :8080 # 默认端口8080停止测试sudo systemctl stop tomcat sudo systemctl status tomcat # 应显示 inactive (dead)重启测试sudo systemctl restart tomcat最终极测试服务器重启 在测试环境中执行sudo reboot重启服务器。重启后等待系统完全启动然后直接检查Tomcat进程和端口是否已经自动拉起。这是验证开机自启是否成功的唯一标准。5. 常见问题与排查技巧实录即使按照步骤操作你也可能会遇到一些问题。这里记录了几个最常见的“坑”及其解决方案。5.1 服务启动失败权限不足现象执行start命令后很快失败status显示未运行查看catalina.out日志发现Permission denied或Cannot create directory等错误。排查检查/opt/tomcat及其子目录尤其是logs,temp,work,webapps的所有者和权限。确保tomcat用户有读写权限。检查CATALINA_PID指定的目录如/opt/tomcat/temp是否存在tomcat用户是否有写权限。如果使用su - tomcat -c方式确保root用户可以切换到tomcat用户检查/etc/passwd和/etc/shadow。解决sudo chown -R tomcat:tomcat /opt/tomcat sudo chmod -R urwX,grX,o-rwx /opt/tomcat sudo mkdir -p /opt/tomcat/temp sudo chown tomcat:tomcat /opt/tomcat/temp5.2 服务状态查询不准确现象Tomcat进程明明在但service tomcat status却显示stopped。排查问题几乎都出在get_pid函数上。检查CATALINA_PID文件路径是否正确以及Tomcat是否真的将PID写入了该文件。Tomcat的CATALINA_PID环境变量需要被其启动脚本识别。有时需要在setenv.sh位于bin目录中设置CATALINA_PID。检查ps -ef | grep的匹配模式PROCESS_NAME。如果服务器上有多个Java进程可能匹配错误。可以登录服务器手动执行ps -ef | grep “java.*/opt/tomcat”看是否能唯一匹配到目标进程。如果不行需要调整PROCESS_NAME变量例如使用更精确的类路径“org.apache.catalina.startup.Bootstrap”。解决确保Tomcat启动时使用了正确的CATALINA_PID。可以在start函数中启动后强制写入PID作为保障。优化PROCESS_NAME。一个更稳健的获取PID的方法是PID$(pgrep -f “Dcatalina.home/opt/tomcat”)前提是Tomcat启动命令中包含了-Dcatalina.home参数通常startup.sh会设置。5.3 开机自启未生效现象服务器重启后Tomcat没有自动启动。排查首先确认服务是否真的被启用运行systemctl is-enabled tomcat或chkconfig --list tomcat。检查启动顺序如果Tomcat依赖的服务如网络、数据库没准备好它可能启动失败。查看脚本头部的Required-Start设置。可以尝试在脚本start()函数的开头加入sleep 10延迟启动以等待网络就绪。查看启动日志systemd系统可以查看服务启动日志sudo journalctl -u tomcat.service -b # -b 表示本次启动以来的日志对于init.d脚本可以检查/var/log/boot.log或系统启动的通用日志。手动模拟启动环境测试在/etc/init.d/tomcat脚本的start()函数最前面添加env /tmp/tomcat_start_env.log然后重启。重启后检查这个文件看环境变量尤其是JAVA_HOME是否在启动时被正确设置。有时系统启动时的环境与用户登录后的环境不同。解决在脚本中显式、绝对地设置所有关键环境变量不要依赖系统级的profile。在start()函数开始时添加简单的依赖检查例如# 等待网络接口就绪 while ! ping -c 1 -W 2 8.8.8.8 /dev/null; do sleep 1 done对于复杂依赖考虑将服务启动顺序调后增大chkconfig行中的启动顺序号如从80改为90。5.4 优雅停止无效总是走到强制Kill现象每次停止服务都会等待30秒后触发kill -9。排查Tomcat的shutdown.sh脚本默认通过向8005端口发送SHUTDOWN命令来停止服务。如果失败常见原因有端口被占用或配置修改检查$CATALINA_HOME/conf/server.xml中的Server port”8005″ …端口是否被防火墙阻挡或者是否被修改。停止命令执行用户权限问题stop()函数中切换用户执行的命令可能因为环境变量问题找不到shutdown.sh。使用绝对路径。应用有非守护线程Web应用中有线程未响应中断导致JVM无法退出。解决确保shutdown.sh脚本路径正确且可执行。在stop()函数中可以尝试直接使用$CATALINA_HOME/bin/catalina.sh stop它比shutdown.sh逻辑更健壮一些。如果问题持续可以缩短等待时间比如10秒但需要评估应用正常关闭所需的最短时间。我个人在大量生产服务器上部署的经验是这个基于init.d的脚本方案成功率在99%以上。最关键的就是前期对环境变量、用户权限和PID管理的细致配置。一旦配好它就像服务器的基石一样可靠无论系统如何升级换代只要Linux还是那个Linux这套服务管理机制就能持续工作。
返回列表