记录一次修改 PNPM 版本,部署 NextJs 服务时导致服务器崩溃的问题 😡😡😡

最近在使用 NestJs 和 NextJs 在做一个协同文档 DocFlow,如果感兴趣,欢迎 star,有任何疑问,欢迎加我微信进行咨询 yunmz777

最近在使用 NextJs 开发 DocFlow 协同编辑这个项目,运行了很久都没有问题,最近终于在部署方面出现了问题了,早上的时候发现 ssh 连不上了,一开始并没有太在意,但后来打开京东云控制台,好家伙真的好家伙:

真的小母牛坐飞机牛逼上天了!原来是 CPU 爆了,怪不得连接不上了,当然网站也 502 了:

那是什么原因呢,接下来就要开始慢慢排查了,既然 vscode 连不上,那我们就在官方提供的 ssh 来连接:

输入命令 htop,罪魁祸首马上出现:

原来是 next-server 和 node(运行在 /home/DocFlow-Server/dist/main.js)占用了基本全部的 CPU,那这个原因就很清楚了,那么接下来就可以排查了这个问题的原因了。

首先,可以明确的一点是,之前一直都是同样的方式部署的,没有出现问题,但是为什么今天就一直出现这个问题呢,那肯定是在运行的进程出现了问题。

我项目使用的是 pm2 部署的,那打个日志看看咯:

问题找到了,原来是 pm2 一直在重启,把内存全部占用了,具体是什么原因的已经不好查了,有可能是安装了一些依赖,但是我的 Github Action 是有一些问题没有处理的:

yaml 复制代码
name: Deploy Next.js to JD Cloud

on:
  push:
    branches:
      - main

