阿和
发布于 2026-09-16 / 0 阅读
0

Java JAR 一键部署脚本

deploy_jar.v2.sh 干一件事:按 JAR 名找到旧进程停掉,再 nohup 把新包拉起来。需要的话再 curl 一下健康检查。POSIX sh,不依赖 bash。

流程就是:解析参数 → 找 java → 补 JVM 参数 → stop → 等一会 → start → 探活。

远程获取

https://shell.minio.yhliu.cn/deploy_jar.v2.sh

# 下到本地
curl -sSL -o deploy_jar.v2.sh https://shell.minio.yhliu.cn/deploy_jar.v2.sh
chmod +x deploy_jar.v2.sh
./deploy_jar.v2.sh my-app /data/web/java_app /data/logs/my-app.log "$env_params" "$java_params"

# 不落盘,参数跟在 -- 后面
curl -sSL https://shell.minio.yhliu.cn/deploy_jar.v2.sh | sh -s -- my-app /data/web/java_app /data/logs/my-app.log "$env_params" "$java_params"

参数

前 5 个是位置参数,JAR 名不要带 .jar,脚本自己拼。可选开关写在 -- 后面,--key=value--key value 都行。

./deploy_jar.v2.sh <jar名> <jar目录> <日志路径> "<系统属性>" "<JVM参数>" -- [可选开关]

位置参数

位置

含义

示例

$1

JAR 名(不含 .jar)

my-app

$2

JAR 所在目录,启动前会 cd 进去

/data/web/java_app

$3

进程 stdout/stderr 路径

/data/logs/my-app.log

$4

拼到 java 命令行上,一般是 -Dxxx,不是 export 环境变量

-Dspring.profiles.active=prod

$5

JVM 参数;空则用脚本里那组 1g 堆

-Xms2g -Xmx2g

可选开关

开关

默认

说明

--java-path

自动探测

优先用这个路径,否则 JAVA_HOME/bin/java,再否则 PATH 里的 java

--monitor-url

空就跳过探活;配了必须返回 HTTP 200

--monitor-timeout

5

单次 curl 超时(秒),连接超时和总超时都是这个值

--monitor-count

5

最多探几次

--monitor-delay

15

第一次检查前先睡几秒,给 JVM 启动留时间

--monitor-interval

10

两次检查之间的间隔

--stop-wait

30

SIGTERM 之后等多久;<=0 直接 kill -9

--restart-wait

2

停完到启动之间等几秒,小于 1 按 1 算

--start-log

file

file 追加到 log_path;file-overwrite 覆盖;devnull 丢到 /dev/null

脚本实现

文件末尾 main "$@",函数拆开写的。下面按执行顺序说。

main() {
  parse_args "$@"
  resolve_java_path
  resolve_java_params
  stop
  wait_restart
  start
  wait_for_startup
}

log

部署过程打到当前终端,带时间戳。跟 JAR 写到 log_path 的那份不是同一个文件,发布系统采集的是这个。

log() {
  echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"
}

parse_args

前 5 个写进全局变量,$1 会变成 $1.jar。然后 shift 5,剩下的用 case 循环吃开关。

  • 单独一个 -- 会被丢掉,所以可以写成 script a b c d e -- --monitor-url=...

  • 不认识的参数直接 shift,不报错

  • 参数不足 5 个时 shift 5 失败会被 || true 吞掉,真正失败一般是后面 cd-jar

resolve_java_path / log_java_version

找 java 的顺序:

  1. --java-path 有值且可执行

  2. $JAVA_HOME/bin/java 存在且可执行

  3. command -v java

  4. 都没有就 exit 1

绑定之后打一遍 java -version。这个命令输出在 stderr,所以脚本用 2>&1 再逐行 log。一台机器多套 JDK 时把 --java-path 写死,别指望 PATH。

resolve_java_params

第 5 个位置参数为空时补这组,并打一行日志:

