systemd-notify 可让 Shell 脚本向 systemd 主动上报状态,需在服务单元中设 Type=notify 和 NotifyAccess=all,并在脚本中分阶段调用 --status、--ready 等命令实现状态驱动的依赖编排。

在 Linux 中,通过 systemd-notify 让 Shell 脚本主动向 systemd 服务管理器上报初始化状态,是实现“服务就绪等待”与“依赖编排”的关键手段。它能让上游服务(如 Nginx、Kubernetes kubelet 或其他依赖你的服务)真正等到业务进程完成加载、端口就绪、健康检查通过后再启动,而非仅靠简单的 ExecStart 返回即认为就绪。
一、前提:启用 NotifyMode 并配置服务单元
systemd 默认不监听通知,需显式开启。在你的服务单元文件(如 /etc/systemd/system/myapp.service)中必须设置:
-
NotifyAccess=all:允许任意进程(包括子 shell)调用
systemd-notify - Type=notify:告诉 systemd 使用“通知就绪”模式,而非默认的 fork+exit 模式
- 可选但推荐:Restart=on-failure 和 StartLimitIntervalSec=0,便于调试失败重试
示例最小单元配置:
[Unit] Description=My App with Progress Reporting After=network.target <p>[Service] Type=notify NotifyAccess=all ExecStart=/opt/myapp/start.sh Restart=on-failure RestartSec=5</p><p>[Install] WantedBy=multi-user.target
二、Shell 脚本中分阶段调用 systemd-notify
在 start.sh 中,每完成一个关键初始化步骤,就发送一条状态更新。systemd 会记录这些信息,并可通过 systemctl show myapp.service -p StatusText 或 journalctl -u myapp 查看。
- 用
systemd-notify --status="Loading config..."更新状态文本(显示在systemctl status中) - 用
systemd-notify --ready表示主进程已启动并可接受请求(触发READY=1) - 用
systemd-notify --progress=30上报进度百分比(需配合--progress参数启用,且要求 systemd v240+) - 用
systemd-notify --booted仅用于特殊场景(如 initramfs),普通服务不用
注意:所有通知必须在 ExecStart 启动的主进程上下文中执行(即不能 fork 到后台再发,除非用 --pid=1 指定父 PID)。
三、典型进度脚本结构(含超时与错误防护)
以下是一个带健壮性控制的初始化脚本骨架:
#!/bin/bash # /opt/myapp/start.sh set -e <h1>1. 标记启动中</h1><p>systemd-notify --status="Starting: loading configs" if ! cp /etc/myapp/config.yaml /run/myapp/config.yaml; then systemd-notify --status="ERROR: config load failed" exit 1 fi</p><h1>2. 初始化数据库连接(带超时)</h1><p>systemd-notify --status="Connecting to database" timeout 30s bash -c 'until nc -z db.local 5432; do sleep 2; done' if [ $? -ne 0 ]; then systemd-notify --status="ERROR: DB unreachable" exit 1 fi</p><h1>3. 启动主进程(保持前台运行)</h1><p>systemd-notify --status="Launching worker" --ready exec /usr/local/bin/myapp-server --config /run/myapp/config.yaml
关键点:--ready 必须在 exec 前调用,否则 systemd 会因超时(默认 90s)而杀掉服务;exec 确保主进程替换当前 shell,避免双进程问题。
四、高可用编排中的实际价值
仅靠 Type=notify 不足以构建可靠依赖链,需结合单元依赖关系:
- 让 Web 服务等待你的应用就绪:
After=myapp.service+Wants=myapp.service,并在 Web 单元中设StartLimitIntervalSec=0避免因 myapp 尚未 ready 导致反复重启 - 使用
ConditionPathExists=/run/myapp/ready作为额外守卫(配合脚本 touch 文件),实现双重就绪判断 - 在 Kubernetes Init Container 或 Ansible playbook 中,可用
systemctl is-system-running或轮询systemctl show myapp -p SubState | grep running判断是否真正进入 running 状态
这种机制使“服务启动顺序”从“时间驱动”变为“状态驱动”,是云原生环境中跨组件协调的基础能力之一。


















