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参数>" -- [可选开关]位置参数
可选开关
脚本实现
文件末尾 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 的顺序:
--java-path有值且可执行$JAVA_HOME/bin/java存在且可执行command -v java都没有就
exit 1
绑定之后打一遍 java -version。这个命令输出在 stderr,所以脚本用 2>&1 再逐行 log。一台机器多套 JDK 时把 --java-path 写死,别指望 PATH。
resolve_java_params
第 5 个位置参数为空时补这组,并打一行日志:
-Xms1g -Xmx1g -Xmn250m -Xss256k -server -XX:+HeapDumpOnOutOfMemoryError1g 堆只是兜底。生产按机器内存自己传 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 $pidwait_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。配了之后:
先睡
monitor_delay秒curl 取 HTTP 状态码,连接超时和总超时都是
monitor_timeout只有
200算成功;其它码(空、000、502 都算)隔monitor_interval再试最多
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几个坑
依赖
ps、grep、awk、kill、nohup;探活还要curl。按 JAR 文件名 grep 进程。名字太泛(比如
app.jar)可能误杀。$4叫 env_params,实际是 java 命令行参数。探活只认 200,失败不回滚。