如何编写 Shell 脚本自动部署 Java SpringBoot 项目?

文章导读
对于个人项目或内部测试环境,编写 Shell 脚本配合 nohup 启动是最轻量的方案;如果是生产环境,建议优先考虑 systemd 或容器化部署,脚本仅作为辅助手段。
📋 目录
  1. 完整部署脚本示例
  2. 脚本授权与执行
  3. 验证部署是否生效
  4. 生产环境增强建议
  5. 常见坑与排查
A A

对于个人项目或内部测试环境,编写 Shell 脚本配合 nohup 启动是最轻量的方案;如果是生产环境,建议优先考虑 systemd 或容器化部署,脚本仅作为辅助手段。

先说结论:脚本能解决重复劳动,但必须包含进程清理、端口等待和健康检查逻辑,生产环境需谨慎。

  • 适合:小型 VPS 或测试环境,资源有限且变更频率低
  • 核心:避免使用 pkill 误杀,增加启动失败回滚机制
  • 建议:生产环境改用 systemd 管理,脚本用于构建或备份环节

完整部署脚本示例

以下脚本包含了环境变量配置、安全停止、备份、启动及健康检查流程。请根据实际路径修改头部变量。

#!/bin/bash

# ================= 配置区 =================
APP_NAME="your-app.jar"
APP_PORT="8080"
WORK_DIR="/opt/app"
BACKUP_DIR="/opt/app/backup"
JAVA_OPTS="-Xms512m -Xmx512m"
HEALTH_URL="http://localhost:${APP_PORT}/actuator/health"
# =========================================

cd ${WORK_DIR} || exit 1

# 1. 停止旧进程 (避免 pkill 误杀)
echo "Stopping old process..."
PID=$(ps -ef | grep ${APP_NAME} | grep -v grep | awk '{print $2}')
if [ -n "$PID" ]; then
    kill $PID
    # 等待端口释放
    for i in {1..30}; do
        if ! netstat -tlnp | grep :${APP_PORT} > /dev/null 2>&1; then
            break
        fi
        sleep 1
    done
    # 如果端口仍占用,强制杀死
    if netstat -tlnp | grep :${APP_PORT} > /dev/null 2>&1; then
        PID=$(ps -ef | grep ${APP_NAME} | grep -v grep | awk '{print $2}')
        [ -n "$PID" ] && kill -9 $PID
        sleep 2
    fi
fi

# 2. 备份旧版本
if [ -f "${WORK_DIR}/${APP_NAME}" ]; then
    mkdir -p ${BACKUP_DIR}
    cp ${APP_NAME} ${BACKUP_DIR}/${APP_NAME}.$(date +%Y%m%d%H%M%S)
    echo "Backup completed."
fi

# 3. 启动新服务
echo "Starting new process..."
nohup java ${JAVA_OPTS} -jar ${APP_NAME} `--server`.port=${APP_PORT} > app.log 2>&1 &

# 4. 健康检查 (等待最多 60 秒)
echo "Checking health..."
for i in {1..60}; do
    sleep 1
    if curl -s ${HEALTH_URL} | grep -q "UP"; then
        echo "Deployment successful!"
        exit 0
    fi
done

echo "Deployment failed! Check app.log"
# 此处可添加回滚逻辑,如恢复 backup 目录最新文件
exit 1

脚本授权与执行

脚本编写完成后,需要赋予执行权限并通过后台任务或直接调用运行。

如何编写 Shell 脚本自动部署 Java SpringBoot 项目?
# 赋予执行权限
chmod +x deploy.sh

# 执行部署
./deploy.sh

# 查看实时日志
tail -f app.log

验证部署是否生效

脚本执行退出码为 0 仅代表健康检查通过,建议人工二次确认。

  1. 进程验证ps -ef | grep ${APP_NAME} 确认进程存在且 PID 已更新。
  2. 端口验证ss -tlnp | grep ${APP_PORT} 确认端口处于 LISTEN 状态。
  3. 业务验证:调用核心接口或访问首页,确认业务逻辑正常。
  4. 日志验证tail -n 100 app.log 确认无 Exception 堆栈信息。

生产环境增强建议

纯脚本部署缺乏守护能力,生产环境建议配合以下措施:

如何编写 Shell 脚本自动部署 Java SpringBoot 项目?

1. 日志切割配置

防止 app.log 无限增长占满磁盘,配置 logrotate

/opt/app/app.log {
daily
rotate 7
missingok
notifempty
copytruncate
}

2. 定时任务备份

可结合 crontab 定期清理备份目录,保留最近 N 个版本。

# 每天凌晨清理 7 天前的备份
0 2 * * * find /opt/app/backup -name "*.jar.*" -mtime +7 -delete

3. 使用 systemd 托管

更推荐编写 /etc/systemd/system/myapp.service 文件,利用系统能力实现开机自启和崩溃重启。

常见坑与排查

  • 端口占用等待:旧进程 killed 后端口可能处于 TIME_WAIT 状态,脚本中已增加循环检测端口释放的逻辑。
  • Java 版本不一致:编译环境与实践环境 Java 版本不同可能导致 class version 错误,部署前务必核对 java -version
  • 权限问题:确保脚本用户对 WORK_DIR 和日志文件有读写权限,避免 Permission denied
  • 环境变量丢失:nohup 启动可能丢失部分环境变量,建议在脚本中显式 export 所需变量。