macOS 进程自动化方法概览

macOS 进程自动化方法概览

macOS 提供了多种进程自动化和服务管理方法,每种方法适用于不同的场景。从原生的 launchd 系统到特定语言的进程管理器(如 PM2),了解这些工具对于在 macOS 上运行后台服务、定时任务和生产应用程序至关重要。

引言

macOS 上的进程自动化不仅仅是编写脚本——它还涉及保持服务运行、调度任务以及管理独立于用户会话运行的应用程序。无论您是运行 Node.js API 的开发者、管理系统服务的系统管理员,还是调度 Python 脚本的数据科学家,macOS 都能为您提供量身定制的工具。

本指南涵盖 macOS 上主要的进程自动化方法:

launchd - macOS 原生初始化系统

brew services - 简单的 Homebrew 服务管理

PM2 - Node.js 生产级进程管理器

Supervisord/Circus - Python 进程控制

Overmind - 使用 Procfiles 的开发环境

cron - 传统 Unix 调度器

快速对比表

工具

类型

使用场景

复杂度

------

------

----------

--------

**launchd**

原生系统

生产服务、定时任务

**brew services**

包装器

Homebrew 服务

**PM2**

Node.js

Node.js 生产环境

**Forever**

Node.js

简单 Node.js

**Supervisord**

Python

Python/任意语言

**Circus**

Python

现代 Python

**Overmind**

Procfile

开发环境

**cron**

Unix 调度器

传统任务

原生 macOS:launchd

什么是 launchd

launchd 是 macOS 的初始化系统和服务管理器,等效于 Linux 上的 systemd。它于 Mac OS X 10.4(Tiger)中引入,负责管理守护进程、代理和定时任务。macOS 上的每个进程(除内核外)最终都是 launchd 的子进程。

核心组件:

launchd - 系统服务管理器

launchctl - 用于管理服务的命令行接口

LaunchAgents - 用户级服务(用户登录时运行)

LaunchDaemons - 系统级服务(启动时运行,以 root 身份)

launchctl 命令参考

# 列出所有已加载的服务
launchctl list

# 加载服务
launchctl load ~/Library/LaunchAgents/com.example.service.plist

# 卸载服务
launchctl unload ~/Library/LaunchAgents/com.example.service.plist

# 启动服务
launchctl start com.example.service

# 停止服务
launchctl stop com.example.service

# 查看服务日志
log stream --predicate 'process == "your-process-name"' --level debug

LaunchAgents 与 LaunchDaemons 的区别

LaunchAgents 和 LaunchDaemons 之间的区别是根本性的:

方面

LaunchAgents

LaunchDaemons

------

--------------

---------------

**运行时机**

用户登录时

系统启动时

**运行身份**

登录用户

root/系统

**权限**

用户级

系统级

**GUI 访问**

**位置**

`~/Library/LaunchAgents/` 或 `/Library/LaunchAgents/`

`/Library/LaunchDaemons/`

LaunchAgents 适用于需要 GUI 访问的用户特定任务:

• 菜单栏应用程序

• 用户自动化脚本

• 每个用户的后台服务

LaunchDaemons 适用于不依赖用户运行的服务:

• 数据库服务器

• Web 服务器

• 系统监控工具

创建 plist 文件

服务在 XML 属性列表(plist)文件中定义:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>com.example.myapp</string>

    <key>ProgramArguments</key>
    <array>
        <string>/usr/local/bin/node</string>
        <string>/Users/user/myapp/app.js</string>
    </array>

    <key>RunAtLoad</key>
    <true/>

    <key>KeepAlive</key>
    <true/>

    <key>WorkingDirectory</key>
    <string>/Users/user/myapp</string>

    <key>StandardOutPath</key>
    <string>/Users/user/myapp/logs/stdout.log</string>

    <key>StandardErrorPath</key>
    <string>/Users/user/myapp/logs/stderr.log</string>

    <key>EnvironmentVariables</key>
    <dict>
        <key>NODE_ENV</key>
        <string>production</string>
    </dict>
