使用 PM2 守护 Flask 应用:从安装到配置详解

random-pic-api

简介

PM2 是一个功能强大的 Node.js 进程管理工具,不仅适用于 JavaScript 项目,也能很好地守护 Python 应用。本文将详细介绍如何在服务器上使用 PM2 来守护一个基于 Flask 的 Python 应用。


1. 安装环境

安装 Node.js

PM2 是基于 Node.js 的工具,因此需要先安装 Node.js 环境:

1
2
3
4
sudo apt update && sudo apt install curl -y

curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash -
sudo apt install nodejs -y

安装 PM2

使用 npm 全局安装 PM2:

1
npm install pm2 -g

验证安装是否成功:

1
2
pm2 -v  # 查看版本
pm2 list # 列出当前管理的所有进程

2. 准备 Flask 应用

生成依赖文件

在 Python 项目中,通常会使用 requirements.txt 来管理依赖包。如果尚未生成该文件,可以通过以下命令创建:

1
pip freeze > requirements.txt

配置虚拟环境(可选但推荐)

为了保持项目的独立性,建议为 Flask 应用创建一个 Python 虚拟环境。

1
2
python3 -m venv my_flask_env  # 创建虚拟环境
source my_flask_env/bin/activate # 激活虚拟环境

激活后,在虚拟环境中安装项目依赖:

1
pip install -r requirements.txt

编写启动脚本

Flask 应用通常需要通过 gunicornuwsgi 来运行,以提高性能和并发处理能力。

假设你的 Flask 应用文件为 app.py,以下是使用 gunicorn 启动的配置示例:

1
2
3
4
5
# 安装 gunicorn
pip install gunicorn

# 使用 gunicorn 启动 Flask 应用
gunicorn -b "0.0.0.0:8000" --workers 4 app:app

你也可以为 gunicorn 配置一个参数文件(如 gunicorn_config.py),以进一步优化性能:

1
2
3
4
5
6
7
8
# gunicorn_config.py

bind = '0.0.0.0:8000' # 绑定地址和端口
workers = 4 # 工作进程数,根据 CPU 核心调整
worker_class = 'gevent' # 使用异步工作者模式
timeout = 60 # 请求超时时间(秒)
accesslog = '-' # 输出访问日志到标准输出
errorlog = '-' # 输出错误日志到标准输出

3. 使用 PM2 守护 Flask 应用

直接启动

你可以将 gunicorn 命令通过 PM2 管理:

1
pm2 start gunicorn -c gunicorn_config.py wsgi:app --name=flask_app

使用自定义脚本

为了更好地管理,可以编写一个启动脚本(如 start.sh):

1
2
3
4
5
6
7
#!/bin/bash

# 激活虚拟环境(如果需要)
source /path/to/my_flask_env/bin/activate

# 使用 gunicorn 启动 Flask 应用
gunicorn -c gunicorn_config.py wsgi:app

然后通过 PM2 管理该脚本:

1
pm2 start start.sh --name=flask_app

配置文件(可选)

你也可以为 PM2 编写一个 JSON 配置文件(如 ecosystem.config.js),以便管理和部署多个应用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
module.exports = {
apps: [
{
name: "Flask App",
script: "./start.sh", // 启动脚本路径
cwd: "/path/to/your/project", // 工作目录
env: {
NODE_ENV: "production",
},
watch: false, // 是否监听文件变化(默认 false)
max_memory_restart: "100M", // 内存限制,超过后自动重启
},
],
};

然后运行:

1
pm2 start ecosystem.config.js

4. PM2 命令列表

