灯下哥谭 灯下哥谭
首页
关于
  • Hermes Agent 平台
  • Claude Code
  • OpenClaw
  • GPU 推理节点运维
  • DeepSeek Harness
  • MySQL 运维知识地图
  • Elasticsearch 运维知识地图
  • Redis 运维知识地图
  • TiDB 体系
  • DBA 常用 SQL 与命令
  • Nginx 运维知识地图
  • Prometheus 监控
  • Docker
  • Systemd
  • Iptables
  • Firewalld
  • Sshd
  • MySQL8 运维 SOP 手册
  • MySQL 实战 45 讲(读书笔记)
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

灯下哥谭

灯还亮着
首页
关于
  • Hermes Agent 平台
  • Claude Code
  • OpenClaw
  • GPU 推理节点运维
  • DeepSeek Harness
  • MySQL 运维知识地图
  • Elasticsearch 运维知识地图
  • Redis 运维知识地图
  • TiDB 体系
  • DBA 常用 SQL 与命令
  • Nginx 运维知识地图
  • Prometheus 监控
  • Docker
  • Systemd
  • Iptables
  • Firewalld
  • Sshd
  • MySQL8 运维 SOP 手册
  • MySQL 实战 45 讲(读书笔记)
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 工作笔记

  • 容器与编排

  • Nginx

  • 监控

  • 网络安全

  • 其他

    • Systemd编写服务管理脚本
      • 1. Unit(单元)的配置文件
      • 2. 服务类型 unit 的详细配置
        • [Unit] 部分
        • [Service] 部分
        • [Install] 部分
      • 3. Timer 类型 unit 的详细配置
      • 4. 配置 Redis 服务
        • 添加 Redis 配置文件
        • 配置由 systemd 管理的 Redis 服务
      • 5. 通过脚本定时备份文件
        • 创建 Timer unit 配置文件
      • 6. systemd 与 Supervisor:怎么选
        • 6.1 各自适用场景
        • 6.2 Supervisor 在没有 systemd 的场景
        • 6.3 两者在开机自启与日志上的差异
        • 6.4 Supervisor 最小配置
      • 7. 坑与边界
      • 8. 验证:怎么确认服务真的起来了
      • 参考资料
      • Agent 可直接解析的元数据块
    • WSL2 + Windows Terminal + VSCode:从架构差异到踩坑实录
    • Nginx WebDAV 配置实录:从模块原理到生产坑位
  • Linux笔记
  • 其他
灯下哥谭
2022-03-10
目录

Systemd编写服务管理脚本

本文讲解如何使用 systemd 编写服务管理脚本,包括 Unit 配置、Service 配置详解、Timer 定时任务,并补充 Supervisor 工具的对比与配置示例,帮助将前台进程托管为开机自启的常驻服务。

版本说明

本文写于 2022-03。
未在更高版本上重新验证,请以对应版本的官方文档为准。

# 1. Unit(单元)的配置文件

[Unit]
Description=Prometheus Server
Documentation=https://prometheus.io/docs/introduction/overview/
After=network.target

[Service]
User=prometheus
Restart=on-failure
WorkingDirectory=/usr/local/share/prometheus/
ExecStart=/usr/local/share/prometheus/prometheus \
          -config.file=/usr/local/share/prometheus/prometheus.yml

[Install]
WantedBy=multi-user.target
1
2
3
4
5
6
7
8
9
10
11
12
13
14

这是 Prometheus 服务的配置文件。将上述内容保存到文件 /lib/systemd/system/prometheus.service 中,然后就可以使用 systemctl 命令管理 Prometheus 服务了。注意,服务类型的配置文件名称必须以 .service 结尾。

查看上面配置信息的详细内容,我们会发现整个配置分为三个部分:

  • [Unit]: unit 本身的说明,以及与其它有依赖关系的服务的设置,包括在什么服务之后才启动此 unit。
  • [Service]: 不同的 unit 类型需要使用相对应的设置项目,服务类型的 unit 就是 [Service],这个项目主要规范服务启动的脚本、环境配置文件名、重新启动的方式等等。
  • [Install]: 这个部分主要设置该 unit 安装到哪个 target。

# 2. 服务类型 unit 的详细配置

配置文件分为三个部分,每个部分都可以提供详细的配置信息。为了精确控制服务的运行方式,我们需要了解这些详细的配置选项,并让服务以期望的方式运行。

# [Unit] 部分

  • Description: 关于该 unit 的简易说明。
  • Documentation: 文档相关内容,如 Documentation=https://prometheus.io/docs/introduction/overview/。
  • After: 说明本 unit 是在哪个服务启动后才启动,仅说明服务启动顺序,并没有强制要求。
  • Before: 与 After 相反,指定的服务启动前最好启动本服务。
  • Requires: 本 unit 需要在某个服务启动后才能启动,设置服务间的依赖性。
  • Wants: 与 Requires 相反,规范的是本 unit 之后还要启动哪些服务,如果设置的服务未启动成功,不会影响本 unit。
  • Conflicts: 这个项目后面接的服务如果已启动,则本 unit 无法启动,反之亦然。