</dict>
</plist>

关键的 plist 键:

Label - 唯一的服务标识符(必需)

ProgramArguments - 要执行的命令和参数

RunAtLoad - 加载后立即启动

KeepAlive - 进程终止时重启

WorkingDirectory - 设置工作目录

StandardOutPath / StandardErrorPath - 日志文件位置

EnvironmentVariables - 进程的环境变量

StartInterval - 按指定间隔运行(用于定时任务)

CalendarInterval - 在特定时间运行(cron 的替代方案)

使用 launchd 执行定时任务

在定时任务方面,launchd 很大程度上已取代 cron。使用日历间隔而非 cron 语法:

<key>StartCalendarInterval</key>
<dict>
    <key>Hour</key>
    <integer>2</integer>
    <key>Minute</key>
    <integer>0</integer>
</dict>

这将在每天凌晨 2:00 运行任务。更多示例:

<!-- 每小时 -->
<key>StartInterval</key>
<integer>3600</integer>

<!-- 每周一上午 9 点 -->
<key>StartCalendarInterval</key>
<dict>
    <key>Weekday</key>
    <integer>1</integer>
    <key>Hour</key>
    <integer>9</integer>
    <key>Minute</key>
    <integer>0</integer>
</dict>

<!-- 每月第一天 -->
<key>StartCalendarInterval</key>
<dict>
    <key>Day</key>
    <integer>1</integer>
    <key>Hour</key>
    <integer>3</integer>
    <key>Minute</key>
    <integer>0</integer>
</dict>

调试 launchd 服务

当服务无法启动时:

1. 检查语法:

plutil -lint ~/Library/LaunchAgents/com.example.service.plist

1. 查看系统日志:

log show --predicate 'process == "launchd"' --last 1h
log stream --predicate 'eventMessage contains "com.example"'

1. 检查服务状态:

launchctl list | grep com.example

1. 手动执行测试:

# 直接运行命令以查看错误
/usr/local/bin/node /Users/user/myapp/app.js

参考来源: Understanding macOS LaunchAgents and Login Items, What are launchd agents and daemons on macOS?

调度:launchd 与 cron

cron 的弃用状态

Apple 已弃用 cron,转而支持 launchd,但为保持向后兼容,cron 仍受支持。在 macOS Sequoia 中,Apple 还在系统设置中添加了传统 cron 支持的选项。

何时使用 launchd 调度

launchd 相比 cron 提供了以下优势:

更好的集成 - 原生 macOS 服务管理

更多触发器 - 文件监视、系统事件,而不仅仅是时间

睡眠处理 - 智能处理睡眠/唤醒周期

安全上下文 - 使用适当的 macOS 权限运行

日志记录 - 集成统一日志记录

将 crontab 迁移到 launchd

Crontab 条目:

0 2 * * * /Users/user/scripts/backup.sh

等效的 launchd plist:

<key>Label</key>
<string>com.user.backup</string>

<key>ProgramArguments</key>
<array>
    <string>/Users/user/scripts/backup.sh</string>
</array>

<key>StartCalendarInterval</key>
<dict>
    <key>Hour</key>
    <integer>2</integer>
    <key>Minute</key>
    <integer>0</integer>
</dict>

cron 仍有意义的情况

对于您已经熟悉 cron 语法的简单基于时间的任务:

# 编辑 crontab
crontab -e

# 列出 crontab
crontab -l

# 删除 crontab
crontab -r

参考来源: Scheduled jobs with launchd rather than cron, Use launchd instead of crontab on your Mac

Homebrew 服务

安装

首先,如果您尚未安装 Homebrew:

/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

然后验证安装:

brew --version

什么是 brew services

brew services 是一个 Homebrew 扩展,用于简化后台服务管理。它包装了 launchd,为通过 Homebrew 安装的服务提供熟悉的接口。

