LinuxNotes

第 09 章:systemd

zjc 于 2026-01-09 发布

这是《Linux 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开,代码块保留了原书可直接运行的版本。 systemd 是多数主流 Linux 发行版的 init 系统和服务管理器。它负责启动服务、管理依赖、收集日志、限制资源、隔离进程树。现代后端服务应优先使用 systemd 或容器编排,而不是手工 nohup

9.1 systemd 组成

systemd
  |-- systemd(1) 主进程
  |-- unit 配置
  |-- journald 日志
  |-- logind 登录管理
  |-- timedated 时间管理
  +-- cgroup 资源分组

查看:

ps -p 1 -o pid,comm,args
systemctl --version
systemctl --no-pager --type=service

9.2 常用 systemctl 命令

服务:

sudo systemctl start nginx
sudo systemctl stop nginx
sudo systemctl restart nginx
sudo systemctl reload nginx
sudo systemctl try-restart nginx
sudo systemctl enable nginx
sudo systemctl disable nginx
sudo systemctl daemon-reload

状态:

systemctl status nginx
systemctl is-active nginx
systemctl is-enabled nginx
systemctl list-units --type=service
systemctl list-units --failed
systemctl list-unit-files --type=service

查看配置:

systemctl cat nginx
systemctl show nginx
systemctl show nginx -p MainPID -p ActiveState -p SubState

修改配置后:

sudo systemctl daemon-reload
sudo systemctl restart app

daemon-reload 重新读取单元文件,不等于重启服务。

9.3 Unit 类型

类型 后缀 用途
service .service 服务
socket .socket socket 激活
timer .timer 定时任务
target .target 一组 unit
mount .mount 挂载
path .path 路径变化触发
slice .slice 资源分组

单元路径:

/etc/systemd/system      管理员配置,优先级高
/run/systemd/system      运行时配置
/usr/lib/systemd/system  软件包安装

覆盖配置推荐使用:

systemctl edit nginx

它会生成 drop-in 文件,避免直接修改包管理的原始单元。

9.4 服务单元示例

[Unit]
Description=Order Service
Documentation=https://example.internal/docs/order
Wants=network-online.target
After=network-online.target

[Service]
Type=simple
User=app
Group=app
WorkingDirectory=/opt/order
Environment=JAVA_OPTS=-Xms1g -Xmx1g
EnvironmentFile=-/etc/order/env
ExecStart=/usr/bin/java $JAVA_OPTS -jar app.jar
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-failure
RestartSec=5s
TimeoutStartSec=60s
TimeoutStopSec=30s
KillSignal=SIGTERM
KillMode=mixed
LimitNOFILE=65535
UMask=0027
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ReadWritePaths=/var/log/order /opt/order/data

[Install]
WantedBy=multi-user.target

安装:

sudo cp order.service /etc/systemd/system/order.service
sudo systemctl daemon-reload
sudo systemctl enable --now order.service

9.5 Type 与启动状态

Type 判定方式
simple ExecStart 启动即认为主进程启动
exec ExecStart 执行成功后进入启动
forking 子进程分离,依赖 PIDFile 或推断
notify 应用主动调用 sd_notify
oneshot 一次性任务,退出后进入 inactive 或 dead

Java 应用常使用:

Type=simple
ExecStart=/usr/bin/java -jar app.jar

需要等待就绪时,可用 Type=notify,但应用必须支持 sd_notify;也可通过健康检查系统处理,而不是让 systemd 猜测业务就绪。

9.6 依赖关系

[Unit]
Wants=network-online.target
After=network-online.target
Requires=postgresql.service
BindsTo=nginx.service

语义:

指令 行为
Wants 弱依赖,失败也启动自己
Requires 强依赖,被依赖失败则自己失败
Requisite 必须已激活,否则不启动
BindsTo 自己随被依赖停止
After 在指定 unit 后启动
Before 在指定 unit 前启动

After 只定义顺序,不自动创建依赖。需要同时写 RequiresWants

9.7 重启策略