# [Service] 部分

  • Type: 说明服务的启动方式,会影响到 ExecStart,主要有以下几种类型:

    • simple: 默认值,由 ExecStart 设置的程序来启动,启动后常驻于内存中。
    • forking: 由 ExecStart 指定的启动程序通过 spawns 产生子进程提供服务,然后父进程退出。
    • oneshot: 与 simple 类似,但程序工作完毕后即结束,不会常驻内存。
    • dbus: 与 simple 类似,但需在取得一个 D-Bus 名称后,才继续运行。
    • idle: 与 simple 类似,意思是要执行此服务必须在所有工作顺利执行完毕后才执行。
    • notify: 与 simple 类似,但需收到一个 sd_notify() 函数发送的消息后,才会继续运行。
  • ExecStart: 实际执行此服务的程序,接受 "命令 参数 参数..." 的格式,不能接受特殊字符,许多 bash 语法也不支持。

  • ExecStartPre 和 ExecStartPost: 分别在服务启动前后,执行额外的命令。

  • ExecStop: 用来实现 systemctl stop 命令,关闭服务。

  • ExecReload: 用来实现 systemctl reload 命令,重新加载服务的配置信息。

  • Restart: 定义服务在何种情况下自动重启,常用值包括:no(默认值,不重启), on-failure(仅在服务失败时重启), always(总是重启), on-abnormal(在异常退出时重启)等。

  • RestartSec: 与 Restart 配合使用,设置服务终止多长时间后重新启动,默认是 100ms。

  • KillMode: 定义systemd如何停止服务,可以是 process, control-group, mixed, none 之一。

  • TimeoutSec: 设置服务启动或关闭时的超时时间。

  • RemainAfterExit: 当设置为 yes 时,服务所属的所有程序都终止后,服务仍被视为活动状态。适用于Type=oneshot的服务。

  • User 和 Group: 指定服务以哪个用户和组的身份运行。

  • WorkingDirectory: 指定服务的工作目录。

  • Environment: 用来设置环境变量,可以多次使用。

  • EnvironmentFile: 通过文件方式设置环境变量,指定文件路径,内容格式类似 KEY=VALUE。

# [Install] 部分

  • WantedBy: 设置 unit 附挂在哪个 target unit 下面。
  • Also: 当前 unit 被 enable 时,Also 后面接的 unit 也要 enable。
  • Alias: 当 systemctl enable 相关服务时,此服务会进行链接文件的创建。

# 3. Timer 类型 unit 的详细配置

Timer 类型的 unit 主要用来执行定时任务,有可能取代 cron 服务。与服务类型的 unit 不同,timer unit 配置文件中的主要部分是 [Timer]。

  • OnActiveSec: 当 timers.target 启动后多久执行此 unit。
  • OnBootSec: 当开机后多久执行此 unit。
  • OnStartupSec: 当 systemd 第一次启动后多久执行此 unit。
  • OnUnitActiveSec: 管理的服务最后一次启动后,隔多久再执行一次。
  • OnUnitInactiveSec: 管理的服务最后一次停止后,隔多久再执行一次。
  • OnCalendar: 使用实际时间(非循环时间)的方式来启动服务,支持复杂的日期时间表达式。 例如:OnCalendar=*-*-* 02:00:00 表示每天凌晨2点执行 或者:OnCalendar=Mon,Tue *-*-* 00:00:00 表示每周一、二的零点执行
  • Unit: 一般不需要设置,若服务名称和 timer 名称不相同,则在 .timer 文件中通过 Unit 项指定服务名称。
  • Persistent: 当使用 OnCalendar 设置时,如果设为 yes,则表示如果因为系统关机错过了执行时间,则在下次启动时会立即执行一次。

# 4. 配置 Redis 服务

在 Ubuntu 上,我们一般会手动编译并安装 Redis。在安装完成后需要将 Redis 配置为 systemd 管理的服务,具体配置过程如下:

# 添加 Redis 配置文件

首先手动创建 /etc/redis 目录并添加配置文件:

$ sudo mkdir /etc/redis
1

然后将 Redis 配置文件 redis.conf 拷贝到 /etc/redis 目录中:

$ sudo cp /tmp/redis-4.0.0/redis.conf /etc/redis/
1

修改配置文件 /etc/redis/redis.conf 中的 supervised 为 systemd:

supervised systemd
1

继续修改配置文件 /etc/redis/redis.conf 中的工作目录:

dir /var/lib/redis
1

# 配置由 systemd 管理的 Redis 服务

