systemd 是现代 Linux 发行版中默认的初始化系统和服务管理器,它通过单元文件(unit file)来定义服务、挂载点、定时任务等资源。对于系统管理员和开发者而言,掌握单元文件的编写是管理 Linux 服务的核心技能。本文将深入探讨单元文件中的三大关键主题:依赖管理、资源限制与自动重启,帮助你编写出健壮、可控的服务配置。
一、单元文件基础结构
一个典型的服务单元文件以 .service 为后缀,通常存放在 /etc/systemd/system/ 或 /usr/lib/systemd/system/ 目录下。其基本结构由多个段落(section)组成,其中最常用的是 [Unit]、[Service] 和 [Install]。
[Unit]
Description=My Custom Service
After=network.target
[Service]
ExecStart=/usr/bin/my-service
Restart=on-failure
[Install]
WantedBy=multi-user.target
[Unit] 段定义元数据和依赖关系,[Service] 段描述服务进程的启动、停止和资源控制方式,[Install] 段则决定服务在何种目标(target)下被启用。
二、依赖管理:After、Before、Requires 与 Wants
systemd 的依赖管理非常灵活,但容易混淆。理解不同指令的语义是避免启动顺序错误的关键。
1. 顺序依赖:After 与 Before
After 和 Before 仅控制启动顺序,不隐含依赖关系。例如:
[Unit]
After=network.target
Before=nginx.service
这表示本服务应在 network.target 之后、nginx.service 之前启动。但如果 network.target 启动失败,本服务仍会尝试启动。
2. 需求依赖:Requires 与 Wants
Requires:强依赖。如果被依赖的单元启动失败,本单元也会失败。Wants:弱依赖。被依赖单元启动失败不影响本单元,只是尽力启动对方。
[Unit]
Requires=postgresql.service
Wants=redis.service
After=postgresql.service redis.service
注意:Requires 并不自动包含 After,因此通常需要同时指定 After 来确保顺序。
3. 冲突与排斥:Conflicts
Conflicts 表示互斥关系。如果本单元启动,被冲突的单元会被停止;反之亦然。常用于替代服务场景。
[Unit]
Conflicts=apache2.service
4. 实际建议
- 对于网络服务,至少添加
After=network.target或After=network-online.target。 - 避免过度使用
Requires,以免一个次要服务失败导致整个依赖链崩溃。 - 使用
systemd-analyze verify检查依赖循环或语法错误。
三、资源限制:CPU、内存与 I/O 控制
systemd 提供了基于 cgroup 的资源限制能力,可以直接在 [Service] 段中配置,无需额外工具。
1. CPU 限制
CPUQuota=:限制 CPU 使用率百分比。例如CPUQuota=50%表示最多使用一个核心的 50%。CPUShares=:相对权重,默认 1024。数值越高,获得的 CPU 时间越多。AllowedCPUs=:绑定到特定 CPU 核心(需 systemd 244+)。
[Service]
CPUQuota=200%
AllowedCPUs=0-1
2. 内存限制
MemoryMax=:硬限制,超过则触发 OOM 杀死进程。MemoryHigh=:软限制,超过后系统会尝试回收内存,但不会立即杀死。MemorySwapMax=:限制交换分区使用。
[Service]
MemoryMax=512M
MemoryHigh=400M
3. I/O 限制
IOWeight=:相对 I/O 权重(1-10000)。IOReadBandwidthMax=与IOWriteBandwidthMax=:限制特定设备的读写带宽。
[Service]
IOWeight=100
IOWriteBandwidthMax=/dev/sda 10M
4. 其他限制
TasksMax=:限制进程/线程数。LimitNOFILE=:限制打开文件描述符数量。
这些限制在服务启动时自动应用,可通过 systemd-cgtop 或 systemctl status 查看实际使用情况。
四、自动重启:Restart 策略与退避机制
服务意外退出时自动重启是生产环境的基本要求。systemd 提供了细粒度的重启控制。
1. Restart 指令
[Service]
Restart=on-failure
RestartSec=5s
常用取值:
no:不重启(默认)。on-success:仅正常退出时重启。on-failure:非零退出码、信号杀死、超时等异常时重启。on-abnormal:仅被信号杀死或超时时重启。always:无论退出状态如何都重启。
2. 重启退避与限制
为避免频繁重启导致系统资源耗尽,systemd 内置了退避机制:
RestartSec=:重启前等待时间,默认 100ms。StartLimitIntervalSec=与StartLimitBurst=:在指定时间间隔内允许的最大启动次数。超过后服务进入 failed 状态,不再重启。
[Unit]
StartLimitIntervalSec=60
StartLimitBurst=3
[Service]
Restart=on-failure
RestartSec=10s
上述配置表示:60 秒内最多启动 3 次,每次重启前等待 10 秒。若超过限制,需手动 systemctl reset-failed 后重新启动。
3. 重启时的清理
对于需要清理临时文件或套接字的服务,可配合 ExecStopPost= 或使用 RuntimeDirectory= 自动管理运行时目录。
五、完整示例与最佳实践
以下是一个综合示例,展示了一个带有依赖、资源限制和自动重启的 Web 服务单元文件:
[Unit]
Description=My Web Application
After=network-online.target postgresql.service
Wants=network-online.target
Requires=postgresql.service
[Service]
Type=simple
User=webapp
Group=webapp
WorkingDirectory=/opt/webapp
ExecStart=/opt/webapp/bin/server --port 8080
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-failure
RestartSec=5s
StartLimitIntervalSec=120
StartLimitBurst=5
CPUQuota=150%
MemoryMax=1G
MemoryHigh=800M
TasksMax=256
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
最佳实践总结:
- 明确依赖类型,优先使用
After+Wants而非Requires,除非确实需要强依赖。 - 为关键服务设置合理的资源上限,防止单个服务拖垮系统。
- 始终配置
Restart和RestartSec,并设置启动频率限制。 - 使用
systemd-analyze verify和systemctl cat验证配置。 - 修改单元文件后执行
systemctl daemon-reload使更改生效。
通过熟练掌握依赖管理、资源限制与自动重启这三项核心能力,你可以编写出适应复杂生产环境的 systemd 单元文件,让 Linux 服务运行得更加稳定、可控。
未经允许不得转载:任鹏个人博客 » systemd 单元文件编写指南:依赖管理、资源限制与自动重启


朋友圈点赞图在线生成源码