# 列出所有服务
brew services list

# 启动服务
brew services start postgresql

# 停止服务
brew services stop redis

# 重启服务
brew services restart nginx

# 在前台运行服务(用于调试)
brew services run postgresql

brew services 如何使用 launchd

brew services 自动创建 launchd plist 文件:

用户服务~/Library/LaunchAgents/

系统服务/Library/LaunchDaemons/

例如,brew services start postgresql 会在 ~/Library/LaunchAgents/homebrew.mxcl.postgresql.plist 创建一个 plist。

常见使用场景

数据库:

brew install postgresql
brew services start postgresql

brew install mysql
brew services start mysql

brew install redis
brew services start redis

Web 服务器:

brew install nginx
brew services start nginx

开发环境栈:

# 启动多个服务
brew services start postgresql
brew services start redis
brew services start memcached

GUI 工具

对于喜欢图形界面的用户:

[BrewServicesManager](https://github.com/validatedev/BrewServicesManager) - 用于管理 Homebrew 服务的菜单栏应用程序

[brew-services-manage](https://github.com/persiliao/brew-services-manage) - 具有日志访问功能的服务管理

参考来源: Homebrew Services: How to Use, How It Works, Managing background processes in Ventura

Node.js 进程管理器

PM2

PM2 是 Node.js 生产环境进程管理的事实标准。它提供集群、监控和自动重启功能。

安装:

npm install pm2 -g

基本命令:

# 启动应用程序
pm2 start app.js

# 带名称启动
pm2 start app.js --name "api"

# 启动多个实例(集群模式)
pm2 start app.js -i max

# 列出所有进程
pm2 list

# 查看日志
pm2 logs

# 监控仪表板
pm2 monit

# 停止进程
pm2 stop api

# 重启进程
pm2 restart api

# 删除进程
pm2 delete api

在 macOS 上启动 PM2(launchd 集成)

要使 PM2 在启动时自动运行:

# 生成并保存启动脚本
pm2 startup darwin

# 保存当前进程列表
pm2 save

# 现在启动您的应用
pm2 start app.js
pm2 save

pm2 startup darwin 命令会生成一个在系统启动时加载 PM2 的 launchd plist,而 pm2 save 会保存当前进程列表,以便 PM2 可以在重启后恢复您的应用程序。

参考来源: PM2 Documentation, How to use pm2 startup on macOS

Forever

对于更简单的使用场景,Forever 提供基本的保活功能:

npm install forever -g

# 启动脚本
forever start app.js

# 列出运行的脚本
forever list

# 停止脚本
forever stop app.js

最适合: 开发环境、简单脚本、不需要 PM2 功能时。

参考来源: Running Node.js scripts continuously using forever

其他 Node.js 选项

nodemon - 文件更改时自动重启(仅限开发)

``bash npm install -g nodemon nodemon app.js ``

concurrently - 同时运行多个 npm 脚本

``bash npm install -g concurrently concurrently "npm run watch" "npm run serve" ``

nohup - 用于快速后台任务的内置 Unix 命令

Python 进程管理器

Supervisord

Supervisord 是一个成熟的 Unix 类系统进程控制系统,广泛用于 Python 环境。

安装:

brew install supervisord
# 或通过 pip:
pip install supervisor

配置(`/etc/supervisord.conf` 或 `~/.supervisor/supervisord.conf`):

[supervisord]
nodaemon=false
logfile=/var/log/supervisor/supervisord.log

[program:myapp]
command=/usr/bin/python3 /path/to/app.py
directory=/path/to/app
user=myuser
autostart=true
autorestart=true
stderr_logfile=/var/log/supervisor/myapp.err.log
stdout_logfile=/var/log/supervisor/myapp.out.log

命令:

# 启动 supervisord
supervisord -c /path/to/supervisord.conf

# 检查状态
supervisorctl status

# 启动程序
supervisorctl start myapp

# 停止程序
supervisorctl stop myapp

# 重启程序
supervisorctl restart myapp

# 重新读取配置
supervisorctl reread
supervisorctl update

参考来源: Supervisor Official Documentation, Install supervisor on Mac M1

Circus

Circus 是 Supervisord 的现代 Python 替代方案,具有更好的性能和活跃的开发:

pip install circus

Circus 配置(`circus.ini`):

[watcher:myapp]
cmd = python3 /path/to/app.py
uid = myuser
numprocesses = 1
autostart = true
stop_signal = TERM

命令:

circusd circus.ini
circusctl status
circusctl start myapp
circusctl stop myapp

相对于 Supervisord 的优势:

• 使用 ZeroMQ 获得更好的性能

• 对进程变化响应更灵敏

• 活跃的开发

• 跨平台

参考来源: Circus Documentation

基于 Procfile 的管理器

Overmind

Overmind 是一个使用 tmux 实现终端 UI 的现代进程管理器,非常适合具有多个进程的开发环境。

安装:

brew install overmind

Procfile(`Procfile`):

web: bundle exec rails server
worker: bundle exec sidekiq
redis: redis-server

使用:

overmind start

Overmind 创建一个 tmux 会话,每个进程在其自己的窗口中,可通过标准 tmux 命令访问。

最适合: 开发环境、多进程应用程序、基于终端的工作流程。

参考来源: Overmind GitHub, Control Your Dev Processes with Overmind

Honcho(Python)和 Foreman(Ruby)

Honcho(Python):

pip install honcho
honcho start

Foreman(Ruby):

foreman start

两者都使用与 Overmind 相同的 Procfile 格式 gem install for,但没有 tmux TUI。

其他方法

nohup

用于快速后台任务:

nohup node app.js &

最适合: 快速测试、简单的后台任务、临时运行。

screen/tmux

用于持久的终端会话:

screen -S mysession
# 或
tmux new -s mysession

最适合: 交互式会话、远程工作、长时间运行的终端进程。

综合对比:优缺点

工具

描述

优点

缺点

------

------

------

------

**launchd**

macOS 原生初始化系统

✅ 原生 macOS<br>✅ 无需安装<br>✅ surviving系统更新<br>✅ 最可靠的选项<br>✅ 完整系统集成

❌ XML 配置冗长<br>❌ 学习曲线陡峭<br>❌ 调试可能困难<br>❌ 社区工具有限

**brew services**

Homebrew 服务包装器

✅ 极其简单的界面<br>✅ 一键安装<br>✅ Homebrew 用户熟悉<br>✅ 自动生成 plist

❌ 需要 Homebrew<br>❌ 自定义有限<br>❌ 不适合生产环境<br>❌ 隐藏的复杂性

**PM2**

Node.js 进程管理器

✅ 为 Node.js 而生<br>✅ 集群支持<br>✅ 出色的监控<br>✅ 崩溃时自动重启<br>✅ 零停机重载

❌ 特定于 Node.js<br>❌ 需要额外软件依赖<br>❌ 内存开销<br>❌ 功能学习曲线

**Forever**

简单的 Node.js 保活

✅ 非常轻量<br>✅ 简单易用<br>✅ 资源占用低

❌ 功能极少<br>❌ 无监控仪表板<br>❌ 无集群<br>❌ 仅手动重启

**Supervisord**

Python 进程管理器

✅ 成熟稳定<br>✅ 语言无关<br>✅ Web 监控界面<br>✅ 进程分组

❌ 架构较老<br>❌ 配置复杂<br>❌ 比现代替代方案慢<br>❌ 开发不够活跃

**Circus**

现代 Python 进程管理器

✅ 更好的性能<br>✅ ZeroMQ 通信<br>✅ 活跃开发<br>✅ 跨平台

❌ 社区较小<br>❌ 不如 Supervisord 成熟<br>❌ 配置格式不同<br>❌ 可用教程较少

**Overmind**

基于 Procfile 的开发管理器

✅ 出色的 tmux TUI<br>✅ 标准 Procfile 格式<br>✅ 非常适合开发<br>✅ 可视化进程控制

❌ 依赖 tmux<br>❌ 不适合生产<br>❌ 需要终端访问<br>❌ macOS 特定 quirks

**Honcho**

Python Procfile 管理器

✅ 跨平台<br>✅ 简单界面<br>✅ 纯 Python

❌ 无 TUI<br>❌ 不如 Overmind 流行<br>❌ 功能极少

**Foreman**

Ruby Procfile 管理器

✅ 原始 Procfile 工具<br>✅ 经过实战考验<br>✅ 庞大社区

❌ 依赖 Ruby<br>❌ 代码库老旧<br>❌ 无 TUI<br>❌ 比替代方案慢

**cron**

Unix 作业调度器

✅ 熟悉的语法<br>✅ 快速任务简单<br>✅ 适用于所有 Unix

❌ 在 macOS 上已被弃用<br>❌ 无原生 macOS 集成<br>❌ 错误处理差<br>❌ 睡眠/唤醒处理不佳

**nohup**

Unix 后台命令

✅ 内置 Unix<br>✅ 无需安装<br>✅ 测试快速

❌ 无自动重启<br>❌ 无监控<br>❌ 终端关闭时丢失<br>❌ 仅手动进程管理

**screen/tmux**

终端多路复用器

✅ 持久会话<br>✅ 非常适合远程工作<br>✅ 多个窗口

❌ 不适合自动化<br>❌ 需要终端访问<br>❌ 手动进程管理<br>❌ 基于会话(非服务)

决策框架

按使用场景选择

场景

推荐工具

原因

------

----------

------

生产系统服务

launchd

原生、可靠、重启后存活

使用 Procfile 开发

Overmind

TUI、简单的进程管理

Node.js 生产环境

PM2

集群、监控、自动重启

Node.js 简单/开发

Forever

简单、轻量

Python 生产环境

Circus/Supervisord

Python 原生、成熟

数据库/Redis

brew services

简单、Homebrew 集成

一次性后台任务

nohup

内置、无需设置

生产定时任务

launchd

原生、更多功能

跨平台开发

Honcho

到处可用

按技能水平选择

用户画像

从何开始

扩展到

----------

----------

--------

关注 macOS 原生

launchd

brew services

Node.js 开发者

PM2

launchd 用于系统集成

Python 开发者

Circus

brew services 用于数据库

DevOps 工程师

launchd

Supervisord/Circus

初学者

brew services

PM2 或 launchd

高级用户

Overmind

根据需要使用所有工具

集成策略

PM2 与 launchd:

pm2 startup darwin  # 创建 launchd plist
pm2 save           # 保存当前进程列表

brew services 与 launchd: brew services 自动创建 launchd plist。

Overmind 与 Docker: 在本地使用 Overmind 进程,使用 Docker 容器,用脚本编排。

结论

macOS 为进程自动化提供了丰富的工具包:

1. 对于大多数用户:从 brew services 开始,因为它简单

2. 对于生产环境:直接使用 launchd 或通过工具集成(PM2 startup)

3. 对于 Node.js:PM2 是行业标准

4. 对于 Python:Supervisord 或 Circus

5. 对于开发环境:Overmind 提供出色的工作流程

关键是为您特定的用例和环境选择正确的工具。像 launchd 这样的原生工具提供与 macOS 最可靠的集成,而像 PM2 这样的特定语言工具为其生态系统提供专门的功能。

封面图片: 包含 launchd 架构、brew services 接口、PM2 仪表板和对比表的 macOS 自动化工具技术图表。

参考来源:

Understanding macOS LaunchAgents

Homebrew Services Guide

PM2 Documentation

Supervisor Documentation

Overmind GitHub