[{"data":1,"prerenderedAt":739},["ShallowReactive",2],{"content:\u002Fposts\u002Fjenkins-deploy-nodejs-springboot":3,"surround:\u002Fposts\u002Fjenkins-deploy-nodejs-springboot":727},{"id":4,"title":5,"body":6,"categories":701,"date":703,"description":704,"draft":705,"extension":706,"image":707,"meta":708,"navigation":710,"path":711,"permalink":707,"pinned":705,"published":707,"readingTime":712,"recommend":707,"references":707,"seo":717,"sitemap":718,"stem":719,"tags":720,"type":725,"updated":703,"__hash__":726},"content\u002Fposts\u002Fposts\u002Fjenkins-deploy-nodejs-springboot.md","CI\u002FCD 手册：Jenkins 自动化发布全流程",{"type":7,"value":8,"toc":676},"minimark",[9,19,24,27,44,52,57,67,73,76,83,91,98,102,105,108,122,146,150,162,165,171,201,220,223,229,235,241,245,258,268,272,281,284,385,394,398,402,408,419,425,435,441,444,447,453,465,507,511,514,522,526,534,538,544,550,556,583,589,592,601,605,611,649,653,656,662,667,670],[10,11,15],"alert",{"type":12,"icon":13,"title":14},"info","tabler:robot","AI 迁移提示",[16,17,18],"p",{},"本文由 AI 协助从旧站迁移，尚未完成逐篇人工审校；内容如有疏漏，将在复核后修订。",[20,21,23],"h2",{"id":22},"一部署-jenkins","一、部署 Jenkins",[16,25,26],{},"Jenkins 有两种安装方式：",[28,29,30,38],"ul",{},[31,32,33,37],"li",{},[34,35,36],"strong",{},"组件方式","：直接安装在宿主机或虚拟机上，适合对环境隔离要求不高的场景。",[31,39,40,43],{},[34,41,42],{},"Docker 方式","：容器化部署，迁移方便，推荐优先选择。",[16,45,46,47,51],{},"本文以 Docker 方式为主，使用 ",[48,49,50],"code",{"code":50},"docker-compose"," 管理容器配置。",[53,54,56],"h3",{"id":55},"docker-compose-配置","Docker Compose 配置",[58,59,65],"pre",{"className":60,"code":62,"language":63,"meta":64},[61],"language-yaml","services:\n  jenkins:\n    image: jenkins\u002Fjenkins:lts       # 使用最新长期维护版本\n    container_name: jenkins-docker\n    restart: always\n    privileged: true                  # 允许容器内调用宿主机 Docker\n    user: root                        # 避免挂载目录权限问题\n    ports:\n      - \"9001:8080\"                   # Web UI 访问端口\n      - \"50000:50000\"                 # Agent 节点连接端口\n    volumes:\n      - .\u002Fjenkins_home:\u002Fvar\u002Fjenkins_home          # Jenkins 数据持久化\n      - .\u002Fweb_home:\u002Fweb_home                      # 前端构建产物目录\n      - \u002Fvar\u002Frun\u002Fdocker.sock:\u002Fvar\u002Frun\u002Fdocker.sock # 挂载宿主机 Docker Socket\n      - \u002Fusr\u002Fbin\u002Fdocker:\u002Fusr\u002Fbin\u002Fdocker           # 挂载宿主机 Docker 命令\n    environment:\n      - TZ=Asia\u002FShanghai\n","yaml","",[48,66,62],{"__ignoreMap":64},[68,69,70],"blockquote",{},[16,71,72],{},"如果因网络限制无法拉取镜像，请自行配置 Docker 镜像加速地址。",[53,74,75],{"id":75},"获取初始密码",[16,77,78,79,82],{},"容器启动后，访问 ",[48,80,81],{"code":81},"http:\u002F\u002F\u003C服务器IP>:9001"," 进入初始化向导。初始管理员密码可通过以下命令获取：",[58,84,89],{"className":85,"code":87,"language":88,"meta":64},[86],"language-bash","# 在宿主机上直接读取（推荐）\ncat jenkins_home\u002Fsecrets\u002FinitialAdminPassword\n\n# 通过 docker exec 读取\ndocker exec -it jenkins-docker cat \u002Fvar\u002Fjenkins_home\u002Fsecrets\u002FinitialAdminPassword\n","bash",[48,90,87],{"__ignoreMap":64},[16,92,93],{},[94,95],"img",{"alt":96,"src":97},"Jenkins 初始化向导页面",".\u002Fimages\u002Fjenkins-deploy-20260330215645871.webp",[20,99,101],{"id":100},"二配置运行环境","二、配置运行环境",[16,103,104],{},"Jenkins 本身只是一个调度框架，真正编译代码需要配置对应的工具链。",[53,106,107],{"id":107},"安装必要插件",[16,109,110,111,114,115,114,118,121],{},"进入 ",[48,112,113],{"code":113},"Manage Jenkins"," → ",[48,116,117],{"code":117},"Plugins",[48,119,120],{"code":120},"Available plugins","，搜索并安装以下插件：",[28,123,124,130,136],{},[31,125,126,129],{},[34,127,128],{},"NodeJS Plugin","：提供 Node.js \u002F npm 编译环境",[31,131,132,135],{},[34,133,134],{},"Maven Integration","：提供 Maven 构建支持",[31,137,138,141,142,145],{},[34,139,140],{},"SSH Pipeline Steps"," 或 ",[34,143,144],{},"Publish Over SSH","：用于将构建产物推送到远程服务器",[53,147,149],{"id":148},"配置-jdk-maven-nodejs","配置 JDK \u002F Maven \u002F Node.js",[16,151,110,152,114,154,157,158,161],{},[48,153,113],{"code":113},[48,155,156],{"code":156},"Global Tool Configuration","，分别配置 JDK、Maven 和 Node.js 的本地路径。",[34,159,160],{},"注意不要勾选「自动安装」","，使用预先下载好的本地版本，避免因网络问题导致构建失败。",[16,163,164],{},"各工具的推荐下载地址：",[58,166,169],{"className":167,"code":168,"language":88,"meta":64},[86],"# JDK 1.8\nhttps:\u002F\u002Frepo.huaweicloud.com\u002Fjava\u002Fjdk\u002F8u201-b09\u002Fjdk-8u201-linux-x64.tar.gz\n# JDK 17\nhttps:\u002F\u002Fmirrors.huaweicloud.com\u002Fopenjdk\u002F17\u002Fopenjdk-17_linux-x64_bin.tar.gz\n\n# Maven 3.9.9\nhttps:\u002F\u002Farchive.apache.org\u002Fdist\u002Fmaven\u002Fmaven-3\u002F3.9.9\u002Fbinaries\u002Fapache-maven-3.9.9-bin.tar.gz\n\n# Node.js 22.x\nhttps:\u002F\u002Fnodejs.org\u002Fdist\u002Fv22.22.2\u002Fnode-v22.22.2-linux-x64.tar.xz\n",[48,170,168],{"__ignoreMap":64},[68,172,173,176],{},[16,174,175],{},"更多版本请访问官方镜像站：",[28,177,178,187,194],{},[31,179,180,181],{},"OpenJDK：",[182,183,184],"a",{"href":184,"rel":185},"https:\u002F\u002Fmirrors.huaweicloud.com\u002Fopenjdk\u002F",[186],"nofollow",[31,188,189,190],{},"Maven 3：",[182,191,192],{"href":192,"rel":193},"https:\u002F\u002Farchive.apache.org\u002Fdist\u002Fmaven\u002Fmaven-3\u002F",[186],[31,195,196,197],{},"Node.js：",[182,198,199],{"href":199,"rel":200},"https:\u002F\u002Fnodejs.org\u002Fen\u002Fabout\u002Fprevious-releases",[186],[10,202,203,206],{"type":12},[16,204,205],{},"如需手动安装 JDK 或 Maven 并配置环境变量，参见：",[28,207,208,214],{},[31,209,210],{},[182,211,213],{"href":212},"\u002Fposts\u002Fjdk-install","Linux 手动安装 JDK 1.8",[31,215,216],{},[182,217,219],{"href":218},"\u002Fposts\u002Fmaven-install","Linux 安装 Maven 并配置私服镜像",[16,221,222],{},"配置完成后效果如下：",[16,224,225],{},[94,226],{"alt":227,"src":228},"配置 JDK",".\u002Fimages\u002Fjenkins-deploy-20260330094429913.webp",[16,230,231],{},[94,232],{"alt":233,"src":234},"配置 Maven",".\u002Fimages\u002Fjenkins-deploy-20260330094458253.webp",[16,236,237],{},[94,238],{"alt":239,"src":240},"配置 Node.js",".\u002Fimages\u002Fjenkins-deploy-20260330094504611.webp",[53,242,244],{"id":243},"添加-git-凭据","添加 Git 凭据",[16,246,247,248,114,250,253,254,257],{},"如果源码托管在私有仓库，需要提前配置访问凭据。进入 ",[48,249,113],{"code":113},[48,251,252],{"code":252},"Credentials","，点击 ",[48,255,256],{"code":256},"Add Credentials","，根据实际情况选择用户名密码或 SSH Key 类型。",[10,259,261],{"type":260},"warning",[16,262,263,264,267],{},"凭据（密码、SSH Key、Token）",[34,265,266],{},"切勿硬编码","在 Jenkinsfile 或构建脚本中，应统一通过 Jenkins Credentials 管理，在脚本中以变量形式引用，防止敏感信息随代码泄露。",[20,269,271],{"id":270},"三job-类型说明","三、Job 类型说明",[10,273,274],{"type":12},[16,275,276,277,280],{},"对于前后端混合部署场景，强烈推荐使用 ",[34,278,279],{},"流水线（Pipeline）"," 类型，配合 Jenkinsfile 版本化管理部署逻辑，将构建流程纳入 Git 版本控制，便于回滚和团队协作。",[16,282,283],{},"Jenkins 提供了多种 Job 类型，根据项目复杂度按需选择：",[285,286,287,303],"table",{},[288,289,290],"thead",{},[291,292,293,297,300],"tr",{},[294,295,296],"th",{},"类型",[294,298,299],{},"定位",[294,301,302],{},"适用场景",[304,305,306,320,333,346,359,372],"tbody",{},[291,307,308,314,317],{},[309,310,311],"td",{},[34,312,313],{},"流水线 (Pipeline)",[309,315,316],{},"进阶首选",[309,318,319],{},"前后端混合部署、生产环境、需要版本化管理构建逻辑",[291,321,322,327,330],{},[309,323,324],{},[34,325,326],{},"Freestyle project",[309,328,329],{},"入门级",[309,331,332],{},"临时脚本、单一 Shell 命令测试",[291,334,335,340,343],{},[309,336,337],{},[34,338,339],{},"Maven 项目",[309,341,342],{},"后端专用",[309,344,345],{},"纯 Java\u002FSpring Boot 项目，不涉及前端",[291,347,348,353,356],{},[309,349,350],{},[34,351,352],{},"多配置项目",[309,354,355],{},"兼容性测试",[309,357,358],{},"需要在多个 OS 或 JDK 版本下并行测试",[291,360,361,366,369],{},[309,362,363],{},[34,364,365],{},"文件夹 (Folder)",[309,367,368],{},"任务管理",[309,370,371],{},"Job 数量多时用于分类归档",[291,373,374,379,382],{},[309,375,376],{},[34,377,378],{},"多分支流水线",[309,380,381],{},"高级自动化",[309,383,384],{},"规范团队开发，自动为各 Git 分支创建独立流水线",[16,386,387,390,391,393],{},[34,388,389],{},"流水线","的核心优势在于：流程可视化、脚本可版本化管理，且支持复杂的条件逻辑（例如前端编译失败时自动终止后端发布）。",[34,392,326],{}," 操作简单但配置无法入 Git，逻辑复杂后难以维护。",[20,395,397],{"id":396},"四部署-nodejs-前端项目","四、部署 Node.js 前端项目",[53,399,401],{"id":400},"创建-freestyle-job","创建 Freestyle Job",[16,403,404,405,407],{},"新建一个 ",[48,406,326],{"code":326},"，在源码管理中填入 Git 仓库地址。",[16,409,410,411,414,415,418],{},"在 ",[34,412,413],{},"Environment"," 选项卡中，勾选 ",[34,416,417],{},"Provide Node & npm bin\u002F folder to PATH","，选择前面配置好的 Node.js 环境。",[16,420,421],{},[94,422],{"alt":423,"src":424},"选择 Node.js 环境",".\u002Fimages\u002Fjenkins-deploy-20260330134105734.webp",[16,426,110,427,430,431,434],{},[34,428,429],{},"Build Steps","，点击「增加构建步骤」，选择 ",[34,432,433],{},"Execute shell","，填入以下构建脚本。",[16,436,437],{},[94,438],{"alt":439,"src":440},"添加构建步骤",".\u002Fimages\u002Fjenkins-deploy-20260330134307769.webp",[53,442,443],{"id":443},"编写构建脚本",[16,445,446],{},"以下脚本涵盖依赖安装、打包和产物同步三个阶段，根据实际项目调整变量即可：",[58,448,451],{"className":449,"code":450,"language":88,"meta":64},[86],"# ---------- 基础配置（按需修改）----------\nSOURCE_DIR=\"dist\"          # 打包输出目录（如 dist、build）\nTARGET_PATH=\"\u002Fweb_home\u002F\"   # 宿主机 Nginx 静态文件目录\n\necho \">>> 阶段 1: 配置镜像源并安装依赖\"\nnpm config set registry https:\u002F\u002Fregistry.npmmirror.com\nnpm install\n\necho \">>> 阶段 2: 执行构建 (产物目录: ${SOURCE_DIR})\"\nnpm run build\n\necho \">>> 阶段 3: 同步产物至 Nginx 目录\"\nif [ -d \"${SOURCE_DIR}\" ]; then\n    echo \"确认发现产物目录: ${SOURCE_DIR}\"\n    mkdir -p ${TARGET_PATH}\n    rm -rf ${TARGET_PATH}\u002F*\n    cp -r ${SOURCE_DIR}\u002F* ${TARGET_PATH}\u002F\n    echo \">>> 发布成功！已同步至 ${TARGET_PATH}\"\nelse\n    echo \"ERROR: 找不到目录 '${SOURCE_DIR}'，请检查 package.json 中 build 脚本的输出路径。\"\n    exit 1\nfi\n",[48,452,450],{"__ignoreMap":64},[10,454,455],{"type":260},[16,456,457,460,461,464],{},[48,458,459],{"code":459},"rm -rf ${TARGET_PATH}\u002F*"," 会清空目标目录下的所有文件。执行前请务必确认 ",[48,462,463],{"code":463},"TARGET_PATH"," 变量赋值正确，避免误删宿主机重要数据。",[10,466,468],{"type":467},"tip",[28,469,470,476,489],{},[31,471,472,475],{},[34,473,474],{},"依赖缓存慢："," 建议在 Jenkins Node 插件配置中为 npm 全局设置国内镜像源，避免每次构建都重新下载。",[31,477,478,481,482,141,485,488],{},[34,479,480],{},"多环境打包："," 如果有开发、测试、生产等多套环境，打包命令可能是 ",[48,483,484],{"code":484},"npm run build:prod",[48,486,487],{"code":487},"npm run build:test","，请根据实际情况修改脚本。",[31,490,491,494,495,498,499,502,503,506],{},[34,492,493],{},"Nginx 读取权限："," Jenkins 以 ",[48,496,497],{"code":497},"root"," 用户运行，生成的 ",[48,500,501],{"code":501},"dist"," 目录权限也是 root。若 Nginx 以非 root 用户运行，需在脚本末尾追加 ",[48,504,505],{"code":505},"chmod -R 755 ${TARGET_PATH}"," 确保读取权限。",[53,508,510],{"id":509},"进阶nodejs-项目的-pipeline-实现","进阶：Node.js 项目的 Pipeline 实现",[16,512,513],{},"如果你厌倦了在网页上点点点，可以使用 Pipeline 脚本。它的优势在于：哪怕 Jenkins 挂了，你的构建逻辑依然保存在代码仓库里。",[58,515,520],{"className":516,"code":518,"language":519,"meta":64},[517],"language-groovy","\u002F\u002F 在 Jenkinsfile 或 Pipeline 脚本中填入\npipeline {\n    agent any\n    environment {\n        \u002F\u002F 必须对应“全局工具配置”中的名称\n        NODE_HOME = tool 'node22' \n        WEB_HOME  = \"\u002Fweb_home\"\n    }\n    stages {\n        stage('环境准备与安装') {\n            steps {\n                \u002F\u002F 使用 withEnv 正确注入 PATH，直接赋值 PATH 在 Pipeline 中不生效\n                withEnv([\"PATH+NODE=${NODE_HOME}\u002Fbin\"]) {\n                    sh \"npm config set registry https:\u002F\u002Fregistry.npmmirror.com\"\n                    sh \"npm install\"\n                    sh \"npm run build\"\n                }\n            }\n        }\n        stage('发布产物') {\n            steps {\n                \u002F\u002F 这里的路径需与 Docker 挂载路径一致\n                sh \"rm -rf ${WEB_HOME}\u002F* && cp -r dist\u002F* ${WEB_HOME}\u002F\"\n            }\n        }\n    }\n}\n","groovy",[48,521,518],{"__ignoreMap":64},[20,523,525],{"id":524},"五部署-spring-boot-后端项目","五、部署 Spring Boot 后端项目",[10,527,528],{"type":12},[16,529,530,531,533],{},"Jenkins 以 ",[48,532,497],{"code":497}," 用户运行时，Maven 构建生成的 jar 文件权限同为 root。通过 Docker 部署时，镜像内通常以非 root 用户运行，请确认 Dockerfile 中的用户与文件权限匹配，避免启动时出现权限拒绝错误。",[53,535,537],{"id":536},"创建-maven-job","创建 Maven Job",[16,539,540,541,543],{},"新建 Job 时选择「构建一个 Maven 项目」类型。在 ",[34,542,413],{}," 选项卡中指定 JDK 版本。",[16,545,546],{},[94,547],{"alt":548,"src":549},"指定 JDK 版本",".\u002Fimages\u002Fjenkins-deploy-20260330164359188.webp",[16,551,410,552,555],{},[34,553,554],{},"Build"," 配置中：",[28,557,558,568,577],{},[31,559,560,563,564,567],{},[34,561,562],{},"Root POM","：默认填 ",[48,565,566],{"code":566},"pom.xml","，如果 pom 文件不在根目录请填写相对路径",[31,569,570,573,574],{},[34,571,572],{},"Goals and options","：",[48,575,576],{"code":576},"clean package -U -Dmaven.test.skip=true",[31,578,579,582],{},[34,580,581],{},"Settings file","：配置自定义的 Maven settings.xml（如需指定私有仓库或镜像源）",[16,584,585],{},[94,586],{"alt":587,"src":588},"Maven 构建配置",".\u002Fimages\u002Fjenkins-deploy-20260330164506910.webp",[53,590,443],{"id":591},"编写构建脚本-1",[16,593,594,595,597,598,600],{},"Maven 打包完成后，在 ",[34,596,429],{}," 中追加一个 ",[34,599,433],{}," 步骤，负责将 jar 包构建为 Docker 镜像并重新部署容器：",[16,602,603],{},[94,604],{"alt":439,"src":440},[58,606,609],{"className":607,"code":608,"language":88,"meta":64},[86],"# ---------- 1. 基础参数配置（按需修改）----------\nAPP_NAME=\"my-app-dev\"                          # 容器和镜像名称\nDOCKERFILE_PATH=\"module\u002Fsubmodule\"             # Dockerfile 所在的相对路径\nIMAGE_TAG=\"my-org\u002F${APP_NAME}:${BUILD_NUMBER}\" # 镜像标签，使用构建号保证唯一性\nNETWORK_MODE=\"host\"\nSPRING_PROFILE=\"dev\"\nSERVER_PORT=\"8080\"\n\n# ---------- 2. 构建 Docker 镜像 ----------\necho \">>> 阶段 1: 构建镜像\"\ncd ${DOCKERFILE_PATH}\ndocker build -t ${IMAGE_TAG} .\ndocker tag ${IMAGE_TAG} ${APP_NAME}:latest\n\n# ---------- 3. 清理旧容器 ----------\necho \">>> 阶段 2: 清理旧容器\"\nif [ \"$(docker ps -aq -f name=^${APP_NAME}$)\" ]; then\n    echo \"发现旧容器 ${APP_NAME}，正在清理...\"\n    docker rm -f ${APP_NAME}\nfi\n\n# ---------- 4. 启动新容器 ----------\necho \">>> 阶段 3: 启动新容器\"\ndocker run -d \\\n  --name ${APP_NAME} \\\n  --restart=always \\\n  --network ${NETWORK_MODE} \\\n  ${APP_NAME}:latest \\\n  --spring.profiles.active=${SPRING_PROFILE} \\\n  --server.port=${SERVER_PORT}\n\n# ---------- 5. 验证部署结果 ----------\necho \">>> 阶段 4: 验证部署\"\nsleep 3\nif [ \"$(docker ps -q -f name=^${APP_NAME}$)\" ]; then\n    echo \">>> [SUCCESS] 服务 ${APP_NAME} 已启动，端口: ${SERVER_PORT}\"\nelse\n    echo \">>> [ERROR] 容器启动失败，最近 20 行日志：\"\n    docker logs --tail 20 ${APP_NAME}\n    exit 1\nfi\n\n# 清理构建过程产生的虚悬镜像\ndocker image prune -f\n",[48,610,608],{"__ignoreMap":64},[10,612,613],{"type":467},[28,614,615,629,639],{},[31,616,617,620,621,624,625,628],{},[34,618,619],{},"Dockerfile 路径："," ",[48,622,623],{"code":623},"cd ${DOCKERFILE_PATH}"," 是为了让 Docker 构建上下文正确，使 Dockerfile 内的 ",[48,626,627],{"code":627},"COPY"," 指令能找到编译好的 jar 文件。",[31,630,631,634,635,638],{},[34,632,633],{},"镜像标签："," 使用 ",[48,636,637],{"code":637},"${BUILD_NUMBER}"," 作为版本号，可在 Jenkins 构建历史中精确追溯每次部署对应的镜像。",[31,640,641,644,645,648],{},[34,642,643],{},"多模块项目："," 如果是多模块 Maven 项目，",[48,646,647],{"code":647},"DOCKERFILE_PATH"," 需要指向包含 Dockerfile 的子模块目录。",[53,650,652],{"id":651},"进阶spring-boot-项目的-pipeline-实现","进阶：Spring Boot 项目的 Pipeline 实现",[16,654,655],{},"对于后端项目，Pipeline 可以更优雅地管理 Docker 镜像的生命周期，实现“构建-打标-重启-清理”的丝滑循环。",[58,657,660],{"className":658,"code":659,"language":519,"meta":64},[517],"pipeline {\n    agent any\n    environment {\n        MAVEN_HOME = tool 'maven3.9'\n        APP_NAME   = \"hf-wy-contract-dev\"\n        PORT       = \"31006\"\n    }\n    stages {\n        stage('Maven 打包') {\n            steps {\n                \u002F\u002F 使用 withEnv 正确注入 PATH，直接赋值 PATH 在 Pipeline 中不生效\n                withEnv([\"PATH+MAVEN=${MAVEN_HOME}\u002Fbin\"]) {\n                    sh \"mvn clean package -Dmaven.test.skip=true\"\n                }\n            }\n        }\n        stage('Docker 构建与运行') {\n            steps {\n                sh \"\"\"\n                docker build -t ${APP_NAME}:${BUILD_NUMBER} .\n                docker tag ${APP_NAME}:${BUILD_NUMBER} ${APP_NAME}:latest\n                docker rm -f ${APP_NAME} || true\n                docker run -d --name ${APP_NAME} --network host --restart always \\\n                ${APP_NAME}:latest --server.port=${PORT}\n                \"\"\"\n            }\n        }\n    }\n    post {\n        success {\n            sh \"docker image prune -f\" \u002F\u002F 自动清理临时镜像，保持磁盘整洁\n            echo \"✅ 部署完成！\"\n        }\n    }\n}\n\n",[48,661,659],{"__ignoreMap":64},[16,663,664],{},[34,665,666],{},"架构是冷的，但折腾的心是热的。",[16,668,669],{},"如果你也从这篇文章里感受到了那么一点点自动化带来的爽感，欢迎在评论区留个言。哪怕只是一个 +1，也能让这个孤独的博主觉得：",[16,671,672],{},[673,674,675],"em",{},"原来，真的有人在看啊。",{"title":64,"searchDepth":677,"depth":677,"links":678},4,[679,685,690,691,696],{"id":22,"depth":680,"text":23,"children":681},2,[682,684],{"id":55,"depth":683,"text":56},3,{"id":75,"depth":683,"text":75},{"id":100,"depth":680,"text":101,"children":686},[687,688,689],{"id":107,"depth":683,"text":107},{"id":148,"depth":683,"text":149},{"id":243,"depth":683,"text":244},{"id":270,"depth":680,"text":271},{"id":396,"depth":680,"text":397,"children":692},[693,694,695],{"id":400,"depth":683,"text":401},{"id":443,"depth":683,"text":443},{"id":509,"depth":683,"text":510},{"id":524,"depth":680,"text":525,"children":697},[698,699,700],{"id":536,"depth":683,"text":537},{"id":591,"depth":683,"text":443},{"id":651,"depth":683,"text":652},[702],"服务","2026-03-30","详细记录如何通过 Jenkins 搭建自动化流水线，实现 Node.js 前端项目与 Spring Boot 后端项目的打包、上传及一键部署。",false,"md",null,{"slots":709},{},true,"\u002Fposts\u002Fjenkins-deploy-nodejs-springboot",{"text":713,"minutes":714,"time":715,"words":716},"12 min read",11.635,698100,2327,{"title":5,"description":704},{"loc":711},"posts\u002Fposts\u002Fjenkins-deploy-nodejs-springboot",[721,722,723,724],"Jenkins","Node.js","Spring Boot","运维","tech","pnw0mhgzvGwRPjvhkpWmVj5VU9-FfWulNyjPz5-E8Go",[728,734],{"title":729,"path":730,"stem":731,"date":732,"type":733,"children":-1},"一个字一个字敲出来的文章，为什么没人看？——写给“自闭”中的自己","\u002Fposts\u002Fwhy-nobody-reads-my-blog","posts\u002Fposts\u002Fwhy-nobody-reads-my-blog","2026-03-29","story",{"title":735,"path":736,"stem":737,"date":738,"type":725,"children":-1},"为了护住我那几块硬盘：我的 UPS 监控“三部曲","\u002Fposts\u002Fups-monitoring-trilogy-nodejs","posts\u002Fposts\u002Fups-monitoring-trilogy-nodejs","2026-04-01",1788712222953]