Jenkinsfile 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367
  1. properties([
  2. parameters([
  3. [$class: 'ChoiceParameter',
  4. choiceType: 'PT_SINGLE_SELECT',
  5. description: '选择要部署 API or UI',
  6. name: 'modulePrefix',
  7. randomName: 'choice-parameter-56313144329613978',
  8. script: [
  9. $class: 'GroovyScript',
  10. fallbackScript: [
  11. classpath: [],
  12. sandbox: true,
  13. script:
  14. 'return[\'Could not get Env\']'
  15. ],
  16. script: [
  17. classpath: [],
  18. sandbox: true,
  19. script:
  20. 'return["java","ui"]'
  21. ]
  22. ]
  23. ],
  24. [$class: 'CascadeChoiceParameter',
  25. choiceType: 'PT_SINGLE_SELECT',
  26. description: '选择要部署的项目',
  27. name: 'module',
  28. randomName: 'choice-parameter-56313144562178619',
  29. referencedParameters: 'modulePrefix',
  30. script: [
  31. $class: 'GroovyScript',
  32. fallbackScript: [
  33. classpath: [],
  34. sandbox: true,
  35. script:
  36. 'return[\'Could not get Environment from Env Param\']'
  37. ],
  38. script: [
  39. classpath: [],
  40. sandbox: true,
  41. script:
  42. ''' if (modulePrefix.equals("java")){
  43. return["storlead-api"]
  44. }
  45. else if(modulePrefix.equals("ui")){
  46. return["smarttrade-platform","su-user-center"]
  47. }
  48. '''
  49. ]
  50. ]
  51. ]
  52. ])
  53. ])
  54. pipeline {
  55. options {
  56. timestamps()
  57. }
  58. environment {
  59. GROUPID = readMavenPom().getGroupId()//com.storlead
  60. ARTIFACTID = readMavenPom().getArtifactId()//sp-project
  61. VERSION = readMavenPom().getVersion()//1.0
  62. }
  63. // 参数
  64. parameters {
  65. choice(
  66. name: 'profile',
  67. choices: ['test', 'prod','uat'],
  68. description: '选择要部署的配置文件'
  69. )
  70. }
  71. agent any
  72. stages {
  73. stage('处理参数') {
  74. steps {
  75. script {
  76. echo "处理参数"
  77. script {
  78. //服务器配置
  79. MODULE_PREFIX = "${params.modulePrefix}"
  80. SERVER_SSH_PORT = 53023
  81. SERVER_HOST = "node1.storlead.com"
  82. SWARM_INIT_REPLICAS_NUM = 1
  83. //服务配置
  84. API_PORT = 9200
  85. API_REMOTE_DEBUG_PORT = 9201
  86. // 与 application-*.yml 的 server.servlet.context-path 保持一致,不能用 module 名
  87. API_NAMESPACE_RESTFUL = "router/rest"
  88. //DOCKER配置
  89. DOCKER_REGISTRY = "reg-aliyun"
  90. DOCKER_HOST = "registry.cn-shenzhen.aliyuncs.com"
  91. //Docker私服登陆url https://registry-vpc.cn-shenzhen.aliyuncs.com
  92. DOCKER_LOGIN_REGISTRY = "https://${DOCKER_HOST}"
  93. BUILD_PREFIX = "storlead-huawei"
  94. // 部署服务名:java 用根工程 artifactId(storlead-saas-platform),再拼环境后缀 -test/-prod/-uat
  95. // Maven 模块名 params.module(storlead-api)只用于编译和 jar 路径,不作为运行时服务名
  96. DEPLOY_APP = "${params.module}"
  97. if (params.modulePrefix == "java") {
  98. DEPLOY_APP = "${ARTIFACTID}"
  99. }
  100. DEPLOY_NAME = "${DEPLOY_APP}-${params.profile}"
  101. //镜像完整名称 registry.cn-shenzhen.aliyuncs.com/storlead-huawei/storlead-saas-platform-test:1.0
  102. DOCKER_IMG_NAME = "${DOCKER_HOST}/${BUILD_PREFIX}/${DEPLOY_NAME}:${VERSION}"
  103. IMG_NAME = "${BUILD_PREFIX}/${DEPLOY_NAME}:${VERSION}"
  104. echo "部署服务名 DEPLOY_NAME=${DEPLOY_NAME}"
  105. PUSH_DOCK_IMG = true
  106. SERVER_HOST_NAME = "test1"
  107. echo "为指定 module 设定对应 服务器及端口"
  108. echo "处理 ${params.module} 项目"
  109. if (params.modulePrefix == "java") {
  110. echo "---- storelad-centre-api"
  111. script {
  112. if (params.profile == "test") {
  113. SWARM_INIT_REPLICAS_NUM = 1
  114. SERVER_HOST = "test1.storlead.com"
  115. API_PORT = 10070
  116. API_REMOTE_DEBUG_PORT = 10071
  117. SERVER_HOST_NAME = "test1"
  118. } else if (params.profile == "prod") {
  119. SERVER_HOST = "110.41.82.21"
  120. API_PORT = 10070
  121. API_REMOTE_DEBUG_PORT = 10071
  122. SERVER_HOST_NAME = "node1"
  123. }
  124. }
  125. } else if (params.modulePrefix == "ui") {
  126. script {
  127. if (params.profile == "test") {
  128. SERVER_HOST = "test1.storlead.com"
  129. } else if (params.profile == "prod") {
  130. SERVER_HOST = "110.41.82.21"
  131. }
  132. }
  133. }
  134. echo "处理MODULE_NAMESPACE_RESTFUL=${API_NAMESPACE_RESTFUL}"
  135. echo "处理镜像推送"
  136. }
  137. }
  138. }
  139. }
  140. stage('Node 编译') {
  141. when {
  142. environment name: 'modulePrefix', value: 'ui'
  143. }
  144. agent {
  145. docker {
  146. // image "registry-vpc.cn-shenzhen.aliyuncs.com/storlead/node:19-alpine"
  147. image "node:20-alpine"
  148. args '-v /app:/app/:z -v /etc/localtime:/etc/localtime:ro -u root'
  149. reuseNode true
  150. }
  151. }
  152. steps {
  153. withCredentials([
  154. usernamePassword(credentialsId: 'storleadHW', usernameVariable: 'usernameHW', passwordVariable: 'passwordHW'),
  155. usernamePassword(credentialsId: 'storlead', usernameVariable: 'username', passwordVariable: 'password')
  156. ]) {
  157. script {
  158. echo "发布 UI 代码"
  159. def remote = [:]
  160. remote.allowAnyHosts = true
  161. remote.name = "${SERVER_HOST}"
  162. remote.host = "${SERVER_HOST}"
  163. remote.port = 53023
  164. remote.user = username
  165. remote.password = password
  166. if (params.profile == "prod") {
  167. remote.port = 22
  168. remote.user = "root"
  169. remote.user = usernameHW
  170. remote.password = passwordHW
  171. }
  172. // if (params.module == 'sp-user-mobile-center') {
  173. sh """cd ${params.modulePrefix}/${params.module} && npm install && npm run ${params.profile}"""
  174. // } else {
  175. // sh """cd ${params.modulePrefix}/${params.module} && npm install && npm run ${params.profile}"""
  176. // }
  177. // sh """cd ${params.modulePrefix}/${params.module} && npm run ${params.profile}"""
  178. sshCommand remote: remote, command: "mkdir -p /app/sp/ui/app-centre/${params.profile}/${params.module}"
  179. sshCommand remote: remote, command: "rm -rf /app/sp/ui/app-centre/${params.profile}/${params.module}/dist/*"
  180. echo "/app/sp/ui/app-centre/${params.profile}/${params.module}/*"
  181. sshPut remote: remote, from: "${params.modulePrefix}/${params.module}/dist/", into: "/app/sp/ui/app-centre/${params.profile}/${params.module}/"
  182. echo "${params.modulePrefix}/${params.module}/dist/,/app/sp/ui/app-centre/${params.profile}/${params.module}/"
  183. sshCommand remote: remote, command: "cp -rf /app/sp/ui/app-centre/${params.profile}/${params.module}/dist/* /app/sp/ui/app-centre/${params.profile}/${params.module}/"
  184. }
  185. }
  186. }
  187. }
  188. stage('Maven 编译') {
  189. when {
  190. environment name: 'modulePrefix', value: 'java'
  191. }
  192. agent {
  193. docker {
  194. // 与 Temurin 17 一致(含 Maven);若本地无此镜像会自动拉取
  195. image 'maven:3-eclipse-temurin-17'
  196. //让docker 使用 host 宿主机的 m2仓库 使用root用户来运行 以让指定的~/.m2/config/setting.xml 阿里加速下载maven 依赖生效
  197. args '-v /app:/app:z -v $HOME/.m2:/root/.m2:z -u root'
  198. reuseNode true
  199. }
  200. }
  201. steps {
  202. echo "编译 API 代码"
  203. // 根 reactor 模块路径为 java/storlead-api,-pl 用 :artifactId 按构件名选择
  204. sh "mvn clean package -U -am -pl :${params.module} -P${params.profile} -Dmaven.test.skip=true"
  205. }
  206. }
  207. // 项目打包到镜像并推送到镜像仓库
  208. stage('构建Docker镜像') {
  209. when {
  210. environment name: 'modulePrefix', value: 'java'
  211. }
  212. steps {
  213. echo "构建Dcoker镜像-------------------"
  214. script {
  215. docker.withRegistry(DOCKER_LOGIN_REGISTRY, DOCKER_REGISTRY) {
  216. // 使用 Jenkins 节点已有的 eclipse-temurin:17
  217. BASE_IMAGE = 'eclipse-temurin:17'
  218. sh "docker pull ${BASE_IMAGE} || true"
  219. sh """
  220. echo '
  221. FROM ${BASE_IMAGE}
  222. RUN apt-get update && apt-get install -y --no-install-recommends curl && rm -rf /var/lib/apt/lists/*
  223. COPY java/${params.module}/target/${params.module}-${VERSION}.jar app.jar
  224. EXPOSE ${API_PORT}
  225. EXPOSE ${API_REMOTE_DEBUG_PORT}
  226. HEALTHCHECK --interval=1m --timeout=10s CMD curl -f http://localhost:${API_PORT}/${API_NAMESPACE_RESTFUL}/actuator/health
  227. ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom", "-Djava.awt.headless=true","-Dspring.profiles.active=${params.profile}", "-Dserver.port=${API_PORT}", "-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:${API_REMOTE_DEBUG_PORT}","-jar","/app.jar"]' > Dockerfile
  228. docker build -t ${DOCKER_IMG_NAME} .
  229. """
  230. sh "docker push ${DOCKER_IMG_NAME}"
  231. }
  232. }
  233. }
  234. }
  235. stage('启动服务') {
  236. when {
  237. environment name: 'modulePrefix', value: 'java'
  238. }
  239. steps {
  240. withCredentials([
  241. usernamePassword(credentialsId: 'storleadHW', usernameVariable: 'usernameHW', passwordVariable: 'passwordHW'),
  242. usernamePassword(credentialsId: 'storlead', usernameVariable: 'username', passwordVariable: 'password'),
  243. usernamePassword(credentialsId: 'reg-aliyun', usernameVariable: 'aliUsername', passwordVariable: 'aliPassword')
  244. ]) {
  245. script {
  246. echo "启动服务"
  247. echo "${aliPassword}"
  248. echo "docker login -u ${aliUsername} -p ${aliPassword} ${DOCKER_LOGIN_REGISTRY}"
  249. def remote = [:]
  250. remote.name = "${SERVER_HOST}"
  251. remote.host = "${SERVER_HOST}"
  252. remote.port = 53023
  253. remote.user = username
  254. remote.password = password
  255. remote.allowAnyHosts = true
  256. //因为使用 docker swarm 发布 只能在 manage node 执行部署
  257. if (params.profile == "prod") {
  258. remote.port = 22
  259. remote.user = "root"
  260. remote.user = usernameHW
  261. remote.password = passwordHW
  262. }
  263. if (PUSH_DOCK_IMG) {
  264. //docker 认证
  265. sshCommand remote: remote, command: "docker login -u ${aliUsername} -p ${aliPassword} ${DOCKER_LOGIN_REGISTRY}"
  266. //如果选择推送才使用远程的版本 如果不选择推送到远程则直接运行本机刚打包的镜像版本即可
  267. sshCommand remote: remote, command: "docker pull ${DOCKER_IMG_NAME}"
  268. }
  269. sh """
  270. echo '
  271. version: "3.7"
  272. services:
  273. ${DEPLOY_NAME}:
  274. image: ${DOCKER_IMG_NAME}
  275. deploy:
  276. replicas: ${SWARM_INIT_REPLICAS_NUM}
  277. placement:
  278. constraints:
  279. - 'node.hostname == ${SERVER_HOST_NAME}'
  280. resources:
  281. limits:
  282. memory: 2G
  283. reservations:
  284. memory: 512M
  285. update_config:
  286. parallelism: 1
  287. delay: 1m
  288. restart_policy:
  289. condition: on-failure
  290. # 最多连续重试3次如果仍然失败则放弃重试
  291. max_attempts: 3
  292. healthcheck:
  293. test: ["CMD", "curl", "-f", "http://localhost:${API_PORT}/${API_NAMESPACE_RESTFUL}/actuator/health"]
  294. interval: 1m
  295. timeout: 5s
  296. retries: 3
  297. start_period: 30s
  298. environment:
  299. - "TZ=Asia/Shanghai"
  300. - "SPRING_PROFILES_ACTIVE=${params.profile}"
  301. - "JAVA_OPTS=-Xms512m -Xmx1024m"
  302. volumes:
  303. - /app:/app
  304. - /mnt/vdb/storlead:/mnt/vdb/storlead
  305. ports:
  306. - "${API_PORT}:${API_PORT}"
  307. - "${API_REMOTE_DEBUG_PORT}:${API_REMOTE_DEBUG_PORT}"
  308. networks:
  309. - ${ARTIFACTID}
  310. extra_hosts:
  311. - "www.storlead.com:172.18.194.161"
  312. - "node1:110.41.82.21"
  313. - "node2:110.41.174.46"
  314. - "node3:121.37.226.174"
  315. - "test1.storlead.com:172.18.194.168"
  316. - "test2.storlead.com:172.18.194.163"
  317. networks:
  318. ${ARTIFACTID}:
  319. driver: overlay
  320. external: true
  321. ' > ${DEPLOY_NAME}-stack.yml
  322. """
  323. // 全部都以swarm 模式启动
  324. // 旧栈名为 storlead-api-{profile},改名后需先删掉,否则 10070 端口冲突
  325. sshCommand remote: remote, command: "docker stack ls --format '{{.Name}}' | grep -qx 'storlead-api-${params.profile}' && docker stack rm storlead-api-${params.profile} && sleep 20 || true"
  326. sshPut remote: remote, from: "${DEPLOY_NAME}-stack.yml", into: "/docker/sp/${DEPLOY_NAME}-stack.yml"
  327. sshCommand remote: remote, command: "docker stack deploy --resolve-image=always --with-registry-auth -c /docker/sp/${DEPLOY_NAME}-stack.yml ${DEPLOY_NAME}"
  328. }
  329. }
  330. }
  331. }
  332. }
  333. }