创建 /etc/systemd/system/redis.service 文件:

$ sudo vim /etc/systemd/system/redis.service
1

编辑其内容如下:

[Unit]
Description=Redis In-Memory Data Store
After=network.target

[Service]
User=redis
Group=redis
ExecStart=/usr/local/bin/redis-server /etc/redis/redis.conf
ExecStop=/usr/local/bin/redis-cli shutdown
Restart=always

[Install]
WantedBy=multi-user.target
1
2
3
4
5
6
7
8
9
10
11
12
13

启动服务并配置为开机启动:

$ sudo systemctl start redis
$ sudo systemctl enable redis
$ sudo systemctl status redis
1
2
3

# 5. 通过脚本定时备份文件

备份文件的 bash 脚本:

#!/bin/bash
mydate()
{
        date "+%Y%m%d%H%M%S"
}
backupdate=$(mydate)
tar -zcf /tmp/backup.${backupdate}.tar.gz /home/nick/learn
1
2
3
4
5
6
7

将上述代码保存到 /usr/local/bin/backupdir.sh 文件,并添加可执行权限:

$ sudo chmod +x /usr/local/bin/backupdir.sh
1

然后创建 service unit 配置文件:

[Unit]
Description=nick backup learn dir service

[Service]
User=nick
Group=nick
Type=simple
ExecStart=/usr/local/bin/backupdir.sh

[Install]
WantedBy=multi-user.target
1
2
3
4
5
6
7
8
9
10
11

将上述 unit 配置保存到 /etc/systemd/system/nickbak.service 文件中。然后执行以下命令测试服务执行情况:

$ sudo systemctl daemon-reload
$ sudo systemctl start nickbak.service
1
2

这样的备份任务只会在执行 sudo systemctl start nickbak.service 时执行一次。下面我们通过 timer unit 将其配置为定时执行。

# 创建 Timer unit 配置文件

[Unit]
Description=nick backup learn dir timer

[Timer]
OnCalendar=*:0/15
Persistent=true
Unit=nickbak.service

[Install]
WantedBy=multi-user.target
1
2
3
4
5
6
7
8
9
10

将上述 unit 配置保存到 /etc/systemd/system/nickbak.timer 文件中。配置中的 OnCalendar=*:0/15 表示每 15 分钟执行一次 nickbak.service 服务。

执行以下命令将 nickbak.timer 设置为开机启动,并启动 nickbak.timer:

$ sudo systemctl daemon-reload
$ sudo systemctl enable nickbak.timer
$ sudo systemctl start nickbak.timer
1
2
3

查看 nickbak.timer 的状态:

$ sudo systemctl status nickbak.timer
1

从现在开始 nickbak.timer 会每隔 15 分钟执行一次 nickbak.service 服务。


# 6. systemd 与 Supervisor:怎么选

Supervisor 用来把「一个前台运行的脚本」变成「开机自启、崩了自动拉起、日志有地方看」的常驻服务,比手写 systemd unit 更轻量。

# 6.1 各自适用场景

场景 推荐方案 原因
现代 Linux 服务器(主流发行版) systemd 原生支持、开机自启完善、日志集成 journalctl
容器环境(Docker/K8s) supervisor systemd 在容器内不稳定,适合 supervisor 管理单容器多进程
仅有 Python 环境的老系统 supervisor supervisor 纯 Python 依赖少,部署简单
需要管理复杂进程树 systemd cgroups 隔离更安全,支持依赖控制
快速原型/临时任务 supervisor 配置文件比 unit 文件更简洁

# 6.2 Supervisor 在没有 systemd 的场景

在 Docker 容器或没有 systemd 的老系统中,supervisor 是管理常驻进程的首选:

# 安装 supervisor
pip3 install supervisor

# 创建配置目录
mkdir -p /etc/supervisor/conf.d/

# 启动 supervisor(前台模式,适合容器)
supervisord -c /etc/supervisor/supervisord.conf
1
2
3
4
5
6
7
8

# 6.3 两者在开机自启与日志上的差异

维度 systemd Supervisor
开机自启 systemctl enable <service> 自动注册 需配 systemd 管理 supervisord 自身,或在 init 脚本中启动
日志查看 journalctl -u <service>,自动轮转 配置文件指定 stdout_logfile,需自行配置 logrotate
日志保留 journalctl 默认保留策略,可配 journald 依赖外部工具,易累积占磁盘
进程隔离 cgroups 资源限制、优先级控制 无 cgroups,仅进程管理

# 6.4 Supervisor 最小配置

如果你的环境没有 systemd(比如某些容器镜像),可以用 supervisor 管理服务:

