1. 项目概述从“震惊”到“踏实”的Linux部署之旅看到“震惊如何在linux下部署项目部署/运行jar包 超详细保姆级教程”这个标题我猜你可能是刚接触Linux后端部署的新手或者是从Windows开发环境切换过来正对着黑乎乎的终端窗口感到一丝迷茫和“震惊”。别担心这种感觉我太熟悉了。十年前我第一次把写好的Java程序扔到服务器上时也是手忙脚乱一个简单的java -jar命令背后藏了无数个坑。今天我就以一个踩过无数坑的“老运维”视角帮你把这份“震惊”转化为“踏实”手把手带你走通在Linux环境下部署和运行Jar包的全流程。这不仅仅是敲几个命令更是理解从本地开发到线上服务稳定运行的完整逻辑链条。无论你是个人开发者想把自己的小项目跑起来还是团队里的后端新人需要快速上手这篇内容都会像一位有经验的同事坐在你旁边一样把每一步为什么这么做、可能会遇到什么问题、怎么解决都讲清楚。我们的目标很简单让你看完就能动手动手就能成功成功之后还能明白其中的道理。2. 核心思路与准备工作为什么是Linux和Jar包在开始敲命令之前我们得先统一思想搞清楚两个最基础的问题为什么要在Linux上部署以及为什么是Jar包这决定了我们后续所有操作的底层逻辑。2.1 选择Linux作为部署环境的核心考量首先为什么是Linux这绝不是因为“高手都用这个”的玄学。对于Java应用尤其是Spring Boot这类主流框架构建的应用的服务器端部署Linux几乎是事实上的标准原因非常务实稳定与高效Linux内核以其出色的稳定性和对系统资源尤其是内存和网络的高效管理而闻名。一个配置得当的Linux服务器可以稳定运行数百天甚至数年无需重启这对于需要提供7x24小时不间断服务的后端应用至关重要。相比之下Windows Server虽然图形界面友好但在纯服务端场景下其资源开销和稳定性历史记录并不占优。资源开销极低服务器资源特别是CPU和内存是真金白银买来的。Linux系统本身占用资源极少可以将更多的硬件能力留给你的业务应用。一台1核2G的轻量云服务器跑个Linux Java应用可能绰绰有余但换成Windows可能刚开机就捉襟见肘。强大的命令行与自动化部署和维护本质上是一系列重复性操作。Linux的命令行工具链Shell, SSH, Cron, Systemd等极其强大且标准化非常适合编写自动化脚本。无论是通过一行命令批量更新十台服务器还是用Cron定时执行备份任务都比在图形界面上手动点击要可靠和高效得多。广泛的社区与生态你遇到的几乎任何问题都能在社区找到解决方案。从软件包的安装yum,apt到性能调优参数都有海量的文档和讨论。这对于排查线上问题来说是无价的财富。成本大多数Linux发行版是免费开源的这直接降低了软件授权成本。虽然对于个人或小公司这不是首要问题但在大规模部署时积少成多也是一笔可观的节省。所以选择Linux是选择了稳定性、效率和可维护性这是生产环境部署的基石。2.2 Jar包Java应用交付的标准“集装箱”然后为什么是Jar包你可以把它想象成海运中的标准集装箱。在Java的世界里尤其是Spring Boot兴起之后Fat Jar或称 Uber Jar成为了应用分发的首选格式。开箱即用一个Fat Jar包内嵌了所有依赖的第三方库放在BOOT-INF/lib/下以及你自己的应用代码。这意味着你不需要在目标服务器上复杂地配置CLASSPATH只需要系统装有合适版本的Java运行时环境JRE就能通过java -jar yourapp.jar直接启动。这极大地简化了部署流程实现了“一次构建处处运行”的承诺。版本一致性依赖库和你的应用代码被打包在一起完全避免了服务器环境依赖版本与开发环境不一致导致的“在我机器上是好的”这类经典问题。便于分发和回滚部署就是上传一个文件。如果需要回滚到上一个版本只需要替换回旧的Jar文件并重启服务即可操作简单风险可控。理解了这两个“为什么”我们就能明白在Linux上部署Jar包是一个将标准化应用单元集装箱交付到高可靠运行环境专业港口的最佳实践组合。2.3 部署前必须明确的四个要点动手前请先确认这四件事它们是你的“行军地图”你的Jar包是怎么来的它是通过IDE如IntelliJ IDEA直接打包的还是通过Maven (mvn clean package) 或 Gradle (gradle bootJar) 构建的确保这个包在你的本地开发环境是可以正常启动的。这是所有后续操作的源头如果源头上就有问题在服务器上怎么折腾都是徒劳。目标服务器环境你有一台什么样的Linux服务器是云服务商如阿里云、腾讯云购买的CentOS、Ubuntu还是自己虚拟机里的Debian知道系统版本如cat /etc/os-release很重要因为不同的发行版软件包管理命令不同CentOS/RHEL用yumUbuntu/Debian用apt。网络与权限你如何连接到服务器通常通过SSH。你拥有什么权限最好是root用户或者具有sudo权限的普通用户。很多安装和配置操作需要管理员权限。应用自身配置你的应用是否有外部配置文件如application.yml或application.properties这些配置文件是打算打包进Jar包内部还是放在Jar包外部的特定目录如/opt/yourapp/config/以便于动态修改生产环境的数据库连接、Redis地址等敏感信息绝对不应该硬编码在代码或打包进Jar的资源文件中而应通过外部配置或环境变量注入。这是安全性和灵活性的关键。注意生产环境的密码、密钥等敏感信息严禁写入项目代码或提交到代码仓库。务必使用外部配置文件并做好权限控制如chmod 600、环境变量或专业的配置中心如Apollo, Nacos来管理。3. 部署环境准备打造坚实的“地基”有了清晰的地图我们现在开始为我们的Jar包“集装箱”建造一个稳固的“港口”。这个阶段的目标是搭建一个干净、标准化的运行环境。3.1 服务器基础检查与连接假设你已经拥有一台安装了Linux的服务器并通过SSH客户端如PuTTY、Xshell、或者Mac/Linux终端连接上了它。首先我们做个快速体检了解服务器状态# 查看系统版本确认发行版 cat /etc/os-release # 查看内核版本和系统架构是x86_64还是arm64 uname -a # 查看磁盘空间确保有足够空间存放Jar包和日志 df -h # 查看内存和CPU信息 free -h lscpu这些信息在你后续选择Java版本、排查性能问题时都会用到。3.2 Java运行环境JRE/JDK安装详解这是最重要的依赖。你的Jar包需要特定版本的Java来运行。Spring Boot 2.x 通常需要Java 8或11Spring Boot 3.x 则需要Java 17或21。1. 检查是否已安装Javajava -version如果已经安装了合适的版本会显示类似“openjdk version “11.0.20” 2023-07-18”的信息。如果版本太低或未安装则需要进行安装。2. 安装Java以OpenJDK 11为例这是目前最主流的生产环境选择之一对于CentOS/RHEL/AlmaLinux/Rocky Linux系列# 更新软件包索引 sudo yum update -y # 搜索可用的Java 11包 sudo yum search openjdk-11 # 通常安装名为 java-11-openjdk-devel包含开发工具或 java-11-openjdk sudo yum install -y java-11-openjdk-devel # 验证安装 java -version javac -version # 如果安装了devel包会有编译器对于Ubuntu/Debian系列# 更新软件包列表 sudo apt update # 安装OpenJDK 11 JRE如果只需要运行环境 sudo apt install -y openjdk-11-jre-headless # 或者安装完整的JDK包含编译工具 sudo apt install -y openjdk-11-jdk-headless # 验证安装 java -version3. 设置JAVA_HOME环境变量可选但推荐很多工具和脚本会依赖这个变量。首先找到Java的安装路径# 通常安装在这里 which java # 输出可能是 /usr/bin/java这是一个软链接继续追踪 ls -l /usr/bin/java # 可能会指向 /etc/alternatives/java再追踪一次 ls -l /etc/alternatives/java # 最终会指向类似 /usr/lib/jvm/java-11-openjdk-11.0.20.0.8-1.el7_9.x86_64/bin/java 的路径 # JAVA_HOME就是这个路径去掉最后的 /bin/java # 例如JAVA_HOME/usr/lib/jvm/java-11-openjdk-11.0.20.0.8-1.el7_9.x86_64然后编辑用户配置文件如~/.bashrc或~/.bash_profileecho ‘export JAVA_HOME/usr/lib/jvm/java-11-openjdk-11.0.20.0.8-1.el7_9.x86_64’ ~/.bashrc echo ‘export PATH$JAVA_HOME/bin:$PATH’ ~/.bashrc # 使配置立即生效 source ~/.bashrc # 验证 echo $JAVA_HOME实操心得生产环境强烈建议安装-headless版本无图形界面它更轻量节省资源。对于确定只用Spring Boot Fat Jar运行的应用安装JREJava Runtime Environment就够了。但如果你未来可能需要在服务器上编译或调试安装JDKJava Development Kit会更方便。我个人的习惯是测试环境装JDK生产环境装JRE。3.3 创建专用的应用运行用户和目录永远不要使用root用户直接运行你的应用这是一个重要的安全原则。我们应该创建一个权限受限的专用用户来运行服务。# 创建一个名为‘yourapp’的系统用户且不创建家目录-r参数并指定一个无法登录的shell-s /sbin/nologin sudo useradd -r -s /sbin/nologin yourapp # 创建应用的主目录比如 /opt/yourapp sudo mkdir -p /opt/yourapp # 将目录的所有权赋予新创建的用户和组 sudo chown -R yourapp:yourapp /opt/yourapp # 创建几个子目录用于存放不同内容 sudo -u yourapp mkdir -p /opt/yourapp/{bin,config,logs,lib,backup} # bin: 存放启动脚本 # config: 存放外部配置文件 # logs: 存放应用日志 # lib: 存放Jar包本身 # backup: 用于版本回滚备份这样我们的目录结构就清晰了权限也隔离了。应用运行时产生的日志、临时文件都会在以yourapp用户权限下进行即使应用存在漏洞攻击者获得的权限也被限制在这个用户内无法危及整个系统。4. 项目文件上传与配置管理环境准备好了现在要把我们的“货物”Jar包运到“港口”并安排好它的“泊位”和“作业说明”配置文件。4.1 将Jar包传输到服务器有多种方式可以将本地构建好的Jar包上传到服务器的/opt/yourapp/lib/目录下。方法一使用SCP命令命令行直接操作这是最直接的方式在你的本地电脑的终端中执行# 假设你的服务器IP是 192.168.1.100用户是 root或其他有权限的用户 # 将本地target/下的myapp-0.0.1-SNAPSHOT.jar上传到服务器的临时位置 scp ./target/myapp-0.0.1-SNAPSHOT.jar root192.168.1.100:/tmp/ # 然后登录服务器将文件移动到正确位置并修改属主 ssh root192.168.1.100 mv /tmp/myapp-0.0.1-SNAPSHOT.jar /opt/yourapp/lib/ chown yourapp:yourapp /opt/yourapp/lib/myapp-0.0.1-SNAPSHOT.jar方法二使用SFTP客户端图形化操作如果你不习惯命令行可以使用FileZilla、WinSCP等图形化SFTP工具。连接服务器后直接将本地文件拖拽到远程的/opt/yourapp/lib/目录即可之后同样需要通过SSH登录修改文件属主。方法三通过CI/CD工具自动化在团队协作中通常会使用Jenkins、GitLab CI等工具在构建完成后自动将Jar包上传到服务器指定位置这属于更高级的自动化部署范畴。注意事项上传后务必检查文件是否完整。可以对比本地和服务器上文件的MD5或SHA256校验和# 本地计算 md5sum myapp-0.0.1-SNAPSHOT.jar # 服务器计算 sudo -u yourapp md5sum /opt/yourapp/lib/myapp-0.0.1-SNAPSHOT.jar两者结果必须一致避免因网络传输导致文件损坏。4.2 外部配置文件的管理策略Spring Boot应用默认会从Jar包内部的classpath如/BOOT-INF/classes/加载application.properties或application.yml。但在生产环境我们更希望将配置放在Jar包外部原因有三1) 无需重新打包即可修改配置2) 便于管理不同环境测试、生产的配置3) 安全避免敏感信息被打包。策略使用--spring.config.location参数指定外部配置。准备生产环境配置文件在本地根据生产环境数据库、缓存、消息队列等地址创建一个独立的配置文件例如application-prod.yml。切记里面不要包含真实密码密码应通过环境变量或启动参数传入。上传配置文件将这个配置文件上传到服务器的/opt/yourapp/config/目录并确保权限正确# 假设配置文件已上传到/tmp sudo mv /tmp/application-prod.yml /opt/yourapp/config/ sudo chown yourapp:yourapp /opt/yourapp/config/application-prod.yml sudo chmod 600 /opt/yourapp/config/application-prod.yml # 限制读写权限仅属主可读可写配置文件内容示例(application-prod.yml)server: port: 8080 servlet: context-path: /api spring: datasource: url: jdbc:mysql://prod-db-host:3306/yourdb?useSSLfalsecharacterEncodingutf8 username: prod_user # 密码不写在这里将通过环境变量传递 # password: ${DB_PASSWORD} redis: host: prod-redis-host port: 6379 # 指定生产环境日志级别和输出文件 logging: file: name: /opt/yourapp/logs/application.log level: com.yourcompany: INFO org.springframework: WARN如何安全地传入密码环境变量在启动脚本中设置export DB_PASSWORDyour_strong_password。启动参数在启动命令中直接传递--spring.datasource.passwordyour_strong_password不推荐因为密码可能在进程列表中被看到。使用配置中心的API或加密文件进阶方案。最常用且相对安全的是环境变量法。我们将在启动脚本中体现。5. 编写可靠的启动与管理脚本直接使用java -jar命令启动应用一旦关闭终端应用就停止了。这显然不适合生产环境。我们需要一个“守护进程”来管理应用的生命周期启动、停止、重启、查看状态。在Linux世界systemd是现代发行版的标准服务管理工具它比古老的init.d脚本更强大、更易用。5.1 创建Systemd服务单元文件我们将为应用创建一个systemd服务。以root或sudo权限在/etc/systemd/system/目录下创建文件例如yourapp.service。sudo vim /etc/systemd/system/yourapp.service将以下内容写入文件请根据你的实际情况修改注释部分[Unit] DescriptionYour Awesome Spring Boot Application Service Afternetwork.target syslog.target # 如果你的应用依赖MySQL、Redis等可以在这里声明确保它们先启动 # Afternetwork.target mysql.service redis.service # Wantsmysql.service redis.service [Service] # 使用我们之前创建的专用用户和组运行 Useryourapp Groupyourapp # 指定工作目录应用运行时产生的相对路径文件都会基于此目录 WorkingDirectory/opt/yourapp # 最重要的启动命令 # 1. 从 /opt/yourapp/lib/ 目录下找到最新的jar包假设我们按版本号命名 # 2. 指定外部配置文件位置 # 3. 通过环境变量传入敏感密码 # 4. 设置JVM内存参数非常重要 ExecStart/bin/bash -c ‘JAR_FILE$(ls -t /opt/yourapp/lib/*.jar | head -n 1) exec java \ -server \ -Xms512m \ -Xmx1024m \ -XX:UseG1GC \ -XX:MaxGCPauseMillis200 \ -XX:HeapDumpOnOutOfMemoryError \ -XX:HeapDumpPath/opt/yourapp/logs/heapdump.hprof \ -Dspring.profiles.activeprod \ -Dspring.config.locationfile:/opt/yourapp/config/application-prod.yml \ -jar “$JAR_FILE”‘ # 环境变量文件可以集中管理所有环境变量更安全整洁 # EnvironmentFile/opt/yourapp/config/yourapp.env # 如果不用文件也可以直接在这里写密码等敏感信息不推荐 Environment“DB_PASSWORDyour_very_strong_production_password_here” Environment“REDIS_PASSWORDanother_strong_password” # 标准输出和错误输出重定向到系统日志同时也可以输出到自定义文件 StandardOutputjournal StandardErrorjournal # 也可以同时输出到文件 # StandardOutputappend:/opt/yourapp/logs/stdout.log # StandardErrorappend:/opt/yourapp/logs/stderr.log # 指定重启策略当进程异常退出时自动重启 Restarton-failure # 重启间隔避免频繁重启刷日志 RestartSec10s # 限制进程资源增强安全性可选但推荐 # LimitNOFILE65535 # LimitNPROC4096 [Install] WantedBymulti-user.target关键参数解析-Xms512m -Xmx1024m设置JVM堆内存初始大小为512MB最大为1024MB。这是调优关键必须根据你的服务器物理内存和应用实际需求设置。通常-Xms和-Xmx设为相同值可以避免运行期堆内存扩容带来的性能抖动。例如在2G内存的服务器上可以设置为-Xms1g -Xmx1g为系统和其他进程留出空间。-XX:UseG1GC指定使用G1垃圾收集器它在大多数场景下比老的Parallel GC或CMS表现更好尤其对于响应时间有要求的应用。-XX:MaxGCPauseMillis200告诉G1收集器期望的最大GC停顿时间为200毫秒它会尽力达成这个目标。-XX:HeapDumpOnOutOfMemoryError -XX:HeapDumpPath...在发生内存溢出错误时自动生成堆转储文件。这是事后分析OOM问题的救命稻草-Dspring.profiles.activeprod激活名为prod的Spring Profile。你的配置文件中可以有application-prod.yml部分来覆盖默认配置。-Dspring.config.locationfile:...明确指定外部配置文件的位置优先级最高。$(ls -t /opt/yourapp/lib/*.jar | head -n 1)这是一个Shell技巧它会找到/opt/yourapp/lib/目录下按修改时间排序的最新一个Jar包。这样当你部署新版本时只需要把新的Jar包上传到这个目录重启服务就会自动运行最新的版本无需修改服务文件。非常方便5.2 管理服务启动、停止、重启、查看状态创建好服务文件后需要让systemd重新加载配置然后就可以管理服务了。# 1. 重新加载systemd配置使其识别新的服务文件 sudo systemctl daemon-reload # 2. 启动服务 sudo systemctl start yourapp.service # 3. 设置服务开机自启非常重要避免服务器重启后服务丢失 sudo systemctl enable yourapp.service # 4. 查看服务状态这是最常用的命令 sudo systemctl status yourapp.service # 你会看到服务是活跃active状态以及最近的日志片段。 # 5. 停止服务 sudo systemctl stop yourapp.service # 6. 重启服务常用于配置更新后 sudo systemctl restart yourapp.service # 7. 查看服务的完整日志使用journalctl它是systemd的日志系统 sudo journalctl -u yourapp.service -f # -f 表示实时跟踪follow日志输出 sudo journalctl -u yourapp.service --since “2024-01-01” --until “2024-01-02” # 查看特定时间段的日志实操心得systemctl status是你最好的朋友。任何时候服务出问题第一个命令就应该是它。它会显示服务是否在运行、最近的日志、以及可能出现的错误信息。journalctl -u yourapp.service -f则像是一个实时监控的控制台在部署后观察启动过程是否顺利异常时查看错误输出。6. 部署后的验证、监控与维护服务启动成功显示active (running)并不意味着万事大吉。我们需要进行一系列验证并建立基本的监控和维护习惯。6.1 服务健康检查与功能验证检查进程是否存在ps -ef | grep java | grep yourapp # 应该能看到一个以‘yourapp’用户运行的java进程命令行参数包含你的Jar包名。检查端口监听你的应用通常在配置文件中指定了服务器端口如server.port8080。sudo netstat -tlnp | grep :8080 # 或使用更现代的ss命令 sudo ss -tlnp | grep :8080 # 应该能看到java进程在监听8080端口。从服务器内部发起HTTP请求测试# 如果应用提供了健康检查端点Spring Boot Actuator的/actuator/health curl -s http://localhost:8080/api/actuator/health | jq . # 需要安装jq工具来美化JSON # 期望返回{“status”: “UP”} # 测试一个简单的业务API curl -s http://localhost:8080/api/hello从外部网络访问测试确保服务器的安全组云服务器或防火墙firewalld/iptables已经放行了应用端口如8080。然后从你的个人电脑浏览器访问http://你的服务器IP:8080/api/...。6.2 日志管理应用的“黑匣子”日志是排查线上问题的唯一可靠依据。我们之前已经在systemd服务文件中将标准输出和错误重定向到了系统日志journal。但为了长期保存和方便查看我们通常还会配置应用将日志输出到文件。Spring Boot Logback配置示例 (logback-spring.xml或application.yml中配置) 确保你的生产环境配置application-prod.yml中指定了日志文件路径如前文示例中的/opt/yourapp/logs/application.log。同时需要管理日志文件避免单个文件过大。可以使用Logback的滚动策略或者更通用的Linux工具logrotate。配置logrotate 创建配置文件/etc/logrotate.d/yourappsudo vim /etc/logrotate.d/yourapp内容如下/opt/yourapp/logs/*.log { daily # 每天滚动一次 missingok # 如果日志文件丢失不报错 rotate 30 # 保留30个归档即30天的日志 compress # 压缩旧的日志文件以节省空间 delaycompress # 延迟一天压缩方便查看昨天的日志 notifempty # 如果日志文件为空不进行滚动 create 640 yourapp yourapp # 创建新日志文件时的权限和属主 sharedscripts # 在所有日志文件滚动后执行一次postrotate脚本 postrotate # 通知应用重新打开日志文件如果应用支持 # 对于Spring Boot通常需要发送信号或调用Actuator端点更简单的方式是配置Logback的自动扫描。 # 这里我们采用重启服务的方式在低峰期或者使用Logback的自动刷新。 # 最简单的方式配置Logback使用 prudent 模式或依赖其自动刷新此处可以不操作。 # 如果需要可以发送USR1信号给Java进程如果Logback配置了接收信号 # kill -USR1 $(cat /opt/yourapp/yourapp.pid 2/dev/null) 2/dev/null || true endscript }这样日志管理就自动化了你可以在/opt/yourapp/logs/目录下看到类似application.log、application.log-20240101.gz这样的文件。6.3 基础监控与告警对于个人项目或小规模应用基础的监控可以手动设置。进程存活监控最简单的方法是写一个Cron定时任务定期检查进程是否存在如果不存在就尝试重启并发送通知如邮件、钉钉、企业微信机器人。# 编辑crontab: sudo crontab -e # 添加一行每5分钟检查一次 */5 * * * * /usr/bin/pgrep -f ‘yourapp.jar’ /dev/null || (sudo systemctl restart yourapp.service echo “$(date): yourapp restarted” /opt/yourapp/监控.log)注意这只是最简陋的监控。更好的做法是使用专业的监控系统如Prometheus Grafana。Spring Boot Actuator暴露了/actuator/metrics和/actuator/prometheus端点可以很方便地与Prometheus集成监控JVM内存、GC、线程池、HTTP请求量等丰富指标。磁盘空间监控日志和堆转储文件可能会占满磁盘。同样可以用Cron任务监控/opt/yourapp/logs/目录的大小。# 检查磁盘使用率 df -h /opt # 或者检查日志目录大小 du -sh /opt/yourapp/logs/6.4 版本更新与回滚流程当你有新版本需要部署时一个规范化的流程能避免混乱。备份当前版本sudo -u yourapp cp /opt/yourapp/lib/yourapp-current.jar /opt/yourapp/backup/yourapp-$(date %Y%m%d%H%M%S).jar.bak # 如果用了“最新jar包”的启动方式备份整个lib目录下的旧jar包。上传新版本Jar包使用SCP或SFTP将新构建的Jar包上传到/opt/yourapp/lib/目录。建议使用带版本号的文件名如myapp-0.0.2-SNAPSHOT.jar这样备份和识别更清晰。重启服务sudo systemctl restart yourapp.service验证新版本立即使用sudo systemctl status yourapp.service和sudo journalctl -u yourapp.service -f观察启动日志并通过健康检查接口和核心业务接口验证功能是否正常。回滚如果出现问题停止服务sudo systemctl stop yourapp.service恢复旧版Jar包sudo -u yourapp cp /opt/yourapp/backup/yourapp-备份时间.jar /opt/yourapp/lib/yourapp-current.jar或删除新版确保旧版是最新的文件启动服务sudo systemctl start yourapp.service7. 常见问题与故障排查实录即使按照教程一步步来也难免会遇到问题。下面是我总结的一些常见“坑”及其解决方案。7.1 服务启动失败Status203/EXEC 或 Permission denied现象sudo systemctl status yourapp.service显示Failed to start ...状态码可能是203日志显示Permission denied。原因与解决Jar包或脚本没有执行权限确保Jar包和任何被ExecStart引用的脚本对运行用户yourapp是可读的。sudo chmod 644 /opt/yourapp/lib/*.jar # Jar包需要读权限 # 如果启动命令是一个脚本则需要执行权限 # sudo chmod 755 /opt/yourapp/bin/start.shJava命令未找到systemd的环境变量可能与你的Shell环境不同。在ExecStart中使用Java的绝对路径。# 使用 which java 找到的路径 which java # 假设输出 /usr/bin/java # 然后在服务文件中使用 ExecStart/usr/bin/java -jar ...运行用户无权访问目录确保/opt/yourapp及其子目录的所有者和组是yourapp并且该用户有读和执行权限。7.2 服务启动后立即退出Status0/SUCCESS 但进程不存在现象systemctl start显示成功但status显示inactive (dead)journalctl日志显示应用启动后自己退出了。原因与解决应用自身启动失败这是最常见的原因。Spring Boot应用可能因为数据库连接不上、配置文件错误、端口被占用等原因启动失败。仔细查看日志sudo journalctl -u yourapp.service -n 100 --no-pager # 查看最近100行日志重点关注日志中的Exception、Error、Failed to configure DataSource等关键词。端口被占用另一个常见原因。检查你配置的端口如8080是否已被其他程序占用。sudo ss -tlnp | grep :8080如果被占用要么停止那个程序要么修改你应用的server.port配置。JVM参数错误例如-Xmx设置得比服务器可用物理内存还大导致JVM无法分配内存而退出。检查日志开头部分是否有内存相关的错误。7.3 应用运行一段时间后内存占用过高或OOM现象服务运行几天后响应变慢最终可能因OutOfMemoryError崩溃。原因与解决内存泄漏这是Java应用的经典问题。某个对象被意外地长期持有无法被垃圾回收。排查在服务文件中我们已经配置了-XX:HeapDumpOnOutOfMemoryError当OOM发生时会在指定路径生成堆转储文件.hprof。将这个文件下载到本地使用MATMemory Analyzer Tool或JVisualVM等工具进行分析找出泄漏的对象和引用链。预防定期检查JVM内存使用情况。可以使用jstat命令或通过Actuator的/actuator/metrics/jvm.memory.used端点监控。JVM堆内存设置不合理-Xmx设置太小业务量上来后不够用或者设置太大导致系统本身内存不足触发OOM Killer杀掉Java进程。调整根据服务器总内存和应用实际使用情况调整-Xms和-Xmx。一个经验法则是对于总内存为2G的服务器-Xmx可以设为1G1024m为系统和其他进程留出1G空间。使用top或htop命令观察系统的内存使用情况。非堆内存问题Metaspace存储类元信息或直接内存Direct Buffer泄漏。排查同样可以通过堆转储和JVM参数监控。可以添加参数-XX:MaxMetaspaceSize256m来限制Metaspace大小。7.4 如何查看实时日志和筛选错误除了journalctl -u yourapp.service -f还有一些高级用法# 查看从今天开始的日志 sudo journalctl -u yourapp.service --since today # 查看包含“ERROR”级别的日志行 sudo journalctl -u yourapp.service -p err # 查看特定时间段的日志 sudo journalctl -u yourapp.service --since “2024-05-01 09:00:00” --until “2024-05-01 10:00:00” # 将日志输出到文件 sudo journalctl -u yourapp.service --since “-1h” /tmp/app_last_hour.log7.5 防火墙与安全组配置如果你的应用无法从外部访问但服务器内部curl localhost:8080是通的那几乎肯定是网络层面的问题。检查服务器本地防火墙如firewalld或ufw# 对于firewalld (CentOS/RHEL 7) sudo firewall-cmd --list-all # 查看当前规则 sudo firewall-cmd --permanent --add-port8080/tcp # 永久添加8080端口规则 sudo firewall-cmd --reload # 重载配置 # 对于ufw (Ubuntu) sudo ufw status sudo ufw allow 8080/tcp检查云服务商的安全组规则登录到阿里云、腾讯云等控制台找到你的云服务器实例确保入方向规则允许访问你的应用端口如8080。通常需要允许来源为0.0.0.0/0或你的特定IP段协议为TCP端口为8080。从“震惊”于Linux的黑色终端到能够有条不紊地完成环境准备、文件上传、服务配置、启动验证和日常维护这个过程本身就是一次宝贵的成长。Linux部署没有魔法有的只是对每一个细节的理解和掌控。我个人的体会是最开始的几次部署肯定会遇到各种稀奇古怪的问题但每一次解决问题的过程都会让你对应用、对系统、对网络的理解更深一层。不要怕麻烦把日志当成最好的朋友把systemctl status和journalctl用熟。当你能够从容地处理线上服务的重启、回滚和故障排查时你会发现当初那个令人“震惊”的黑窗口已经变成了你手中最得力的工具。最后再分享一个小技巧为自己负责的每一个服务写一个简短的README.md放在/opt/yourapp/目录下记录这个应用的用途、关键配置项、启动命令、日志位置和常见问题。几个月后当你再回头看时或者需要交接给同事时这份文档会价值连城。