1.5.1 Systemd与服务单元文件解析

Systemd 是现代 Linux 的初始化与服务管理框架,负责系统引导、服务生命周期管理、依赖编排与并行启动。它通过 unit(单元)统一管理系统资源,常见类型包括:service、socket、target、timer、mount、device、path、slice、scope。理解 unit 文件结构与语义是运维排障与服务定制的基础。

原理草图(系统引导与服务依赖关系):

文章图片

Unit 文件由三部分组成:
- [Unit]:描述与依赖关系
- [Service]:服务具体启动方式(仅 service 类型)
- [Install]:定义安装与启用方式(如系统启动时如何关联 target)

Unit 文件搜索路径(优先级从高到低):
- /etc/systemd/system/(自定义与覆盖)
- /run/systemd/system/(运行时临时)
- /usr/lib/systemd/system/(发行版默认)

关键字段解析:
- [Unit]
- Description:服务描述
- After/Before:启动顺序约束(非依赖)
- Requires/Wants:强依赖/弱依赖
- Conflicts:互斥服务
- [Service]
- Type:服务类型(simple、forking、oneshot、notify、idle)
- ExecStart:启动命令
- ExecReload:重载命令
- ExecStop:停止命令
- Restart:失败重启策略(no、on-failure、always)
- RestartSec:重启间隔
- User/Group:运行用户
- WorkingDirectory:工作目录
- Environment/EnvironmentFile:环境变量
- TimeoutStartSec/TimeoutStopSec:超时控制
- [Install]
- WantedBy:被哪个 target 拉起(如 multi-user.target)
- RequiredBy:强依赖绑定
- Alias:别名

安装与基础检查(systemd 一般随系统默认安装):

# 查看 systemd 版本与 PID 1
systemctl --version
ps -p 1 -o comm=
# 预期输出:systemd

示例:创建一个可运行的自定义服务

# 1) 准备可执行脚本
sudo tee /usr/local/bin/myapp.sh >/dev/null <<'EOF'
#!/bin/bash
echo "MyApp started at $(date)" >> /var/log/myapp.log
sleep 300
EOF
sudo chmod +x /usr/local/bin/myapp.sh

# 2) 编写 unit 文件
sudo tee /etc/systemd/system/myapp.service >/dev/null <<'EOF'
[Unit]
Description=MyApp Service
After=network.target

[Service]
Type=simple
ExecStart=/usr/local/bin/myapp.sh
Restart=on-failure
RestartSec=2
User=root
WorkingDirectory=/tmp

[Install]
WantedBy=multi-user.target
EOF

# 3) 载入与启用
sudo systemctl daemon-reload
sudo systemctl enable --now myapp.service

# 4) 验证状态与日志
systemctl status myapp.service --no-pager
tail -n 3 /var/log/myapp.log

命令解释与常用检查:

systemctl list-units --type=service     # 列出所有 service 单元
systemctl cat nginx.service             # 查看完整 unit(含 drop-in)
systemctl show myapp.service -p ActiveState -p SubState  # 关键状态
systemd-analyze blame                   # 启动耗时分析
systemd-analyze critical-chain          # 依赖链与阻塞定位

常见服务类型说明(示例):

# simple:前台运行,默认类型
Type=simple
ExecStart=/usr/bin/myapp

# forking:传统守护进程,启动后 fork
Type=forking
ExecStart=/usr/sbin/sshd

# oneshot:一次性任务
Type=oneshot
ExecStart=/usr/bin/echo "run once"
RemainAfterExit=yes

依赖与启动顺序要点:
- Requires + After:确保依赖服务存在且先启动
- Wants + After:尽力启动依赖但不影响当前服务
- 仅 After 不保证依赖存在,仅保证顺序

示例:添加依赖与冲突

[Unit]
Description=Demo With Dependencies
After=network.target
Requires=network.target
Conflicts=shutdown.target

单元文件覆盖与配置建议:
- 使用 systemctl edit 创建 drop-in 覆盖:/etc/systemd/system/.d/*.conf
- 不直接修改 /usr/lib/systemd/system 中的默认文件
- 修改后需执行 systemctl daemon-reload 生效

示例:为已有服务添加环境变量

sudo systemctl edit nginx.service
# /etc/systemd/system/nginx.service.d/override.conf
[Service]
Environment="APP_ENV=prod"
sudo systemctl daemon-reload
sudo systemctl restart nginx.service

排错流程与示例:

# 1) 查看失败原因与退出码
systemctl status myapp.service --no-pager

# 2) 查看详细日志(最近 50 行)
journalctl -u myapp.service -n 50 --no-pager

# 3) 校验 unit 语法
systemd-analyze verify /etc/systemd/system/myapp.service

# 4) 常见问题:ExecStart 路径错误
# 现象:code=exited, status=203/EXEC
# 处理:修正 ExecStart 路径并重载

练习:
1. 编写一个 oneshot 单元,每次开机生成 /var/log/bootstamp.log,内容包含时间戳。
2. 使用 systemctl edit 为 sshd.service 添加 Restart=on-failure,验证配置生效。
3. 使用 systemd-analyze critical-chain 找出启动最慢的服务,并解释依赖链。