systemd 教程 / 生产环境最佳实践
生产环境最佳实践
在生产环境中使用 systemd 需要遵循一系列最佳实践,以确保服务的可靠性、安全性和可维护性。
1. Unit 文件编写规范
1.1 文件结构规范
# /etc/systemd/system/myapp.service
[Unit]
Description=My Application Service
Documentation=https://myapp.example.com/docs
After=network.target postgresql.service
Requires=postgresql.service
Wants=redis.service
ConditionPathExists=/opt/myapp/bin/myapp
[Service]
Type=simple
User=myapp
Group=myapp
WorkingDirectory=/opt/myapp
EnvironmentFile=-/etc/sysconfig/myapp
ExecStartPre=/opt/myapp/bin/pre-start.sh
ExecStart=/opt/myapp/bin/myapp --config /etc/myapp/config.yml
ExecReload=/bin/kill -HUP $MAINPID
ExecStop=/opt/myapp/bin/stop.sh
Restart=on-failure
RestartSec=10
StartLimitBurst=5
StartLimitIntervalSec=300
TimeoutStartSec=300
TimeoutStopSec=30
KillMode=mixed
LimitNOFILE=65535
NoNewPrivileges=true
PrivateTmp=true
[Install]
WantedBy=multi-user.target
1.2 文件放置规范
| 路径 |
用途 |
是否可编辑 |
/etc/systemd/system/ |
管理员自定义服务 |
✅ |
/usr/lib/systemd/system/ |
发行版提供的服务 |
❌(使用 override) |
/run/systemd/system/ |
运行时生成的服务 |
❌(临时) |
⚠️ 注意:永远不要直接修改 /usr/lib/systemd/system/ 下的文件,使用 systemctl edit 创建 override。
2. 服务依赖设计
2.1 After/Requires/Wants 选择
| 依赖类型 |
含义 |
使用场景 |
After= |
启动顺序 |
必须在之后启动 |
Requires= |
强依赖 |
依赖服务失败则本服务也失败 |
Wants= |
弱依赖 |
依赖服务失败不影响本服务 |
Requisite= |
必需依赖 |
依赖服务未运行则拒绝启动 |
Conflicts= |
冲突 |
互斥服务 |
2.2 依赖设计示例
# 场景 1:Web 应用依赖数据库(强依赖)
[Unit]
After=network.target postgresql.service
Requires=postgresql.service # 数据库必须运行
Wants=redis.service # Redis 可选
# 场景 2:微服务架构(弱依赖避免级联失败)
[Unit]
After=network.target
Wants=auth-service.service inventory-service.service
3. 重启策略设计
3.1 重启参数配置
[Service]
# 重启条件
Restart=on-failure # 失败时重启
Restart=on-abnormal # 异常退出时重启
Restart=always # 总是重启(不推荐)
Restart=no # 不重启
# 重启间隔
RestartSec=10
# 启动限制(防止重启风暴)
StartLimitBurst=5 # 时间窗口内最多启动 5 次
StartLimitIntervalSec=300 # 5 分钟时间窗口
3.2 重启策略选择
| 场景 |
推荐策略 |
说明 |
| Web 服务器 |
on-failure |
正常退出不重启 |
| 后台守护进程 |
on-failure |
正常退出不重启 |
| 一次性任务 |
no |
执行一次即完成 |
| 关键服务 |
on-abnormal |
仅异常退出时重启 |
3.3 看门狗(Watchdog)
[Service]
Type=notify
WatchdogSec=30
NotifyAccess=main
# 应用需要定期发送 WATCHDOG=1 通知 systemd
4. 日志策略
4.1 journald 配置
# /etc/systemd/journald.conf
[Journal]
Storage=persistent
SystemMaxUse=2G
SystemKeepFree=1G
SystemMaxFileSize=100M
MaxRetentionSec=30day
Compress=yes
ForwardToSyslog=yes
4.2 日志轮转
# 手动清理旧日志
sudo journalctl --vacuum-time=30d
sudo journalctl --vacuum-size=2G
# 查看日志占用空间
journalctl --disk-usage
# 立即应用配置
sudo systemctl restart systemd-journald
5. 安全加固
5.1 最小权限原则
[Service]
User=myapp
Group=myapp
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
PrivateTmp=true
PrivateDevices=true
ReadOnlyPaths=/etc/myapp
ReadWritePaths=/var/lib/myapp /var/log/myapp /run/myapp
5.2 Capabilities 限制
[Service]
# 仅授予必要权限
CapabilityBoundingSet=CAP_NET_BIND_SERVICE CAP_DAC_READ_SEARCH
# 或完全移除
CapabilityBoundingSet=
5.3 系统调用过滤
[Service]
SystemCallFilter=@system-service @network-io @file-system
SystemCallFilter=~@mount @reboot @swap @debug
5.4 网络限制
[Service]
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX
RestrictNamespaces=true
RestrictSUIDSGID=true
RestrictRealtime=true
5.5 安全评分检查
# 评估服务安全等级
systemd-analyze security myapp.service
# 目标:< 2.0 (OK)
6. 资源限制(cgroups)
6.1 CPU 和内存
[Service]
CPUQuota=50%
CPUWeight=100
CPUAffinity=0 1 2 3
MemoryMax=1G
MemoryHigh=512M
OOMPolicy=continue
6.2 I/O 和进程
[Service]
IOWeight=500
IOReadBandwidthMax=/dev/sda 50M
TasksMax=100
LimitNOFILE=65535
LimitNPROC=65535
6.3 Slice 管理
# /etc/systemd/system/myapp.slice
[Unit]
Description=MyApp Slice
[Slice]
CPUQuota=80%
MemoryMax=2G
TasksMax=200
7. 监控集成
7.1 Prometheus node_exporter
# /etc/systemd/system/node_exporter.service
[Unit]
Description=Prometheus Node Exporter
After=network.target
[Service]
Type=simple
User=prometheus
ExecStart=/usr/local/bin/node_exporter \
--collector.systemd \
--web.listen-address=:9100
Restart=on-failure
RestartSec=5
NoNewPrivileges=true
ProtectSystem=strict
[Install]
WantedBy=multi-user.target
7.2 自定义健康检查
#!/bin/bash
# /opt/myapp/bin/healthcheck.sh
# 检查进程
if ! pgrep -x myapp > /dev/null; then
echo "Process not running"; exit 1
fi
# 检查端口
if ! ss -tlnp | grep -q ":8080"; then
echo "Port 8080 not listening"; exit 1
fi
# 检查 HTTP
if ! curl -sf http://localhost:8080/health > /dev/null; then
echo "Health check failed"; exit 1
fi
echo "OK"
8. 配置管理集成
8.1 Ansible 示例
# deploy-service.yml
---
- name: Deploy systemd service
hosts: webservers
become: yes
tasks:
- name: Deploy systemd unit file
template:
src: templates/myapp.service.j2
dest: "/etc/systemd/system/myapp.service"
notify: Reload systemd
- name: Enable and start service
systemd:
name: myapp
enabled: yes
state: started
handlers:
- name: Reload systemd
systemd:
daemon_reload: yes
8.2 Puppet 示例
file { '/etc/systemd/system/myapp.service':
ensure => file,
content => template('myapp/myapp.service.erb'),
notify => Exec['systemctl-daemon-reload'],
}
exec { 'systemctl-daemon-reload':
command => '/bin/systemctl daemon-reload',
refreshonly => true,
}
service { 'myapp':
ensure => 'running',
enable => true,
}
9. 生产 Checklist
9.1 部署前检查
| 检查项 |
命令 |
期望结果 |
| Unit 文件语法 |
systemd-analyze verify myapp.service |
无错误 |
| 安全评分 |
systemd-analyze security myapp.service |
< 2.0 |
| 依赖关系 |
systemctl list-dependencies myapp.service |
所有依赖正常 |
| 用户存在 |
id myapp |
用户存在 |
| 端口可用 |
ss -tlnp | grep :8080 |
端口未被占用 |
9.2 部署后检查
| 检查项 |
命令 |
期望结果 |
| 服务状态 |
systemctl status myapp.service |
active (running) |
| 日志无错误 |
journalctl -u myapp.service -p err |
无错误 |
| 端口监听 |
ss -tlnp | grep :8080 |
端口已监听 |
| 健康检查 |
curl http://localhost:8080/health |
返回 OK |
9.3 运维 Checklist
| 检查项 |
频率 |
说明 |
| 服务状态 |
每日 |
检查所有关键服务 |
| 日志检查 |
每日 |
检查错误和警告 |
| 资源使用 |
每周 |
CPU、内存、磁盘 |
| 安全更新 |
每月 |
更新 systemd |
| 配置备份 |
每月 |
备份 Unit 文件 |
10. 故障恢复
10.1 快速回滚
# 使用版本化的 Unit 文件
# /etc/systemd/system/myapp.service -> myapp.service.v1.0
sudo cp /etc/systemd/system/myapp.service.v1.0 /etc/systemd/system/myapp.service
sudo systemctl daemon-reload
sudo systemctl restart myapp.service
⚠️ 注意事项
- 永远不要修改
/usr/lib/systemd/system/:使用 systemctl edit 创建 override
- 测试环境先验证:所有配置变更先在测试环境验证
- 配置版本控制:Unit 文件应纳入版本控制
- 监控告警:配置服务异常告警
- 定期审查:定期审查服务配置和安全设置
💡 提示
- 使用
systemctl edit 创建 override 配置,保持原始文件不变
systemd-analyze security 可以快速评估服务安全等级
- 使用
systemctl show 查看服务的所有配置属性
- 使用 Slice 组织相关服务,便于资源管理
- 配置
StartLimitBurst 和 StartLimitIntervalSec 防止重启风暴
扩展阅读