jobs:
  deploy:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v4.2.0

      - name: Deploy to server via SSH
        uses: appleboy/ssh-action@v1.2.1
        with:
          host: ${{ secrets.SERVER_HOST }}
          username: ${{ secrets.SERVER_USERNAME }}
          port: ${{ secrets.SERVER_PORT }}
          password: ${{ secrets.SERVER_PASSWORD }}
          script: |
            # 加载环境配置
            source ~/.bashrc 2>/dev/null || true
            source ~/.profile 2>/dev/null || true

            # 加载 NVM 环境
            export NVM_DIR="$HOME/.nvm"
            [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"

            # 设置 PATH
            export PATH="$HOME/.nvm/versions/node/*/bin:/usr/local/bin:/usr/bin:/bin:$HOME/.npm-global/bin:$PATH"

            # 设置工作目录
            PROJECT_DIR="/home/DocFlow"
            mkdir -p $PROJECT_DIR
            cd $PROJECT_DIR

            # 克隆或更新代码
            if [ ! -d ".git" ]; then
              echo "Cloning repository..."
              rm -rf ./*
              git clone git@github.com:xun082/DocFlow.git .
            else
              echo "Updating repository..."
              git fetch origin main
              git reset --hard origin/main
            fi

            # 创建 .npmrc 文件 (如果不存在)
            if [ ! -f ".npmrc" ]; then
              echo "Creating .npmrc file..."
              echo "@tiptap-pro:registry=https://registry.tiptap.dev/" > .npmrc
              echo "//registry.tiptap.dev/:_authToken=${{ secrets.TIPTAP_AUTH_TOKEN }}" >> .npmrc
            else
              echo ".npmrc file already exists, skipping creation."
            fi

            # 安装全局依赖 (只在需要时安装)
            command -v pnpm >/dev/null 2>&1 || npm install -g pnpm@9.4.0
            command -v pm2 >/dev/null 2>&1 || npm install -g pm2

            # 安装项目依赖
            pnpm install

            # 构建项目
            pnpm run build

            # 重启应用
            pm2 restart DocFlow 2>/dev/null || pm2 start "pnpm start" --name DocFlow

我这个部署脚本是基本没问题的,至少能跑,现在查看这两天的 commit,想起来有一个这样的操作:

那基本就可以确定是这个问题了。

其他部署方案

PM2 是一种流行的进程管理工具,适用于 Node.js 应用,尤其在小型项目和单机部署时很有效。然而,PM2 的部署流程存在一些局限性:

  1. 依赖管理和版本控制:PM2 需要手动确保服务器上安装正确的 Node.js 和 pnpm 版本。每次代码更新后,你还需要在服务器上重新 构建项目(执行 pnpm run build),这可能会导致生产环境与开发环境之间的不一致,并增加了维护工作。

  2. 服务中断问题:每次代码更新时,PM2 需要 重启应用 来使新版本生效。如果没有正确配置或管理,应用在重启过程中可能会停机一段时间,这对于高可用性要求较高的生产环境来说是个问题。

与 PM2 相比,Docker 自动化部署 通过容器化解决了这些问题,提供了更简洁、更一致的部署流程,特别适用于云端和多环境部署。主要优势包括:

  1. 无需依赖管理和环境配置:Docker 将应用及其所有依赖、环境变量、构建过程封装在 Docker 镜像 中。这样,服务器上无需安装特定版本的 Node.js 或 pnpm,避免了手动配置和版本不一致的问题。你只需要在 构建镜像时 配置好所有的依赖和环境,部署时无需再进行任何安装或构建操作。

  2. 代码问题不会影响运行:如果代码有问题,Docker 使得应用 不会受到当前部署的影响。因为 Docker 镜像已经包含了所有必要的依赖,部署失败时,原先的镜像仍然可以继续运行,确保生产环境不受影响。而在 PM2 部署 时,更新代码后需要重启应用,如果代码有问题,应用会直接停机,导致服务中断。

  3. 无缝的 CI/CD 集成:Docker 完美集成 CI/CD 流程。例如,使用 GitHub Actions,你可以自动构建 Docker 镜像并推送到仓库。服务器只需要 拉取最新镜像,并启动新的容器。如果部署失败,不会影响当前运行的容器,你可以迅速恢复服务,且无需手动修复。相反,PM2 需要重新启动应用,且服务停机时需要手动修复代码。

  4. 快速回滚:由于 Docker 镜像的版本化机制,你可以轻松回滚到之前的稳定版本。即便遇到部署失败的情况,只需要拉取旧版本的镜像,应用立即恢复,不需要手动操作,这为应用提供了更高的可靠性。

总结

PM2 部署中,如果代码有问题,必须手动修复并重启应用,这可能会导致 服务中断,并影响用户体验。而且每次更新都需要在服务器上重新构建项目,这可能导致环境不一致,增加了部署的复杂性。

Docker 部署则通过 容器化 确保应用和环境的一致性,部署失败时不会影响生产环境的运行,原有容器仍可继续工作,且通过 CI/CD 流程可以自动恢复和快速回滚,不需要人工干预。即使代码存在问题,原有的容器仍可保持正常服务,确保应用的高可用性。

因此,对于生产环境,Docker 提供了比 PM2 更稳定、高效、自动化的部署方式,尤其适合大规模、高可用的应用部署。

相关推荐
浪裡遊3 小时前
css面试题1
开发语言·前端·javascript·css·vue.js·node.js
钮钴禄·爱因斯晨3 小时前
不只是字符串:Actix-web 路由与 FromRequest的类型安全艺术
前端·安全
杜子不疼.3 小时前
仓颉语言构造函数深度实践指南
java·服务器·前端
IT_陈寒3 小时前
我用这5个JavaScript性能优化技巧,让页面加载速度提升了60%
前端·人工智能·后端
清空mega3 小时前
Flask入门学习指南
后端·python·flask
风一样的美狼子3 小时前
仓颉语言 LinkedList 链表实现深度解析
java·服务器·前端
zero13_小葵司3 小时前
JavaScript 性能优化系列(六)接口调用优化 - 6.4 错误重试策略:智能重试机制,提高请求成功率
开发语言·javascript·ecmascript
风象南3 小时前
SpringBoot 时间轮实现延时任务
后端
Victor3563 小时前
Redis(93)Redis的数据加密机制是什么?
后端