在 Linux 服务器上部署 Java 应用,看似简单,实则细节决定成败。很多新手直接 java -jar app.jar 就跑起来了,这在开发环境没问题,但在生产环境,这种裸奔的方式是绝对不可取的。
作为一名在云原生和运维领域摸爬滚打多年的“老兵”,我把部署前的准备工作拆解为基础设施层、运行环境层、应用配置层和安全与监控层四个维度来聊。这不仅是步骤,更是避坑指南。
一、 基础设施层:选对土壤
Java 应用对资源有一定要求,尤其是内存和 CPU。在阿里云、腾讯云或 AWS 等主流云厂商购买云服务器(ECS/CVM)时,不要只看价格。
-
操作系统选型
- 推荐:CentOS Stream 7/8, Ubuntu 20.04/22.04 LTS, Debian 11。
- 注意:CentOS 8 已停止维护,建议迁移到 CentOS Stream 或 Rocky Linux/AlmaLinux。Ubuntu Server 版本较新,内核支持较好,适合需要较新内核特性的场景。
- 内核参数优化:这是很多人忽略的点。Linux 默认的文件描述符限制(ulimit)通常较低,Java 高并发场景下容易报错
Too many open files。- 修改
/etc/security/limits.conf,增加nofile限制(如 65535)。 - 调整
/etc/sysctl.conf中的net.ipv4.tcp_tw_reuse,net.core.somaxconn等网络参数,提升 TCP 连接处理能力。
- 修改
-
磁盘 I/O 性能
- Java 应用涉及大量日志写入和临时文件操作。如果是核心业务,建议使用云盘中的 ESSD 或 SSD 类型,避免使用低性能的 HDD,否则 GC(垃圾回收)停顿加上磁盘 IO 等待,会导致服务响应极慢。
二、 运行环境层:JDK 不是随便装的
别直接从官网下载二进制包解压了事,虽然可行,但不够专业。
-
JDK 版本选择
- LTS 原则:生产环境务必使用长期支持版本(LTS),如 JDK 8, 11, 17, 21。
- 发行版差异:
- OpenJDK:开源免费,主流选择。
- Corretto (AWS) / Dragonwell (阿里) / TencentKona:国内大厂提供的 OpenJDK 发行版,针对 Linux 内核和硬件做了深度优化,GC 算法也有增强,强烈建议在国产云服务器上使用对应厂商的 JDK。
- 版本一致性:确保测试环境和生产环境的 JDK 小版本号一致,避免因版本差异导致的类加载问题或 Bug。
-
安装方式
- 推荐:使用包管理器(yum/apt)安装,或者使用 SDKMAN! 管理多版本。
- 避免:手动解压 tar.gz 后不配置环境变量,导致后续升级麻烦。
- 验证:安装后执行
java -version和javac -version(如果需要编译),确认路径正确。
三、 应用配置层:解耦与标准化
这是最容易出问题的地方。不要把配置硬编码在代码里。
-
配置文件分离
- 使用 Spring Boot 的
application.yml或properties文件。 - 关键配置:
server.port: 指定非 8080 端口(防止冲突和安全考虑)。spring.profiles.active: 区分 dev, test, prod 环境。- 数据库连接池:检查 HikariCP 或 Druid 的配置,特别是
maximumPoolSize,根据服务器 CPU 核心数和内存合理设置,不要盲目调大。
- 使用 Spring Boot 的
-
JVM 参数调优(重中之重)
- 不要依赖 JVM 默认参数!必须显式指定。
- 基础参数:
-Xms2g -Xmx2g # 堆内存初始值和最大值设为相同,避免动态扩容带来的性能抖动 -XX:+UseG1GC # 现代 JVM 推荐使用 G1 垃圾收集器 -XX:MaxGCPauseMillis=200 # 目标最大 GC 停顿时间 -XX:+HeapDumpOnOutOfMemoryError # OOM 时自动 dump 堆栈 -XX:HeapDumpPath=/data/logs/heapdump.hprof # dump 文件存放路径 - 元空间:
-XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m,防止类加载过多导致 OOM。 - 日志输出:将 GC 日志输出到文件,便于后期分析:
-Xloggc:/data/logs/gc.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps。
-
启动脚本规范化
- 不要直接用
nohup java -jar ... &。 - 编写标准的 Shell 启动脚本,包含:
- PID 文件管理(用于优雅停止)。
- 日志重定向(stdout/stderr 分离)。
- 健康检查钩子(可选)。
- 示例结构:
#!/bin/bash APP_NAME="my-app" JAVA_OPTS="-Xms2g -Xmx2g ..." nohup java $JAVA_OPTS -jar $APP_NAME.jar > /dev/null 2>&1 & echo $! > /var/run/$APP_NAME.pid
- 不要直接用
四、 安全与监控层:看不见的不存在
-
防火墙与安全组
- 最小权限原则:只开放必要端口(如 80, 443, 以及内部通信端口)。
- 云厂商安全组:在阿里云/腾讯云的控制台配置安全组规则,禁止所有 IP 访问 SSH(22 端口),仅允许特定管理 IP 段访问。
- 本地防火墙:即使有云安全组,也建议在服务器内部启用
firewalld或iptables作为第二道防线。
-
用户权限隔离
- 严禁使用 root 运行 Java 应用!
- 创建一个专用用户,如
appuser,拥有应用目录的读写权限,但没有系统级高危权限。 - 修改应用目录所有权:
chown -R appuser:appuser /opt/my-app。
-
日志与监控
- 日志轮转:使用
logrotate配置日志切割,防止日志文件撑爆磁盘。 - 监控探针:
- 基础监控:CPU、内存、磁盘 IO、网络流量(云厂商自带)。
- 应用监控:集成 Micrometer + Prometheus + Grafana,暴露
/actuator/prometheus端点。 - APM 工具:如果预算允许,接入 SkyWalking 或 Pinpoint,追踪链路耗时。
- 日志轮转:使用
五、 部署流程自动化(进阶)
如果你还在手动 scp 上传 jar 包,那效率太低且易出错。
- CI/CD 流水线:使用 Jenkins、GitLab CI 或 GitHub Actions。
- 容器化部署:强烈建议将 Java 应用打包成 Docker 镜像。
- 优势:环境一致性、快速回滚、资源隔离。
- 结合 Kubernetes (K8s) 或 Docker Swarm 进行编排。
- 即使不用 K8s,单节点 Docker 部署也比裸机部署更可控。
总结清单(Checklist)
在按下“发布”按钮前,请核对:
- [ ] 服务器 OS 已更新补丁,内核参数优化完成。
- [ ] JDK 版本正确,环境变量配置无误。
- [ ] 应用以非 root 用户身份运行。
- [ ] JVM 堆内存、GC 参数、元空间大小已根据服务器资源配置。
- [ ] 配置文件中的数据库密码、Redis 地址等敏感信息已加密或通过配置中心注入。
- [ ] 防火墙仅开放必要端口。
- [ ] 日志轮转策略已配置。
- [ ] 备份了旧版本应用和数据,以便紧急回滚。
部署 Java 应用,“稳”比“快”重要。前期花时间在环境准备和参数调优上,能省去后期无数个深夜排查问题的痛苦。
云技术博客