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 找出启动最慢的服务,并解释依赖链。