体验 Whisper ,本地离线部署自己的 ASR 语音识别服务

news2024/11/23 11:29:29

需求背景

最近看视频,过几天后经常忘记内容,所以有了把重点内容总结提炼到自己知识库的需求,这涉及到了提取视频中的音频数据、离线语音识别等功能。

提取视频中的音频数据,可以使用格式工厂或 FFmpeg 等工具, FFmpeg 可以使用命令 ffmpeg -i test.mp4 -f mp3 -vn test.mp3 将视频文件转换为 MP3 格式的音频文件,其中,-i test.mp4-i 参数指定输入文件,这里的输入文件是 test.mp4-f mp3-f 参数用于指定输出文件的格式,这里指定为 mp3 格式。-vn:这个参数告诉 FFmpeg 在处理文件时忽略视频流,即不处理视频数据,只处理音频数据。test.mp3:指定了输出文件的名称,即转换后的音频文件将被保存为当前目录下的 test.mp3

Whisper 是由 OpenAI 创建的开源通用语音识别项目,是一个经过大量的音频数据训练出来的支持多任务处理的语言识别模型。项目的 GitHub 仓库地址是 OpenAI Whisper ,在仓库介绍的 Show and tell 中发现这个项目 whisper-asr-webservice ,它在 whisper 基础上提供了 web 界面,并且可以用 docker 部署。

下面基于开源项目,体验了本地离线部署自己的 ASR 语音识别服务的过程。

镜像构建

克隆 whisper-asr-webservice 仓库到本地,切换到稳定版本,

git clone https://github.com/ahmetoner/whisper-asr-webservice.git
cd whisper-asr-webservice
git checkout v1.3.0

进入仓库目录,分析 Dockerfile.gpu 文件内容,这个 Dockerfile 使用了多阶段构建,

