systemctl —— systemd 服务管理
systemctl —— systemd 服务管理
分类:05 服务与计划任务 适用:所有使用 systemd 的 Linux(systemd 219 ~ 257) 一句话:启停、开机自启、看状态、改配置、查依赖,现代 Linux 服务管理的中枢。
1. 它解决什么问题
- 服务起不来,为什么?退出码是什么?日志在哪?
- 改完 unit 文件为什么不生效?
- 服务老是自动重启/被杀,谁干的?
- 开机启动顺序、依赖关系怎么配。
2. 日常六件事
systemctl start nginx # 启动
systemctl stop nginx # 停止
systemctl restart nginx # 重启(先停后起,会断连接)
systemctl reload nginx # 重载配置(不断连接,需服务支持)
systemctl status nginx # 状态
systemctl enable --now nginx # 开机自启 + 立即启动
systemctl disable nginx # 取消自启(不停止当前进程)
systemctl is-active nginx # active / inactive / failed
systemctl is-enabled nginx # enabled / disabled / masked
systemctl is-failed nginx
enable 与 start 是两件事:前者管开机,后者管现在。新手最常犯的错是只 enable 没 start,或反过来。
3. status 输出怎么读
● nginx.service - A high performance web server
Loaded: loaded (/lib/systemd/system/nginx.service; enabled; preset: enabled)
Active: active (running) since Thu 2026-11-05 09:12:33 CST; 2h 3min ago
Docs: man:nginx(8)
Main PID: 1234 (nginx)
Tasks: 5 (limit: 4915)
Memory: 12.3M
CPU: 1.234s
CGroup: /system.slice/nginx.service
├─1234 nginx: master process /usr/sbin/nginx
└─1235 nginx: worker process
| 行 | 关注点 |
|---|---|
Loaded |
unit 文件路径、是否 enabled、是否被 mask、是否有 drop-in |
Active |
active (running) 正常;failed 看下面的日志;activating (auto-restart) 说明在反复重启 |
Main PID |
主进程,kill 从这里下手 |
Tasks / Memory / CPU |
cgroup 统计 |
| 末尾几行 | 最近日志摘要(等同 journalctl -u nginx -n 10) |
status 只显示最近 10 行日志,完整日志用 journalctl。
4. 查看与检索
systemctl list-units --type=service # 运行中的服务
systemctl list-units --type=service --all # 含未运行
systemctl list-units --state=failed # 失败的服务
systemctl list-unit-files --type=service # 所有已安装服务及自启状态
systemctl list-unit-files | grep enabled
systemctl list-dependencies nginx # 依赖树
systemctl list-dependencies --reverse nginx # 谁依赖它
systemctl cat nginx # 看 unit 文件内容(含 drop-in)
systemctl show nginx # 所有属性(脚本用)
systemctl show -p MainPID -p ActiveState nginx
systemctl --failed # 失败的单元
systemctl --no-pager status nginx # 不进入分页器
5. 单元文件
位置与优先级
| 路径 | 用途 | 优先级 |
|---|---|---|
/etc/systemd/system/ |
管理员自定义 | 最高 |
/run/systemd/system/ |
运行时生成 | 中 |
/usr/lib/systemd/system/(或 /lib/...) |
软件包安装 | 最低 |
同名 unit 高优先级覆盖低优先级。修改软件包自带的 unit 不要直接改 /usr/lib,应该:
systemctl edit nginx # 创建 /etc/systemd/system/nginx.service.d/override.conf
systemctl edit --full nginx # 复制完整文件再改
systemctl edit 生成的 drop-in 只写要覆盖的部分:
[Service]
LimitNOFILE=65535
Environment="TZ=Asia/Shanghai"
改完必须:
systemctl daemon-reload
systemctl restart nginx
一个典型 unit
[Unit]
Description=My App
After=network-online.target mysql.service
Wants=network-online.target
Requires=mysql.service
[Service]
Type=simple
User=app
Group=app
WorkingDirectory=/opt/app
EnvironmentFile=-/etc/sysconfig/myapp
Environment="JAVA_OPTS=-Xms512m -Xmx2g"
ExecStartPre=/bin/mkdir -p /var/log/myapp
ExecStart=/usr/bin/java $JAVA_OPTS -jar /opt/app/app.jar
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-failure
RestartSec=5
TimeoutStopSec=30
LimitNOFILE=65535
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
Type 怎么选
| Type | 含义 | 适用 |
|---|---|---|
simple(默认) |
ExecStart 一执行就认为启动成功 |
前台常驻进程 |
forking |
父进程退出、子进程继续 | 传统守护进程(nginx、php-fpm) |
oneshot |
执行完就退出 | 初始化脚本、一次性任务 |
notify |
进程主动发 sd_notify 才认为就绪 |
支持通知的服务 |
idle |
等其它任务完成再执行 | 控制台输出类 |
Restart 策略
| 值 | 行为 |
|---|---|
no |
不重启(默认) |
on-failure |
非 0 退出、被信号杀、超时、看门狗触发时重启 |
always |
总是重启(包括正常退出) |
on-abnormal |
仅信号/超时/看门狗 |
配 RestartSec 避免疯狂重启,配 StartLimitBurst / StartLimitIntervalSec 限制次数:
[Unit]
StartLimitIntervalSec=60
StartLimitBurst=5
[Service]
Restart=on-failure
RestartSec=5
超过次数会进入 failed,需要 systemctl reset-failed。
6. 常用运维操作
systemctl daemon-reload # 改 unit 后必须执行
systemctl reset-failed nginx # 清除 failed 状态
systemctl mask nginx # 彻底禁用(连手动 start 也不行)
systemctl unmask nginx
systemctl kill -s HUP nginx # 给服务发信号
systemctl set-property nginx MemoryMax=1G CPUQuota=50% # 临时限制资源
systemctl isolate rescue.target # 切换运行级别(⚠️ 会停服务)
systemctl get-default # 默认 target
systemctl set-default multi-user.target
systemctl poweroff / reboot / suspend
set-property 会写入 /etc/systemd/system.control/,重启后仍生效,用 systemctl revert nginx 恢复。
7. 定时器(systemd timer)
# /etc/systemd/system/backup.timer
[Unit]
Description=Daily backup
[Timer] 02:30:00
Persistent=true
RandomizedDelaySec=300
[Install]
WantedBy=timers.target
# /etc/systemd/system/backup.service
[Unit]
Description=Backup job
[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.sh
systemctl daemon-reload
systemctl enable --now backup.timer
systemctl list-timers --all
systemctl start backup.service # 手动触发一次
Persistent=true 表示关机错过的任务在开机后补跑,这是相比 cron 的最大优势。
8. 实战场景
8.1 服务起不来,四步定位
systemctl status app --no-pager -l # 1. 状态与退出码
journalctl -u app -n 100 --no-pager # 2. 完整日志
systemctl cat app # 3. unit 是否有语法/路径错
systemctl show -p ExecMainStatus -p Result app # 4. 退出码与结果
常见退出码:
| 码 | 含义 |
|---|---|
| 1 | 应用自身报错 |
| 2 | 参数/配置错误 |
| 126 | 文件不可执行(权限) |
| 127 | 命令不存在(路径错) |
| 137 | 被 SIGKILL(多为 OOM) |
| 143 | 被 SIGTERM(正常停止或被超时杀) |
| 203 | ExecStart 指向的文件无法执行 |
| 226 | ExecStart 命令不存在 |
| 233 | 服务未就绪(Type=notify 超时) |
8.2 服务反复重启
systemctl status app | grep -E 'Active|restart'
journalctl -u app --since '-10 min' | grep -i -E 'kill|oom|signal|timeout'
dmesg -T | grep -i oom
如果是 activating (auto-restart),说明 Restart= 在生效,根因还是启动失败。
8.3 改了配置不生效
systemctl cat app # 确认实际加载的 unit 与 drop-in
systemctl daemon-reload # 改 unit 后必须
systemctl restart app # 改 Environment/Exec 后必须重启
systemctl show -p Environment app # 确认变量
注意:只改 ExecStart 里的应用配置文件,reload 就够;改了 unit 文件本身,必须 daemon-reload + restart。
8.4 ulimit 不生效
/etc/security/limits.conf 对 systemd 服务无效,必须写 unit:
[Service]
LimitNOFILE=65535
LimitNPROC=65535
验证:
systemctl show -p LimitNOFILE app
cat /proc/$(systemctl show -p MainPID --value app)/limits | grep 'open files'
8.5 看服务启动耗时
systemd-analyze # 总启动时间
systemd-analyze blame # 各单元耗时排序
systemd-analyze critical-chain # 关键路径
systemd-analyze verify /etc/systemd/system/app.service # 校验语法
8.6 加环境变量
systemctl edit app
# 写入
# [Service]
# Environment="PROFILE=prod"
# EnvironmentFile=-/etc/sysconfig/app
systemctl daemon-reload && systemctl restart app
EnvironmentFile 前的 - 表示文件不存在也不报错。
9. 常见坑
daemon-reload忘记执行:改 unit 后不重载,restart用的还是旧配置,容易怀疑人生。enable不等于start:两个都要,或enable --now。disable不会停止当前进程:还要stop。mask比disable更彻底:被 mask 的服务无法被依赖拉起(如防止误启)。limits.conf对 systemd 服务无效:见 8.4。Type=forking配错:simple服务用forking会误判启动失败。$MAINPID在ExecReload中才有效,ExecStart里不要用。ExecStart不支持 shell 语法:管道、重定向、&&都不行,要写成ExecStart=/bin/bash -c '...'。- 环境变量不展开:
ExecStart=/opt/$APP/bin不会展开,用EnvironmentFile配绝对路径。 StandardOutput=file:的文件不会自动轮转,用 journal 或配 logrotate。- 容器内 systemd 受限:需要
--privileged或 cgroup 挂载,一般不推荐容器里跑 systemd。 systemctl --user需要 lingering:用户级服务在用户退出后停止,loginctl enable-linger <user>可保持。status显示的日志被截断:不是没日志,是只显示尾部,用journalctl。
10. 速查表
systemctl start|stop|restart|reload <svc>
systemctl status <svc> --no-pager -l
systemctl enable --now <svc>
systemctl disable --now <svc>
systemctl is-active / is-enabled / is-failed <svc>
systemctl list-units --type=service --state=failed
systemctl list-unit-files --type=service
systemctl cat <svc> # 看 unit
systemctl edit <svc> # drop-in 覆盖
systemctl daemon-reload # 改完必做
systemctl reset-failed <svc>
systemctl mask / unmask <svc>
systemctl show -p MainPID -p ActiveState <svc>
systemctl list-timers --all
systemd-analyze blame / critical-chain / verify
journalctl -u <svc> -n 100 --no-pager