优化GitHub Action工作流:解决NestJS部署超时及内存不足问题
我正尝试将NestJS项目部署至Digital Ocean Droplet(配置25GB SSD、2GB RAM),但遇到两个问题:
- 通过SSH执行
npm i时超时 - 执行
npm run build时Node.js因内存不足崩溃
我的项目仅包含5个端点,结构并不复杂,不确定是Droplet配置问题还是其他原因导致耗时过长。想请教:
- 如何优化当前的GitHub Action工作流?
- 如何避免服务停机并缩短部署时间?
当前使用的GitHub Action工作流配置如下:
name: Node.js CI on: push: branches: - main jobs: build: runs-on: ubuntu-20.04 strategy: matrix: node-version: [16.x] steps: - uses: actions/checkout@v2 - name: Use Node.js ${{ matrix.node-version }} uses: actions/setup-node@v2 with: node-version: ${{ matrix.node-version }} cache: 'npm' - run: npm i - run: npm run build - name: executing remote ssh commands using password uses: appleboy/ssh-action@v0.1.7 with: host: ${{ secrets.SSH_HOST }} username: ${{ secrets.SSH_USER }} port: ${{ secrets.SSH_PORT }} key: ${{ secrets.SSH_KEY }} script: | if [ -d "\/opt\/nodejs_project" ]; then rm -Rf \/opt\/nodejs_project; fi mkdir \/opt\/nodejs_project cp ~\/secret\/.env.prod \/opt\/nodejs_project\/ cp ~\/secret\/db.cert \/opt\/nodejs_project\/ - name: Copy dist to VPS uses: appleboy/scp-action@master with: host: ${{ secrets.SSH_HOST }} username: ${{ secrets.SSH_USER }} port: ${{ secrets.SSH_PORT }} key: ${{ secrets.SSH_KEY }} # Selecting all folders except "node_modules" source: '.,!node_modules' # The path is based on the directory where the user logged into the server starts target: '\/opt\/nodejs_project' - name: executing remote ssh commands using password uses: appleboy/ssh-action@v0.1.7 with: host: ${{ secrets.SSH_HOST }} username: ${{ secrets.SSH_USER }} port: ${{ secrets.SSH_PORT }} key: ${{ secrets.SSH_KEY }} script: | chown -R root \/opt\/nodejs_project cd \/opt\/nodejs_project npm i --production pm2 restart all
一、GitHub Action工作流核心优化
1. 转移构建/依赖安装到GitHub Runner,减少服务器压力
你现在已经在Runner上做了npm i和build,但又在服务器重复执行npm i --production,完全没必要。直接在Runner上完成生产依赖安装,只传必要文件到服务器:
- 在Runner执行
npm ci --only=production替代服务器端的依赖安装 - SCP仅传输
dist、package.json、package-lock.json(或yarn.lock),不用传整个项目目录
调整后的关键步骤示例:
# ... 保留checkout、setup-node步骤 - run: npm ci - run: npm run build - run: npm ci --only=production # 在Runner上安装生产依赖 - name: Copy necessary files to VPS uses: appleboy/scp-action@master with: # ... 主机信息不变 source: 'dist, package.json, package-lock.json' target: '/opt/nodejs_project' # 删掉服务器端的npm i --production步骤
2. 合并SSH操作,减少连接次数
当前多次调用ssh-action和scp-action,每次都要重新建立SSH连接,增加耗时。把多个SSH命令合并成一个步骤:
- name: Deploy to VPS uses: appleboy/ssh-action@v0.1.7 with: # ... 主机信息不变 script: | # 备份旧目录(可选,用于回滚) if [ -d "/opt/nodejs_project" ]; then mv /opt/nodejs_project /opt/nodejs_project.bak; fi mkdir -p /opt/nodejs_project cp ~/secret/.env.prod /opt/nodejs_project/ cp ~/secret/db.cert /opt/nodejs_project/ chown -R root /opt/nodejs_project cd /opt/nodejs_project pm2 reload nest-app # 替换原来的restart all
3. 升级Action版本
当前用的actions/checkout@v2、actions/setup-node@v2版本较旧,升级到最新稳定版(比如v4)能获得性能提升和bug修复。
二、避免服务停机的策略
1. 蓝绿部署
在服务器维护两个部署目录(比如/opt/nodejs_v1和/opt/nodejs_v2),每次部署到空闲目录,启动新服务后再切换流量:
- 检查当前活跃版本(通过PM2应用名称判断)
- 部署到非活跃目录并启动新PM2进程
- 调用健康检查端点验证新服务正常
- 修改Nginx反向代理配置指向新服务端口
- 重启Nginx后停止旧服务
2. 用PM2 reload替代restart
pm2 restart all会一次性重启所有进程,导致短暂停机。换成pm2 reload [你的应用名称],PM2会逐个重启进程,保证服务始终有实例运行。
3. 健康检查前置
在切换流量前,先调用NestJS的健康检查端点(可以用@nestjs/terminus实现),确认新服务正常响应后再完成部署。
三、服务器端临时缓解措施(不用升级Droplet)
1. 增加Node.js内存限制
执行build时通过NODE_OPTIONS分配更多内存:
NODE_OPTIONS="--max-old-space-size=1536" npm run build
(2GB内存的Droplet,给Node分配1.5GB比较合理)
2. 启用Swap分区
给Droplet添加Swap分区,内存不足时用磁盘临时替代:
# 创建1GB swap文件 sudo fallocate -l 1G /swapfile sudo chmod 600 /swapfile sudo mkswap /swapfile sudo swapon /swapfile # 设置开机自动挂载 echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
3. 用pnpm替代npm
pnpm内存占用比npm低很多,安装依赖更快。在Runner和服务器都换成pnpm:
# 在GitHub Action中配置pnpm - uses: actions/setup-node@v4 with: node-version: 16.x cache: 'pnpm' - run: npm install -g pnpm - run: pnpm install - run: pnpm run build
内容的提问来源于stack exchange,提问作者sahil