-Xms1g -Xmx1g -Xmn250m -Xss256k -server -XX:+HeapDumpOnOutOfMemoryError

1g 堆只是兜底。生产按机器内存自己传 java_params

stop

ps -ef | grep "$jar_name" | grep -v grep 取 PID。匹配的是 JAR 文件名,同机不要跑两个同名包。找不到进程直接返回 0,所以第一次部署也能跑。

  • --stop-wait <= 0:立刻 kill -9

  • 否则先 kill(SIGTERM),每秒 kill -0 看还在不在

  • 等到 stop_wait 还活着,再 kill -9

默认 30 秒。

pid=$(ps -ef | grep "$jar_name" | grep -v grep | awk '{print $2}')
kill $pid          # SIGTERM
# ... 循环 kill -0,超时后:
kill -9 $pid

wait_restart

stop 和 start 之间再睡 restart_wait 秒,默认 2,小于 1 按 1。有的服务端口 / pid 文件不会马上释放,立刻拉起会和旧进程撞车。

start

cd "$jar_path",失败就退出。然后:

nohup $java_path $java_params $env_params -jar "$jar_name" ...

start_log_target 只决定 stdout/stderr 去哪:

  • file(默认):>> "$log_path"

  • file-overwrite> "$log_path",每次部署从空文件开始

  • devnull:丢弃。应用日志走 logback / log4j 时用这个

wait_for_startup

没配 --monitor-url 直接返回 0。配了之后:

  1. 先睡 monitor_delay

  2. curl 取 HTTP 状态码,连接超时和总超时都是 monitor_timeout

  3. 只有 200 算成功;其它码(空、000、502 都算)隔 monitor_interval 再试

  4. 最多 monitor_count 次,全失败返回 1

按默认值,最坏要等 15 + 4×10 = 55 秒。失败不会把刚启动的进程杀掉,回滚得调用方自己看退出码。

Spring Boot 的 /actuator/health 没开鉴权时一般能用。前面有网关的话打 127.0.0.1,别打对外域名。

调用例子

不探活

./deploy_jar.v2.sh my-app /data/web/java_app /data/logs/my-app.log "$env_params" "$java_params"

带健康检查

./deploy_jar.v2.sh my-app /data/web/java_app /data/logs/my-app.log "$env_params" "$java_params" -- \
  --monitor-url=http://127.0.0.1:9000/monitor/health

指定 java

./deploy_jar.v2.sh my-app /data/web/java_app /data/logs/my-app.log "$env_params" "$java_params" -- \
  --java-path=/usr/lib/jvm/java-1.8.0-openjdk/bin/java

参数拉满

./deploy_jar.v2.sh my-app /data/web/java_app /data/logs/my-app.log "$env_params" "$java_params" -- \
  --monitor-url=http://127.0.0.1:9000/monitor/health \
  --monitor-timeout=5 \
  --monitor-count=5 \
  --monitor-delay=15 \
  --monitor-interval=10 \
  --stop-wait=10 \
  --restart-wait=2 \
  --start-log=file \
  --java-path=/usr/bin/java

日志和强制停止

# 覆盖旧日志
./deploy_jar.v2.sh my-app /data/web/java_app /data/logs/my-app.log "$env_params" "$java_params" -- \
  --start-log=file-overwrite

# stdout/stderr 丢掉
./deploy_jar.v2.sh my-app /data/web/java_app /data/logs/my-app.log "$env_params" "$java_params" -- \
  --start-log=devnull

# 旧进程直接 kill -9
./deploy_jar.v2.sh my-app /data/web/java_app /data/logs/my-app.log "$env_params" "$java_params" -- \
  --stop-wait=0

几个坑

  • 依赖 psgrepawkkillnohup;探活还要 curl

  • 按 JAR 文件名 grep 进程。名字太泛(比如 app.jar)可能误杀。

  • $4 叫 env_params,实际是 java 命令行参数。

  • 探活只认 200,失败不回滚。