Ian Chou's Blog

用 PM2 + Bun 管理 Python FastAPI 服務:從 systemd 遷移實戰

用 PM2 + Bun 管理 Python FastAPI 服務:從 systemd 遷移實戰

PM2 是 Node.js 生態系中最受歡迎的進程管理工具,但你知道嗎?它其實也能完美管理 Python、Go、Rust 等任何可執行程式。本文記錄了將一個 FastAPI search server 從 systemd 遷移到 PM2 的完整過程。

為什麼選擇 PM2?

相較於 systemd,PM2 提供了更友善的開發者體驗:

特性 systemd PM2
日誌查看 journalctl -u service pm2 logs
重啟服務 systemctl restart service pm2 restart app
查看狀態 systemctl status service pm2 list
設定檔 需要 root 權限編輯 /etc/systemd/system/ 專案目錄下的 ecosystem.config.js
即時監控 無內建 pm2 monit
叢集模式 需手動設定 -i max 一行搞定

環境準備

使用 Bun 安裝 PM2

如果你已經有 Bun,可以直接用它來安裝 PM2:

bun install -g pm2

注意:PM2 的某些功能(如 pm2 startup)仍需要 Node.js。如果你只用 Bun,可能需要額外安裝 Node.js:

sudo apt install nodejs

安裝完成後,如果 pm2 指令無法直接使用(因為 Bun 的 global bin 可能不在 PATH 中),可以建立一個 wrapper script:

mkdir -p ~/.local/bin
echo '#!/bin/bash
exec bun ~/.bun/install/global/node_modules/pm2/bin/pm2 "$@"' > ~/.local/bin/pm2
chmod +x ~/.local/bin/pm2

驗證安裝:

pm2 --version
# 6.0.14

設定 Python FastAPI 應用

建立 ecosystem.config.js

在專案根目錄建立 ecosystem.config.js

module.exports = {
  apps: [{
    name: "search-server",
    cwd: "./search",                    // 工作目錄
    script: ".venv/bin/python",         // 使用 venv 的 Python
    args: "-m uvicorn server:app --host 0.0.0.0 --port 8000",
    interpreter: "none",                // 不使用 Node.js 解釋器
    watch: false,                       // 不自動監控檔案變更
    autorestart: true,                  // 自動重啟
    max_restarts: 10,                   // 最大重啟次數
    env: {
      // 環境變數(可選,app 也可從 .env 讀取)
    }
  }]
}

關鍵設定說明:

啟動服務

pm2 start ecosystem.config.js

輸出範例:

[PM2] App [search-server] launched (1 instances)
┌────┬────────────────────┬──────────┬──────┬───────────┬──────────┬──────────┐
│ id │ name               │ mode     │ ↺    │ status    │ cpu      │ memory   │
├────┼────────────────────┼──────────┼──────┼───────────┼──────────┼──────────┤
│ 0  │ search-server      │ fork     │ 0    │ online    │ 0%       │ 205.8mb  │
└────┴────────────────────┴──────────┴──────┴───────────┴──────────┴──────────┘

處理 Port 衝突

如果舊的服務還在佔用 port,需要先停止它:

# 找出佔用 port 的進程
lsof -i :8000

# 停止舊進程
kill <PID>

# 重啟 PM2 管理的服務
pm2 restart search-server

設定開機自動啟動

PM2 可以整合 systemd 實現開機自動啟動:

# 產生 startup script(需要 Node.js)
pm2 startup

# 會輸出一行需要 sudo 執行的指令,複製執行它
sudo env PATH=$PATH:/home/user/.bun/bin ... pm2 startup systemd ...

# 儲存目前的進程列表
pm2 save

這會建立 /etc/systemd/system/pm2-<username>.service,開機時自動恢復所有 PM2 管理的進程。

PM2 日常操作指南

基本指令

指令 說明
pm2 list 列出所有進程
pm2 start app 啟動應用
pm2 stop app 停止應用
pm2 restart app 重啟應用
pm2 reload app 零停機重載(適用於 cluster mode)
pm2 delete app 從 PM2 列表中刪除應用

監控與日誌

# 即時日誌
pm2 logs

# 只看特定應用的日誌
pm2 logs search-server

# 顯示最後 100 行
pm2 logs --lines 100

# 即時監控面板
pm2 monit

進階操作

# 重啟所有應用
pm2 restart all

# 停止所有應用
pm2 stop all

# 儲存目前進程列表(用於 startup 恢復)
pm2 save

# 查看詳細資訊
pm2 show search-server

# 重新載入設定檔
pm2 reload ecosystem.config.js

管理 PM2 本身

# 停止 PM2 daemon
pm2 kill

# 更新 PM2
bun update -g pm2

# 移除 startup script
pm2 unstartup systemd

實用技巧

1. 開發模式:自動重啟

// ecosystem.config.js
{
  name: "search-server-dev",
  watch: true,
  watch_delay: 1000,
  ignore_watch: ["node_modules", ".git", "__pycache__", ".venv"],
}

2. 環境變數管理

{
  env: {
    NODE_ENV: "development",
    PORT: 8000
  },
  env_production: {
    NODE_ENV: "production",
    PORT: 80
  }
}

// 啟動時指定環境
// pm2 start ecosystem.config.js --env production

3. 記憶體限制(自動重啟)

{
  max_memory_restart: "500M"
}

4. 日誌檔案管理

{
  error_file: "./logs/error.log",
  out_file: "./logs/output.log",
  log_date_format: "YYYY-MM-DD HH:mm:ss Z",
  merge_logs: true
}

總結

PM2 提供了比 systemd 更友善的進程管理體驗,特別適合開發者日常使用。透過 Bun 安裝可以減少對 npm 的依賴,但某些進階功能(如 startup)仍需要 Node.js。

完整的專案結構範例:

project/
├── ecosystem.config.js    # PM2 設定
├── search/
│   ├── .venv/            # Python 虛擬環境
│   ├── server.py         # FastAPI 應用
│   └── pyproject.toml
└── .env                   # 環境變數

現在你可以用簡單的 pm2 restart search-server 來重啟服務,用 pm2 logs 來追蹤日誌,享受更流暢的開發體驗!