# 第一阶段:构建 FFmpeg
# 使用 Debian bookworm slim 版本作为基础镜像来构建 FFmpeg
FROM debian:bookworm-slim AS ffmpeg
# 安装 FFmpeg 编译所需的依赖包
RUN export DEBIAN_FRONTEND=noninteractive \
    && apt-get -qq update \
    && apt-get -qq install --no-install-recommends \
    build-essential \
    git \
    pkg-config \
    yasm \
    ca-certificates \
    && rm -rf /var/lib/apt/lists/*
# 从 GitHub 克隆特定版本的 FFmpeg 源码
RUN git clone https://github.com/FFmpeg/FFmpeg.git --depth 1 --branch n6.1.1 --single-branch /FFmpeg-6.1.1
# 设置工作目录为 FFmpeg 源代码目录
WORKDIR /FFmpeg-6.1.1
# 配置 FFmpeg 编译选项,禁用不需要的功能,以优化最终构建
RUN PATH="$HOME/bin:$PATH" PKG_CONFIG_PATH="$HOME/ffmpeg_build/lib/pkgconfig" ./configure \
      --prefix="$HOME/ffmpeg_build" \
      --pkg-config-flags="--static" \
      --extra-cflags="-I$HOME/ffmpeg_build/include" \
      --extra-ldflags="-L$HOME/ffmpeg_build/lib" \
      --extra-libs="-lpthread -lm" \
      --ld="g++" \
      --bindir="$HOME/bin" \
      --disable-doc \
      --disable-htmlpages \
      --disable-podpages \
      --disable-txtpages \
      --disable-network \
      --disable-autodetect \
      --disable-hwaccels \
      --disable-ffprobe \
      --disable-ffplay \
      --enable-filter=copy \
      --enable-protocol=file \
      --enable-small && \
    PATH="$HOME/bin:$PATH" make -j$(nproc) && \
    make install && \
    hash -r

# 第二阶段:准备 Swagger UI
# 使用 swagger-ui 镜像作为基础来提供 API 文档界面
FROM swaggerapi/swagger-ui:v5.9.1 AS swagger-ui

# 第三阶段:应用程序与 FFmpeg 集成
# 使用 NVIDIA CUDA 基础镜像,准备 Python 环境
FROM nvidia/cuda:11.8.0-cudnn8-runtime-ubuntu22.04
# 设置 Python 版本和 poetry 虚拟环境路径
ENV PYTHON_VERSION=3.10
ENV POETRY_VENV=/app/.venv
# 安装 Python 和 pip
RUN export DEBIAN_FRONTEND=noninteractive \
    && apt-get -qq update \
    && apt-get -qq install --no-install-recommends \
    python${PYTHON_VERSION} \
    python${PYTHON_VERSION}-venv \
    python3-pip \
    && rm -rf /var/lib/apt/lists/*
# 创建软链接以确保 python 和 pip 命令指向正确的版本
RUN ln -s -f /usr/bin/python${PYTHON_VERSION} /usr/bin/python3 && \
    ln -s -f /usr/bin/python${PYTHON_VERSION} /usr/bin/python && \
    ln -s -f /usr/bin/pip3 /usr/bin/pip
# 创建并配置 Python 虚拟环境
RUN python3 -m venv $POETRY_VENV \
    && $POETRY_VENV/bin/pip install -U pip setuptools \
    && $POETRY_VENV/bin/pip install poetry==1.6.1
# 添加 poetry 虚拟环境到 PATH
ENV PATH="${PATH}:${POETRY_VENV}/bin"
# 设置工作目录并复制项目依赖文件
WORKDIR /app

COPY poetry.lock pyproject.toml ./
# 配置 poetry 并安装项目依赖
RUN poetry config virtualenvs.in-project true
RUN poetry install --no-root
# 复制项目文件和依赖
COPY . .
COPY --from=ffmpeg /FFmpeg-6.1.1 /FFmpeg-6.1.1
COPY --from=ffmpeg /root/bin/ffmpeg /usr/local/bin/ffmpeg
COPY --from=swagger-ui /usr/share/nginx/html/swagger-ui.css swagger-ui-assets/swagger-ui.css
COPY --from=swagger-ui /usr/share/nginx/html/swagger-ui-bundle.js swagger-ui-assets/swagger-ui-bundle.js
# 再次运行 poetry 安装以确保所有依赖都已安装
RUN poetry install
# 安装 PyTorch
RUN $POETRY_VENV/bin/pip install torch==1.13.0+cu117 -f https://download.pytorch.org/whl/torch
# 暴露服务端口
EXPOSE 9000
# 定义容器启动时执行的命令
CMD gunicorn --bind 0.0.0.0:9000 --workers 1 --timeout 0 app.webservice:app -k uvicorn.workers.UvicornWorker

第一阶段:构建 FFmpeg

  1. FROM debian:bookworm-slim AS ffmpeg:使用 debian:bookworm-slim 作为基础镜像开始第一个构建阶段,并命名这个阶段为 ffmpeg

  2. 安装构建依赖:

    • RUN export DEBIAN_FRONTEND=noninteractive:设置环境变量,使 apt-get 在非交互模式下运行,避免安装过程中的提示。
    • apt-get -qq update:更新包列表,-qq 选项让更新过程尽可能静默。
    • apt-get -qq install --no-install-recommends:安装必要的包,不安装推荐的依赖包以减少镜像大小。
    • 包括 build-essentialgitpkg-configyasmca-certificates:这些都是编译 FFmpeg 所需的工具和库。
    • rm -rf /var/lib/apt/lists/*:清理 apt 缓存以减小镜像大小。
  3. RUN git clone https://github.com/FFmpeg/FFmpeg.git --depth 1 --branch n6.1.1 --single-branch /FFmpeg-6.1.1:从 GitHub 克隆 FFmpeg 仓库的特定分支(n6.1.1),使用 --depth 1 进行浅克隆以减少下载的数据量。

  4. WORKDIR /FFmpeg-6.1.1:将工作目录切换到 FFmpeg 源码目录。

  5. 编译安装 FFmpeg:

    • 设置编译配置,禁用了一些不需要的功能以缩小最终安装的大小,并优化为静态链接。
    • 使用 make -j$(nproc) 并行编译 FFmpeg,以加速编译过程。
    • make install 将编译好的程序安装到指定目录。
    • hash -r 刷新 shell 的命令缓存。

第二阶段:准备 Swagger UI

  1. FROM swaggerapi/swagger-ui:v5.9.1 AS swagger-ui:这是一个新的阶段,使用 Swagger UI 的官方镜像作为基础,用于提供 API 文档界面。

第三阶段:应用程序与 FFmpeg 集成

  1. FROM nvidia/cuda:11.8.0-cudnn8-runtime-ubuntu22.04:这是最终构建阶段的基础镜像,使用 NVIDIA CUDA 的镜像来支持 GPU 加速。这意味着基础镜像是 nvidia/cuda:11.8.0-cudnn8-runtime-ubuntu22.04,而不是 debian:bookworm-slim

  2. 安装 Python 环境和依赖:

    • 类似于第一阶段,设置非交互模式,更新包列表,并安装 Python 以及相关工具。
    • 创建和配置 Python 虚拟环境,安装 Poetry 作为依赖管理工具。
  3. 配置工作目录 /app 并安装 Python 依赖。

  4. 从之前的阶段复制必要的文件和目录到当前镜像中,包括 FFmpeg 可执行文件和 Swagger UI 静态文件。

  5. 安装额外的 Python 依赖,包括 PyTorch。

  6. 配置容器启动时运行的命令,使用 gunicorn 启动 Web 服务。

修改 Dockerfile

针对本地机器环境,修改 Dockerfile 文件内容,
- 本地机器环境,输入命令 nvidia-smi 查询显卡驱动信息,当前 Nvidia 驱动版本为 515.48.07 ,CUDA 版本最高可以支持到 11.7 ,whisper-asr-webservice 仓库 v1.3.0 版本原来的 Dockerfile 文件使用的基础镜像为 nvidia/cuda:11.8.0-cudnn8-runtime-ubuntu22.04 ,修改为 nvidia/cuda:11.7.1-cudnn8-runtime-ubuntu22.04
- 在构建的镜像中包含 net-toolscurlwgetvimiputils-ping 等工具,方便容器启动后的开发调试。

修改后的例子如下,

# 修改的部分
FROM nvidia/cuda:11.7.1-cudnn8-runtime-ubuntu22.04

# 未修改的部分
ENV PYTHON_VERSION=3.10
ENV POETRY_VENV=/app/.venv

# 修改的部分
RUN export DEBIAN_FRONTEND=noninteractive \
    && apt-get -qq update \
    && apt-get -qq install --no-install-recommends \
    python${PYTHON_VERSION} \
    python${PYTHON_VERSION}-venv \
    python3-pip \
    # ----- add software begin -----
    curl \
    wget \
    vim \
    net-tools \
    iputils-ping \
    # ----- add software end -----
    && rm -rf /var/lib/apt/lists/*

# 剩下的部分保持不变
# ...

修改 Dockerfile 后,需要重新构建镜像。

docker build -f Dockerfile.gpu -t whisper-asr-webservice-gpu:v1.3.0 .

镜像部署

docker save -o whisper_asr_webservice_gpu_v1.3.0.tar whisper-asr-webservice-gpu:v1.3.0
docker load --input whisper_asr_webservice_gpu_v1.3.0.tar
docker tag <IMAGE ID> whisper_asr_webservice_gpu:v1.3.0
docker run -d --gpus '"device=0"' -p 9000:9000 -e ASR_MODEL=base -e ASR_ENGINE=openai_whisper -e ASR_MODEL_PATH=/data/whisper -v /data/whisper:/data/whisper whisper_asr_webservice_gpu:v1.3.0

观察容器日志,

[2024-04-06 06:38:59 +0000] [27] [INFO] Starting gunicorn 21.2.0
[2024-04-06 06:38:59 +0000] [27] [INFO] Listening at: http://127.0.0.1:9000 (27)
[2024-04-06 06:38:59 +0000] [27] [INFO] Using worker: uvicorn.workers.UvicornWorker
[2024-04-06 06:38:59 +0000] [28] [INFO] Booting worker with pid: 28
OpenBLAS blas_thread_init: pthread_create failed for thread 1 of 64: Operation not permitted
OpenBLAS blas_thread_init: RLIMIT_NPROC -1 current, -1 max
[2024-04-06 06:38:59 +0000] [27] [ERROR] Worker (pid:28) was sent SIGINT!
[2024-04-06 06:38:59 +0000] [29] [INFO] Booting worker with pid: 29
OpenBLAS blas_thread_init: pthread_create failed for thread 1 of 64: Operation not permitted
OpenBLAS blas_thread_init: RLIMIT_NPROC -1 current, -1 max
[2024-04-06 06:38:59 +0000] [27] [ERROR] Worker (pid:29) was sent SIGINT!

结合 GPT 给出的排查步骤逐步验证,发现容器中的进程(例如 OpenBLAS 创建的线程)可能因为 Docker 的安全性配置(特别是 seccomp 默认策略)而受到限制,导致操作被禁止。尝试使用更宽松的安全选项启动容器,例如 --security-opt seccomp=unconfined--privileged。这些选项会降低容器的安全性,因此最好分析和定制 Seccomp 策略。

再次观察容器日志,成功启动了容器服务。

[2024-04-06 06:50:26 +0000] [27] [INFO] Starting gunicorn 21.2.0
[2024-04-06 06:50:26 +0000] [27] [INFO] Listening at: http://127.0.0.1:9000 (27)
[2024-04-06 06:50:26 +0000] [27] [INFO] Using worker: uvicorn.workers.UvicornWorker
[2024-04-06 06:50:26 +0000] [28] [INFO] Booting worker with pid: 28
[2024-04-06 06:50:30 +0000] [28] [INFO] Started server process [28]
[2024-04-06 06:50:30 +0000] [28] [INFO] Waiting for application startup.
[2024-04-06 06:50:30 +0000] [28] [INFO] Application startup complete.

验证使用

这个项目提供了 2 个接口:

  • /asr ,自动语音识别,上传语音或视频文件,输出文本。
  • /detect-language ,检测语言,检测上传文件中使用的语言。仅处理前 30 秒。

自动语音识别服务 /asr

  • encode: 识别之前通过 FFmpeg 编码音视频文件。
  • task: transcribe ,(默认)任务,转写上传的文件,中文语音识别为中文文字,英文语音识别为英文文字;translate ,无论源文件中是什么语言,识别后翻译为英文再输出。
  • language: 告诉接口源文件是什么语言,可不指定,可以自动识别出来,如果指定错了,输出的结果是不对的。
  • initial_prompt: prompt 工程可以提高语音识别结果的准确性。
  • word_timestamps: 单词级别的时间戳,在输出为 json 时起作用,可输出每个单词的开始时间、结束时间、识别正确的可能性。
  • output: 输出格式。
  • 可以启用语音活动检测(VAD),通过参数 vad_filter 过滤掉没有语音的音频部分(目前仅支持 Faster Whisper)。

请求 URL 查询参数如下:

NameValues
audio_fileFile
encodetrue (default)
tasktranscribe, translate
languageen (default is auto recognition)
initial_promptprompt
word_timestampsfalse (default)
outputtext (default), json, vtt, srtt, tsv

使用 curl 的示例请求,

curl -X POST -H "content-type: multipart/form-data" -F "audio_file=@test.mp3" http://127.0.0.1:9000/asr?output=json

响应(JSON),

  • text :包含完整的文字记录
  • segments :每个分段包含一个条目。每个条目提供 timestampstranscripttoken idsword leveltimestamps、其他元数据
  • language :检测到或提供的语言(as a language code)

语言检测服务 /detect-language

检测上传文件中使用的语言,仅处理前 30 秒。返回包含以下字段的 json:

  • detected_language: “english”
  • language_code: “en”

使用 curl 的示例请求,

curl -X POST -H "content-type: multipart/form-data" -F "audio_file=@test.mp3" http://127.0.0.1:9000/detect-language
{"detected_language":"chinese","language_code":"zh"}

针对标点符号,和中英文混杂的情况,可以加入 prompt 提升识别的准确率:‘这是一段中文、英文混合的录音,输出请记得加标点符号。’

curl -X POST -H "content-type: multipart/form-data" -F "initial_prompt="这是一段中文、英文混合的录音,输出请记得加标点符号。"" -F "audio_file=@test.mp3" http://127.0.0.9:9000/asr > test.txt

base 模型对短音频的识别效果不错,一般模型越大,识别效果越好。

在看解决方案的过程中,我还发现一个好玩的语音日记应用 Alog ,只需要在想说话的时候,用它录音就行了,它会自动帮你把语音转化为文字,然后同时保存在手机里,转写服务支持 iOS 系统自带或者 Whisper ,自带的仅限于 1 分钟的音频时长。还可以开启 AI 总结功能,自定义提示词,对转写内容总结,输出一份标准日记。


微信公众号「padluo」,分享数据科学家的自我修养,既然遇见,不如一起成长。关注【数据分析】公众号,后台回复【文章】,获得整理好的【数据分析】文章全集。

数据分析二维码.gif

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1986798.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

详细解析socket

我最近开了几个专栏&#xff0c;诚信互三&#xff01; > |||《算法专栏》&#xff1a;&#xff1a;刷题教程来自网站《代码随想录》。||| > |||《C专栏》&#xff1a;&#xff1a;记录我学习C的经历&#xff0c;看完你一定会有收获。||| > |||《Linux专栏》&#xff1…

书生大模型实战营第三期——入门岛——Git基础知识

第三关&#xff1a;Git基础知识 任务如下&#xff1a; 任务描述 破冰活动&#xff1a;自我介绍 每位参与者提交一份自我介绍。 提交地址&#xff1a;GitHub - InternLM/Tutorial: LLM&VLM Tutorial 的 camp3 分支&#xff5e;实践项目&#xff1a;构建个人项目 创建一个个人…

PDF发票解析并将信息回填到前端(2)前端页面

本人前端基础薄弱&#xff0c;此处的前端仅仅是一个练习展示 1. 创建一个前端项目 打开终端使用以下命令创建一个基于webpack模板的新项目 vue init webpack my-project输入命令之后一直点击enter知道项目创建完成即可 进入项目&#xff0c;安装并运行 $ cd my-project //…

ExtJS生成日历组件

文章目录 1.添加日历组件代码2.引入日历组件,创建了补签和取消签到,以及翻页显示的事件 1.添加日历组件代码 首先找到自己项目中对应的Ext的目录,并将日历组件添加到calendar这个目录下,我的是KwDatePicker.js 日历组件代码如下: Ext.define(Ext.calendar.KwDatePicker, {ex…

python six模块是什么

six模块是Python2和3兼容性库&#xff0c;它是为了解决Python2和Python3代码兼容性而产生的&#xff0c;众所周知Python2和Python3版本的分裂给Python开发者们带来了很大的烦恼&#xff0c;为了使代码同时兼容两个版本&#xff0c;往往要增加大量的代码&#xff0c;典型的就有u…

著名人工智能新经济数字经济新能源新质生产力讲师培训师教授专家唐兴通分享人工智能社会学商业模式创新人工智能就业工作与教育学习出海跨境数字化转型数字营销数字销售

添加图片注释&#xff0c;不超过 140 字&#xff08;可选&#xff09; 2024 年是人工智能在工作中真正应用的一年。根据微软和领英进行的调查&#xff08;2024年5月&#xff09;&#xff0c;在过去六个月中&#xff0c;生成式人工智能的使用量几乎翻了一番&#xff0c;全球75%的…

领夹麦克风哪个品牌音质最好?八月领夹式麦克风排行榜

随着移动互联技术的飞速发展&#xff0c;视频内容的消费与创作正以前所未有的速度增长。从直播带货的兴起&#xff0c;到短视频平台的火爆&#xff0c;音频质量作为内容体验的重要组成部分&#xff0c;越来越受到创作者的重视。在这一背景下&#xff0c;无线领夹麦克风凭借其小…

6-8 残差网络(ResNet)

随着我们设计越来越深的网络&#xff0c;深刻理解“新添加的层如何提升神经网络的性能”变得至关重要。更重要的是设计网络的能力&#xff0c;在这种网络中&#xff0c;添加层会使网络更具表现力&#xff0c; 为了取得质的突破&#xff0c;我们需要一些数学基础知识。 残差网络…

Web端高效元件库——高端元件,匠心设计

原型设计&#xff0c;不仅是产品从构想到落地不可或缺的桥梁&#xff0c;更是深化用户体验优化策略的核心环节。Axure&#xff0c;作为原型设计领域的领航者&#xff0c;凭借其卓越的交互设计引擎与无与伦比的灵活性&#xff0c;赢得了产品设计师们的广泛赞誉&#xff0c;成为他…

NiFi :1 初识这把“十年一剑”的利器

--->更多内容&#xff0c;请移步“鲁班秘笈”&#xff01;&#xff01;<--- “现在AI和数据处理密不可分&#xff0c;80%的企业可以利用Apache NiFi轻松解决复杂的数据问题&#xff0c;快速完成场景建设。犹如花上百来块钱在家享受一顿不亚于五星级西餐厅的法式大餐。对…

非负数、0和正数 限制最大值且保留两位小数在elementpuls表单中正则验证

一、结构 <el-form-item label="单价:" prop="price"><el-inputv-model.trim="formData.price"placeholder="请输入"@blur="formMethod.fixTwo"><template #append>(元)</template></el-input…

电源芯片测试系统NSAT2000对比传统ATE测试软件有哪些优势?

随着近几年电源芯片的研究和发展&#xff0c;电源芯片向着高度的集成化、智能化的趋势发展&#xff0c;电源芯片想不过去有了更全面的功能&#xff0c;更稳定可靠的优势。相应的市场中电源芯片的测试系统同样也百花齐放&#xff0c;各类ATE测试软件层出不穷。其中纳米软件的NSA…

系统出现高CPU可能风险因素整理

文章目录 死循环无限递归序列化加解密正则表达式计算密集型任务大流量Full GC资源竞争/死锁I/O阻塞外部接口调用 死循环 死循环是最常见的原因之一。当代码中存在无穷循环&#xff08;例如在多线程环境下的HashMap线程不安全问题或分页查询条件不明确导致的无限循环&#xff0…

C++分析红黑树

目录 红黑树介绍 红黑树的性质与平衡控制关系 红黑树节点的插入 情况1&#xff1a;不需要调整 情况2&#xff1a;uncle节点为红色 情况3&#xff1a;uncle节点为黑色 总结与代码实现 红黑树的删除&#xff08;待实现&#xff09; 红黑树的效率 红黑树介绍 红黑树是第二种平衡二…

e6.利用 docker 快速部署自动化运维平台

利用 docker 快速部署自动化运维平台 1. 安装docker2. 拉取镜像3. 启动容器4. 初始化5. 访问测试 Spug 面向中小型企业设计的轻量级无 Agent 的自动化运维平台&#xff0c;整合了主机管理、主机批量执行、主 机在线终端、文件在线上传下载、应用发布部署、在线任务计划、配置中…

基于tcp,html,数据库的在线信息查询系统项目总结

1.项目背景 在线信息查询系统是一种可用于检索和展示各种信息的计算机程序或平台。主要特点包括&#xff1a; 用户接口&#xff1a;通常提供友好的界面&#xff0c;用户可以方便地输入查询条件。 数据存储&#xff1a;系统往往连接到数据库&#xff0c;存储大量信息&#xf…

几个g视频能压缩成几百mb吗?分享5种视频压缩方法

现如今&#xff0c;高清视频已成为我们日常生活和工作中的重要组成部分。然而&#xff0c;随着视频分辨率和时长的增加&#xff0c;文件体积也随之膨胀&#xff0c;给存储和传输带来了巨大挑战。这时候就需要给视频进行压缩处理&#xff0c;下面给大家分享5种视频压缩方法&…

数据标注在不同行业领域的典型应用场景

数据标注产业通过提供高质量的训练和评测数据集&#xff0c;助力人工智能技术在各领域的应用和发展。 1、科学研究 生物医学&#xff1a;标注病理切片、细胞图像、基因组数据&#xff0c;用于疾病诊断和新药研发的模型训练。地球科学&#xff1a;标注卫星图像、遥感数据&…

什么牌子的超声波清洗机好用又实惠?推荐选购这几个品牌

眼镜在日常佩戴中容易变脏&#xff0c;如果不注意清洁和保养&#xff0c;长时间下来不仅会影响镜片的清晰度&#xff0c;还可能对眼部健康和视力产生负面影响。因此&#xff0c;定期清洁眼镜是非常必要的。与传统手洗方法相比&#xff0c;现在有一种更便捷的选择——超声波清洗…

NUXTJS + pm2 部署开源电商PC商城

为了符合各种服务器场景&#xff0c;使用pm2 部署 PC 商城如下 注意&#xff1a;对比package.json代码修改配置即可&#xff0c;如果2024年5月之后下载的代码可以直接用命令启动 服务器安装node pm2 如已安装跳过此章节 在 CentOS 上安装 Node.js 和 PM2 的步骤如下&#xff1…