Restart=no
Restart=on-success
Restart=on-failure
Restart=always
Restart=on-abnormal
Restart=on-watchdog

常用:

Restart=on-failure
RestartSec=5s
StartLimitIntervalSec=60s
StartLimitBurst=5

如果服务不断崩溃,自动重启只是掩盖问题。应检查退出原因、资源限制和依赖状态。

查看失败:

systemctl status app
journalctl -u app -n 200
systemctl show app -p Result -p ExecMainStatus

9.8 资源限制

文件与进程:

LimitNOFILE=65535
LimitNPROC=4096
LimitCORE=infinity

CPU 与内存:

CPUWeight=100
CPUQuota=200%
MemoryHigh=4G
MemoryMax=6G
TasksMax=1024

查看:

systemctl show app -p MemoryMax -p CPUQuota -p TasksMax
cat /proc/$(systemctl show -p MainPID --value app)/limits

内存硬限制达到时,内核可能杀掉进程;限制应低于机器容量,并保留系统余量。

9.9 journald 日志

查看服务日志:

journalctl -u nginx
journalctl -u nginx -f
journalctl -u nginx -n 100 --no-pager
journalctl -u nginx --since today
journalctl -u nginx --since '2026-08-25 10:00' --until '2026-08-25 11:00'

按优先级:

journalctl -p err
journalctl -p warning -u app

按 PID:

journalctl _PID=<pid>

查看磁盘占用:

journalctl --disk-usage

清理:

sudo journalctl --vacuum-time=14d
sudo journalctl --vacuum-size=2G

长期配置:

# /etc/systemd/journald.conf
[Journal]
Storage=persistent
SystemMaxUse=2G
MaxRetentionSec=30day

9.10 Timer 定时任务

示例 /etc/systemd/system/backup.service

[Unit]
Description=Nightly Backup

[Service]
Type=oneshot
User=backup
ExecStart=/opt/scripts/backup.sh

示例 /etc/systemd/system/backup.timer

[Unit]
Description=Run backup nightly

[Timer]
OnCalendar=*-*-* 02:00:00
Persistent=true
RandomizedDelaySec=300

[Install]
WantedBy=timers.target

启用:

sudo systemctl daemon-reload
sudo systemctl enable --now backup.timer
systemctl list-timers backup.timer

相比 crontab,timer 更容易纳入 systemd 依赖、日志和资源控制。

9.11 排障案例

服务启动失败

systemctl status app --no-pager
journalctl -u app -n 200 --no-pager
systemctl cat app
sudo -u app /opt/app/bin/start --check
ss -lntp

常见原因:

  1. ExecStart 路径错误;
  2. User 无权限;
  3. 配置不存在;
  4. 环境变量缺失;
  5. 端口冲突;
  6. 单元语法错误;
  7. 依赖未就绪。

修改单元不生效

systemctl cat app
sudo systemctl daemon-reload
sudo systemctl restart app

检查是否被 drop-in 覆盖:

systemctl show app -p FragmentPath -p DropInPaths

服务被 OOM 杀掉

journalctl -u app --since today
dmesg -T | grep -i 'out of memory'
systemctl show app -p MemoryMax

处理:

  1. 区分 cgroup 限制和机器内存;
  2. 检查内存泄漏;
  3. 调整应用内存参数;
  4. 评估扩容;
  5. 增加水位监控。

本章小结

systemd 用 unit 描述服务,用依赖控制启动顺序,用 cgroup 管资源,用 journald 收日志。生产服务应显式配置用户、目录、环境、重启策略、停止超时、文件数限制和日志留存。修改单元后要 daemon-reload,并用状态、日志和业务健康检查闭环。

思考题

  1. daemon-reloadrestart 有什么区别?
  2. WantsRequiresAfter 分别解决什么问题?
  3. Restart=always 可能掩盖什么问题?
  4. systemd timer 相比 crontab 有哪些优势?
  5. 为 Java 服务写一个包含资源限制和优雅停机的 unit。