[program:myapp]
; 工作目录
directory=/data/myapp/
; 启动命令
command=/data/myapp/bin/myapp -config /data/myapp/conf/app.toml
; 自动启动
autostart=true
; 异常退出自动重启
autorestart=true
; 启动成功后视为 running 的秒数
startsecs=3
; 以哪个用户运行
user=root
; stdout 日志
stdout_logfile=/var/log/myapp.log
; stderr 重定向到 stdout
redirect_stderr=true
; 日志文件大小(50MB)
stdout_logfile_maxbytes=50MB
; 日志备份数
stdout_logfile_backups=10
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

常用管理命令:

supervisorctl status        # 查看程序状态
supervisorctl start myapp   # 启动
supervisorctl stop myapp    # 停止
supervisorctl restart myapp # 重启
supervisorctl tail -f myapp # 查看日志
supervisorctl update        # 重新加载配置
1
2
3
4
5
6

# 7. 坑与边界

  • Type= 选错,systemd 会误判服务状态。之所以会误判,是因为默认的 Type=simple 认定「ExecStart 起来的那个进程就是主进程」;如果程序自己 fork 到后台再让父进程退出(很多老服务的 -d、--daemon 参数就是这个行为),systemd 看到父进程退出即判定服务已结束,于是按 Restart= 策略反复重拉。这种程序要么用 Type=forking 并配 PIDFile=,要么去掉守护参数让它前台运行、继续用 Type=simple(更推荐,日志也能直接进 journal)。

  • Restart=always 撞上启动限流,会把服务永久拉黑。systemd 默认 StartLimitIntervalSec=10s、StartLimitBurst=5:10 秒内启动超过 5 次,之后就不再重启,systemctl status 显示 start request repeated too quickly。此时改完配置直接 restart 是没用的,原因是限流计数不会因为配置变更而清零,必须先 systemctl reset-failed <name> 把它清掉。给会快速失败的服务加 RestartSec=5 拉开间隔,比调大 burst 更有效。

  • Timer 的 OnCalendar 跟随系统时区,不是 UTC。systemctl show <name>.timer -p NextElapseUSecRealtime 看到的下次触发时间才是权威值;机器时区改过、或容器里没挂 /etc/localtime,定时任务会整体偏移。跨时区部署时用 OnCalendar=*-*-* 02:00:00 UTC 显式声明。

  • ExecStart= 不走 shell。管道、重定向、&&、变量展开都不生效,写了也只会被当成普通参数传给程序。确实需要 shell 语义时写成 ExecStart=/bin/bash -c '...',或者把逻辑放进脚本文件。

  • 改完配置必须 daemon-reload。systemd 在内存里缓存 unit 定义,只 restart 不 daemon-reload,跑的还是旧配置——这是最常见的「我明明改了却没生效」。


# 8. 验证:怎么确认服务真的起来了

# 1. 状态:Active 那一行必须是 active (running);
#    看到 activating / auto-restart 说明还在反复重启,不算起来了
systemctl status myapp.service

# 2. 主进程是不是预期的那个(Type 配错时这里会对不上)
systemctl show myapp.service -p MainPID -p Type -p Result

# 3. 日志:看有没有真正进入工作循环,而不是只打印了启动横幅
journalctl -u myapp.service -n 50 --no-pager

# 4. 重启计数:非 0 说明它在反复挂
systemctl show myapp.service -p NRestarts

# 5. 开机自启是否真的登记了
systemctl is-enabled myapp.service
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

Timer 则额外确认下次触发时间与最近一次执行结果:

systemctl list-timers myapp.timer --no-pager
journalctl -u myapp.service --since today --no-pager
1
2

# 参考资料

  • systemd 服务管理 (opens new window)

# Agent 可直接解析的元数据块

{
  "runbook": {
    "task": "systemd-service-and-timer",
    "permalinks": ["/pages/8f683a/"],
    "merged_from": ["/pages/15e153/"],
    "category": "linux/service-management",
    "tags": ["systemd", "timer", "supervisor", "service"],
    "key_pitfalls": [
      "Type= 选错导致 systemd 误判主进程",
      "StartLimitBurst 拉黑后需 systemctl reset-failed",
      "OnCalendar 跟随系统时区",
      "ExecStart 不走 shell",
      "改配置必须 daemon-reload"
    ],
    "verified_date": "2026-09"
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#SRE#systemd
上次更新: 9/11/2026

← 企业 Docker+OpenVPN+LDAP+OTP 快速搭建实战 WSL2 + Windows Terminal + VSCode:从架构差异到踩坑实录→

最近更新
01
DeepSeek Harness 实战 06|学习笔记:插件、工具、技能不在同一个维度上 原创
09-11
02
DeepSeek Harness 实战 05|让两个编码 Agent 共用一份长期记忆 原创
09-09
03
DeepSeek Harness 实战 04|学习笔记:从「已知限制」里读出三处设计张力 原创
09-08
更多文章>
Theme by Vdoing
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式