如何在同级目录构建Next.js项目并实现零停机部署?
独立目录构建+无停机部署方案优化
问题描述
希望在现有仓库的同级目录中构建项目,避免构建失败导致目录混乱。理想流程是在独立目录完成构建,成功后将文件移至运行目录并重启PM2,同时保证服务无停机。当前使用的GitHub Actions部署配置如下:
name: Staging Deploy on: push: branches: [ "main" ] jobs: build: runs-on: Staging environment: Staging strategy: matrix: node-version: [16.x] # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ steps: - uses: actions/checkout@v3 - name: Use Node.js ${{ matrix.node-version }} uses: actions/setup-node@v3 with: node-version: ${{ matrix.node-version }} cache: 'npm' - run: npm install - name: Create env file run: | touch .env ...create env file with repo secrets - run: npm run build --if-present - name: Restart PM2 id: restart_pm2 run: pm2 reload reponame continue-on-error: true - name: Start PM2 if: steps.restart_pm2.outcome != 'success' run: pm2 start pm2.config.js
解决方案
1. 独立构建目录配置
在GitHub Actions中通过指定working-directory设置独立构建目录,建议将其设为现有仓库的同级目录(比如现有仓库路径为/home/user/repo,构建目录设为/home/user/repo-build),构建前先创建并清空该目录,避免残留旧文件。
2. 原子替换避免目录混乱
构建成功后用原子操作替换运行目录,避免中途失败导致目录损坏:
- 先将构建好的产物临时重命名
- 对原运行目录做备份(可选)
- 用
mv完成原子替换(同一文件系统下mv是原子操作,不会出现中间损坏状态)
3. PM2无停机重载保障
PM2的reload命令本身支持无停机重启,替换运行目录后执行pm2 reload --update-env,确保进程加载新目录下的文件;如果进程不存在则自动启动。
4. 完整优化后的GitHub Actions配置
name: Staging Deploy on: push: branches: [ "main" ] jobs: build-and-deploy: runs-on: Staging environment: Staging strategy: matrix: node-version: [16.x] steps: - name: 创建并清空独立构建目录 run: | # 自定义构建目录路径(与现有仓库同级) BUILD_DIR="/home/user/repo-build" RUN_DIR="/home/user/repo-run" mkdir -p $BUILD_DIR rm -rf $BUILD_DIR/* - uses: actions/checkout@v3 with: # 将代码检出到独立构建目录 path: /home/user/repo-build - name: 配置Node.js环境 uses: actions/setup-node@v3 with: node-version: ${{ matrix.node-version }} cache: 'npm' cache-dependency-path: /home/user/repo-build/package-lock.json - name: 安装依赖 working-directory: /home/user/repo-build run: npm install - name: 创建环境变量文件 working-directory: /home/user/repo-build run: | touch .env # 替换为你的环境变量写入逻辑 # echo "API_URL=${{ secrets.API_URL }}" >> .env # echo "DB_PASSWORD=${{ secrets.DB_PASSWORD }}" >> .env - name: 构建项目 working-directory: /home/user/repo-build run: npm run build --if-present - name: 原子替换运行目录 run: | BUILD_DIR="/home/user/repo-build" RUN_DIR="/home/user/repo-run" TEMP_DIR="/home/user/repo-run-temp" BACKUP_DIR="/home/user/repo-run-backup" # 临时重命名构建目录 mv $BUILD_DIR $TEMP_DIR # 备份原运行目录(可选) if [ -d "$RUN_DIR" ]; then mv $RUN_DIR $BACKUP_DIR fi # 原子替换为新运行目录 mv $TEMP_DIR $RUN_DIR # 可选:删除旧备份 # rm -rf $BACKUP_DIR - name: PM2无停机重载/启动 run: | # 重载进程,更新环境变量 pm2 reload reponame --update-env # 如果重载失败(进程不存在),则启动新进程 if [ $? -ne 0 ]; then pm2 start /home/user/repo-run/pm2.config.js --name reponame fi
额外建议
- 添加构建后健康检查:调用服务的健康状态接口,确认服务正常后再完成部署流程
- 使用PM2的
ecosystem.config.js配置文件,明确指定cwd为运行目录,方便统一管理 - 保留备份目录一段时间,出现问题可快速回滚
内容的提问来源于stack exchange,提问作者LUKER
相关产品推荐
相关产品推荐

