docs.sujiuovo.top

systemd 单元的几处细节:Restart、LimitNOFILE 与日志

2026-08-24systemd 249 / Ubuntu 22.04

写一个能跑起来的 unit 只要十行。写一个出问题之后还能看懂发生了什么的 unit,要多花点心思。下面几条是实际踩过的。

Restart=always 不等于「永远活着」

默认情况下 systemd 有启动频率限制:10 秒内失败 5 次就进入 failed 状态,不再重试。很多教程只写了 Restart=always,没写 StartLimitIntervalSec,结果是服务因为依赖没起来而快速退出,systemd 直接放弃,第二天才发现。

[Unit]
Description=app
After=network-online.target
Wants=network-online.target
# 放宽启动频率限制:60 秒内最多 10 次
StartLimitIntervalSec=60
StartLimitBurst=10

[Service]
ExecStart=/opt/app/run.sh
Restart=on-failure
RestartSec=5
# 退出码 0 也算失败时用 always;只在异常时重启用 on-failure
顺序很重要:StartLimitIntervalSec 和 StartLimitBurst 属于 [Unit],写在 [Service] 里会被静默忽略,不报错。

LimitNOFILE 与 fd 耗尽

代理、网关类服务在连接数上来之后报 too many open files,但 ulimit -n 看起来是 1024——因为 systemd 服务不读 /etc/security/limits.conf。要单独给:

[Service]
LimitNOFILE=65535
LimitNPROC=65535

改完 systemctl daemon-reload 再重启。验证方式不是看 ulimit,而是看进程自己的:

grep 'open files' /proc/$(pidof app)/limits

被 OOM 杀掉时,日志里应该留下线索

容器和裸进程都常见:内存涨到阈值,内核挑一个最大的进程杀掉,服务消失,重启后一切正常,问题被掩盖。两条命令能确认:

journalctl -k | grep -i 'killed process'
dmesg -T | grep -i oom

如果确认是 OOM,除了加内存,还可以给关键服务降低被选中的概率(值越小越不容易被杀,范围 -1000 到 1000):

[Service]
OOMScoreAdjust=-500
MemoryMax=800M
MemoryHigh=700M

日志不要无限长

一个高频打日志的服务,几天就能把 /var/log/journal 撑到几个 G。全局限制写在 /etc/systemd/journald.conf:

[Journal]
SystemMaxUse=500M
SystemMaxFileSize=50M
MaxRetentionSec=2week

单服务的输出量则应该在应用侧控制,别指望 journald 帮你按服务限流——它只有全局配额。

排查顺序

  1. systemctl status app 看最后一次退出码和信号。
  2. journalctl -u app -n 100 --no-pager 看应用自己说了什么。
  3. journalctl -k | grep -i oom 排除内核层面的原因。
  4. /proc/<pid>/limits 确认真实生效的限制。

← 返回归档