deployJar.groovy 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  1. #!/usr/bin/env groovy
  2. /**
  3. * 强验证发布 SpringBoot  fat-jar
  4. *  - 按端口杀进程
  5. *  - 按 profile 做所有进程级校验
  6. */
  7. def call(Map args = [:]) {
  8. String jarName = args.jarName ?: env.JAR_NAME
  9. String jarPath = args.jarPath ?: env.JAR_PATH
  10. String remoteHost = args.remoteHost ?: env.REMOTE_HOST
  11. String remotePort = args.remotePort ?: env.REMOTE_PORT
  12. String remotePath = args.remotePath ?: env.REMOTE_PATH
  13. String profile = args.profile ?: env.PROFILE
  14. String jvmArgs = args.jvmArgs ?: env.JVM_ARGS ?: ''
  15. String remoteJar = "${remotePath}/${jarName}"
  16. PrintMes("开始发布 ${jarName} [profile=${profile}] 到 ${remoteHost}:${remotePort}", 'yellow')
  17. try {
  18. /* 1. 按端口杀旧进程,并确认端口已释放 */
  19. sh """
  20. ssh -o StrictHostKeyChecking=no ${remoteHost} '
  21. OLD_PID=\$(lsof -ti:${remotePort} || true)
  22. if [ -n "\$OLD_PID" ]; then
  23. echo "杀掉占用端口 ${remotePort} 的进程 \$OLD_PID"
  24. kill -9 \$OLD_PID
  25. sleep 3
  26. fi
  27. if lsof -ti:${remotePort}; then
  28. echo "端口 ${remotePort} 仍未释放,杀进程失败"
  29. exit 1
  30. fi
  31. '
  32. """
  33. /* 2. 备份旧包 */
  34. sh """
  35. ssh -o StrictHostKeyChecking=no ${remoteHost} '
  36. test -f ${remoteJar} && mv ${remoteJar} ${remoteJar}-\$(date +%Y%m%d%H%M) || true
  37. '
  38. """
  39. /* 3. 传包 + MD5 强校验 */
  40. // 1. 在本地计算 MD5
  41. String localMd5 = sh(script: "md5sum ${jarPath} | awk '{print \$1}'", returnStdout: true).trim()
  42. // 2. 传输 JAR 包
  43. sh "scp -o StrictHostKeyChecking=no ${jarPath} ${remoteHost}:${remoteJar}"
  44. // 3. 远程计算 MD5 并返回(Jenkins Pipeline 会自动修剪输出)
  45. String remoteMd5Output = sh(
  46. script: "ssh -o StrictHostKeyChecking=no ${remoteHost} 'md5sum ${remoteJar}'",
  47. returnStdout: true
  48. ).trim()
  49. // 4. 在 Jenkins Groovy 侧处理输出,提取 MD5 值
  50. // 输出格式通常是 "MD5_SUM FILENAME"。我们只需要第一个字段。
  51. String remoteMd5 = remoteMd5Output.split('\\s+')[0]
  52. if (localMd5 != remoteMd5) {
  53. error("MD5 校验失败:本地 ${localMd5} != 远程 ${remoteMd5}")
  54. }
  55. /* 4. 启动新进程 */
  56. sh """
  57. ssh -o StrictHostKeyChecking=no ${remoteHost} '
  58. cd ${remotePath} &&
  59. nohup java ${jvmArgs} -jar ${jarName} --spring.profiles.active=${profile} > server.log 2>&1 &
  60. sleep 5
  61. '
  62. """
  63. /* 5. 按 profile 精确校验:有且仅有 1 个进程 */
  64. String procCount = sh(
  65. script: "ssh -o StrictHostKeyChecking=no ${remoteHost} 'ps -ef | grep \"java.*${jarName}.*--spring.profiles.active=${profile}\" | grep -v grep | wc -l'",
  66. returnStdout: true
  67. ).trim()
  68. if (procCount != "1") {
  69. error("启动校验失败:期望 1 个 ${profile} 进程,实际 ${procCount} 个")
  70. }
  71. /* 6. 端口已被新进程占用 */
  72. String newPid = sh(script: "ssh -o StrictHostKeyChecking=no ${remoteHost} 'lsof -ti:${remotePort}'", returnStdout: true).trim()
  73. if (!newPid) {
  74. error("端口 ${remotePort} 未被新进程监听,启动可能失败")
  75. }
  76. PrintMes("发布成功 ${jarName} [profile=${profile}, pid=${newPid}]", 'green')
  77. } catch (Exception e) {
  78. PrintMes("发布失败 ${jarName} : ${e.message}", 'red')
  79. error('deploySpringBoot failed')
  80. }
  81. }