命令描述
pm2 start <app.js> [--name=<name>]启动应用
pm2 stop <app_name/id>停止应用
pm2 restart <app_name/id>重启应用
pm2 delete <app_name/id>删除应用
pm2 list列出所有应用
pm2 show <app_name/id>显示应用详细信息
pm2 info <app_name/id>显示应用信息(等同于 show)
pm2 status显示 PM2 状态
pm2 logs显示所有应用日志
pm2 logs <app_name/id>显示指定应用日志
pm2 flush清空所有日志文件
pm2 reloadLogs重新加载日志
pm2 deploy部署应用
pm2 deploy <configuration_file> <environment>使用配置文件部署到指定环境
pm2 monit监控所有应用
pm2 cpu <app_name/id>显示应用 CPU 使用情况
pm2 memory <app_name/id>显示应用内存使用情况
pm2 startup生成启动脚本
pm2 save保存当前应用列表
pm2 resurrect恢复之前保存的应用列表
pm2 update更新 PM2 到最新版本
pm2 scale <app_name/id> <number_of_instances>扩展应用实例数量
pm2 gracefulReload <app_name/id>优雅重启应用
pm2 trigger <app_name/id> <action_name>触发自定义动作
pm2 set <key> <value>设置 PM2 配置项
pm2 unset <key>取消设置 PM2 配置项
pm2 help显示帮助信息
pm2 home打开 PM2 官网
pm2 plus打开 PM2 Plus 服务页面
pm2 module:list列出所有 PM2 模块
pm2 module:install <module_name>安装 PM2 模块
pm2 module:uninstall <module_name>卸载 PM2 模块
参数描述
--name设置应用的名称
--cwd指定启动应用时的工作目录
--watch监听文件变化,自动重启应用
--ignore-watch忽略监听的文件或目录
--max-memory-restart设置应用内存使用上限,超过后自动重启
--exec-path指定 Node.js 的执行路径
--instances设置应用实例的数量
--instance指定启动的实例编号
--env设置环境变量
--port指定应用的端口号
--cron设置定时重启任务
--interpreter指定解释器路径
--interpreter-args传递给解释器的参数
--output指定输出日志文件路径
--error指定错误日志文件路径
--pid指定 PID 文件路径
--log-date-format设置日志日期格式
--merge-logs合并日志文件
--no-daemon不以守护进程方式运行
--no-vizion禁用 vizion 版本控制
--no-autorestart禁用自动重启
--restart-delay设置重启延迟时间
--force强制重启或停止应用
--update-env更新环境变量
--only只启动指定的应用
--kill-timeout设置强制杀死进程的超时时间
--wait-ready等待应用准备好后再继续
--ready-delay设置等待准备好的延迟时间
--attachattaching to application output
--no-attachdo not attach to application output

常见问题

目前使用 PM2 来管理了多种类型的服务, 比如 python, js, golan 等, 期间也遇到了一些问题, 这里做一下总结.

pm2 找不到

我一般都是在客户端上使用 shell 脚本通过 pm2 来管理服务, 所以会有下面这样的启动命令:

1
ssh "$SSH_ALIAS" "pm2 list"

上面的脚本执行会报下面的错误:

1
zsh:1: command not found: pm2

当通过 SSH 远程执行命令时,系统会启动一个非交互式的 shell。在这种模式下,某些启动文件(如 .bashrc 或 .zshrc)可能不会被自动加载,导致环境变量(如 PATH)未被正确设置。因此,系统无法找到 pm2 命令。

所以正确的方式应该是:

1
ssh "$SSH_ALIAS" "source ~/.nvm/nvm.sh && pm2 list"

watch 问题

PM2 提供一个 watch 参数, 但文件发生变化时会重启服务, 但是在服务运行过程中可能会生成一些临时文件或改动文件, 这会导致服务重启, 这里我们可以设置 ignore_watch 参数来避免这种问题, 比如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
module.exports = {
apps: [
{
name: "summary-server", // 应用名称
namespace: "blog", // 指定命名空间
version: "1.0.0", // 应用版本
cwd: "/mnt/4.860.ssd/summary-server", // 当前工作目录
script: "./summaryServer.js", // 主脚本路径,相对于 cwd
watch: true, // 是否启用文件监控
ignore_watch: ["node_modules", "logs"], // 忽略监控的文件或目录
exec_mode: "fork",
instances: 1, // 应用实例数量
autorestart: true, // 是否自动重启
env: {
VERSION: "1.0.0", // 设置版本号环境变量
},
log_date_format: "YYYY-MM-DD HH:mm:ss", // 日志时间格式
error_file: "./logs/error.log", // 错误日志文件
out_file: "./logs/out.log", // 输出日志文件
merge_logs: true, // 是否合并日志
},
],
};

这里排除了 node_moduleslogs 目录的监控.

用户权限问题

我服务器上使用了普通用户安装的 Node.js 和 pm2, 但是某些服务需要使用 root 用户运行时, 需要修改启动命令:

1
ssh "$REMOTE_SSH_ALIAS" "source /home/{username}/.nvm/nvm.sh && pm2 start $REMOTE_PATH/ecosystem.config.js"

20250211193332_ydJdiJxp.webp

比如上面的服务使用 root 用户运行. 这样也会带来问题, 即在服务器上 root 用户使用 pm2 管理服务需要使用下面的命令:

1
/home/{username}/.nvm/versions/node/{version}/bin/pm2 list

而普通用户也无法通过 sudo pm2 xxx 管理服务, 所以最简单的方式就是使用 root 用户再安装一个 pm2, 简单粗暴往往是最好的方式 🤣.