之前写低配服务器优化那篇时,好几次提到 systemd 的 MemoryMax、Restart 这些配置。有朋友来问这些具体怎么配、systemd 到底怎么用。干脆写一篇,把最常用的场景都摊开说清楚。
Systemd 是什么?
Systemd 是现在几乎所有 Linux 发行版默认的 init 系统——说人话就是:它是操作系统启动后第一个运行的进程(PID=1),负责拉起和管理所有其他进程。
CentOS 7 之后、Ubuntu 15.04 之后、Debian 8 之后,全部默认用 systemd。
你可以把它理解成「操作系统自带的进程管理器」,比 pm2、supervisor、forever 这些第三方工具都更底层、更轻量、更稳定。因为——它就是系统本身的一部分。
1 | # 看看是不是 systemd |
核心概念:Unit
Systemd 管理的一切资源都叫 Unit(单元)。最常见的几种:
| 类型 | 后缀 | 管理什么 |
|---|---|---|
| Service | .service |
守护进程、后台服务 |
| Timer | .timer |
定时任务(cron 替代) |
| Socket | .socket |
网络或进程间通信 socket |
| Path | .path |
监控文件/目录变化 |
| Mount | .mount |
挂载点 |
| Target | .target |
一组 unit 的组合(类似运行级别) |
用得最多的是 Service 和 Timer。下面重点讲这两个。
写一个 Service 单元文件
Service 文件放在两个位置:
/etc/systemd/system/— 用户自定义或覆盖的系统级服务(优先)/usr/lib/systemd/system/— 软件包安装自带的服务
永远在第一个目录下创建你的服务文件。
来看一个完整的例子。假设你有一个 Python 写的 API 服务:
1 | # /etc/systemd/system/myapi.service |
逐段说明
[Unit] 段
Description— 人可读的描述,systemctl status时能看到After=— 指定启动顺序:在这个 unit 之后启动。不意味着依赖,只意味着排序。Wants=— 弱依赖:如果该 unit 可用就启动它,不可用不影响自己Requires=— 强依赖:如果该 unit 启动失败,自己也失败
[Service] 段
Type=simple— 最常用。ExecStart 的进程就是主进程,立即处于 running 状态Type=forking— 服务会 fork 出子进程后父进程退出(传统 daemon 模式)。Nginx、Apache 等用这种Type=oneshot— 执行一次就退出的任务,常配合 RemainAfterExit=yes 使用Type=notify— 服务启动完成后会发通知给 systemd(需要 sd_notify 支持)
User= 指定运行用户——永远不要用 root 跑服务,除非真的需要。
WorkingDirectory= 设工作目录,和 cd 到目录再运行等效。
Restart= 策略:
| 值 | 行为 |
|---|---|
no |
不自动重启(默认) |
on-success |
仅退出码为 0 时重启 |
on-failure |
退出码非 0 或被信号终止时重启 |
on-abnormal |
被信号终止或超时时重启 |
on-abort |
未捕获的异常退出时重启 |
always |
不管什么退出原因都重启 |
**我推荐 on-failure**——正常退出(比如手动停)不重启,崩溃了自动拉起。
RestartSec=5 — 重启前等 5 秒,防止频繁崩溃时疯狂重启。
资源限制部分(需要 systemd v240+,CentOS 8/Ubuntu 20.04 以上都有):
1 | MemoryMax=500M # 硬限制,超过直接 OOM kill |
这是我最喜欢 systemd 的地方——不用 cgroups 命令、不用 docker 限制,原生就在那里。
安全加固(强烈推荐所有对外服务加上):
1 | NoNewPrivileges=true # 防止通过 setuid 等方式提权 |
[Install] 段
WantedBy=multi-user.target 表示这个服务在系统进入多用户模式(正常启动后的状态)时就该启动——这其实就是在说「开机自启」。
日常命令速查
1 | # 启动/停止/重启 |
实战 1:用 systemd Timer 替代 Cron
很多人不知道 systemd 自带定时任务功能,而且比 cron 更强——可以设随机延迟、防止任务重叠、失败通知。
一个 Timer 需要两个文件:
第一步:创建 Service 文件(定义要做什么)
1 | # /etc/systemd/system/backup.service |
第二步:创建 Timer 文件(定义什么时候做)
1 | # /etc/systemd/system/backup.timer |
几个 Timer 参数值得单独说说:
OnCalendar=daily— 每天零点触发。也可以用Mon..Fri 03:00:00、*:0/15(每15分钟)、2026-08-01 00:00:00(单次)RandomizedDelaySec=30m— 在触发时间后 0~30 分钟内随机执行,避免多台机器同时冲 backendPersistent=true— 如果机器在计划时间关机了,开机后补执行OnUnitActiveSec=1h— 上次启动后过 1 小时再执行(不是按日历时间)
启动 timer:
1 | sudo systemctl daemon-reload |
相比 cron 的优势:
- 日志统一走 journalctl,不用单独配日志轮转
- 可以用 Restart 规则,任务失败了能重试
- 可以用 sandboxing 加固定时任务
- 配置文件可以和 .service 一起放 git 管理
实战 2:网络等待
很多服务启动时网络还没完全就绪。一个常见的翻车场景是「服务启动了但连不上外网」。
1 | [Unit] |
但这还不够——network.target 只是说网络服务启动了,DHCP 可能还没拿到 IP。
要真正等网络可用,需要先启用 NetworkManager 或 systemd-networkd 的 wait 服务:
1 | sudo systemctl enable systemd-networkd-wait-online |
然后你的 service 文件里的 After=network-online.target 才会真正生效。
实战 3:日志管理
Systemd 的日志(journald)默认只保留在内存中,重启后丢失。想持久化:
1 | sudo mkdir -p /var/log/journal |
限制日志大小:
1 | # /etc/systemd/journald.conf |
查错:常见翻车点
1. 配置文件改了没生效
1 | # 记得先 reload,再 restart |
2. 服务启动失败,但不知道原因
1 | # 第一件事 |
Status 输出里的信息量很大——它会告诉你:主进程 PID、运行时间、内存占用、重启次数、最近几条日志。一看就懂。
3. 权限问题
systemd 默认用严格的权限隔离。如果你的服务访问不了某个文件,检查:
User=有没有设对ProtectSystem=full会让/usr和/etc只读PrivateTmp=true给了独立的/tmp
4. Type 选错了
如果你启动一个老式 daemon(fork 模式),却用了 Type=simple,systemctl start 会卡住——因为父进程退出后 systemd 以为服务死了。
确认服务是哪种方式运行:
1 | # 看看它是前台还是 fork |
和 pm2 / supervisor 比怎么样?
| systemd | pm2 | supervisor | |
|---|---|---|---|
| 资源占用 | 0(系统自带) | ~30-50MB | ~10-20MB |
| 内存限制 | 原生 cgroups v2 | 无 | 无 |
| CPU 限制 | 原生 | 无 | 无 |
| 日志管理 | journalctl | 自带 + 轮转 | 自带 |
| 开机自启 | enable 一句话 | 额外配 | 额外配 |
| 学习成本 | 稍微高一点 | 低 | 低 |
我的建议:
- 如果你在管一台云服务器上跑的 1-3 个服务,用 systemd 就够了,别多装一个 pm2
- 如果你在服务器上跑 Node.js 服务但不想离开 JS 生态,pm2 也很香
- 如果有多台机器、需要统一管理,那就上 k8s——但那是另一个话题了
小总结
Systemd 作为 Linux 自带的进程管理工具,可以覆盖绝大多数日常需求:
| 需求 | systemd 方案 |
|---|---|
| 服务开机自启 | systemctl enable |
| 服务崩溃自动恢复 | Restart=on-failure |
| 限制内存使用 | MemoryMax=500M |
| 定时任务 | Timer unit |
| 查看日志 | journalctl -u |
| 安全隔离 | PrivateTmp + ProtectSystem |
接触 systemd 之前,我总觉得「操作系统自带的 init 系统」很高深、很底层,不敢碰。但其实日常够用的那一小部分并不复杂——无非就是写一个 .service 文件,设好启动策略和资源限制,剩下交给 systemctl 命令去管。
而且,它是操作系统自带的——系统在,它就在。比任何第三方工具都靠谱、都持久。