跳转到内容
搜索 Fedora 文档

    浏览当前文档目录

    systemd 实操:写一个 service、一个 timer,以及失败怎么查

    Fedora 上「让服务开机自启」不是往 /etc/rc.local 里加一行,而是写一个 unit。定时任务也一样——systemd timer 正在取代 cron(F45 最小化镜像干脆不带 crontab)。

    下面是 F45 上的实测。

    Terminal window
    systemctl --version | head -2
    systemctl get-default
    systemd 261 (261.2-1.fc45)
    +PAM +AUDIT +SELINUX -APPARMOR +IMA +IPE +SMACK +SECCOMP ...
    multi-user.target

    命令行版 Fedora 默认是 multi-user.target(无图形界面);桌面版是 graphical.target。

    看看系统里有多少单元:

    Terminal window
    systemctl list-unit-files --type=service --no-pager | tail -1
    systemctl list-unit-files --type=timer --no-pager | tail -1
    278 unit files listed.
    12 unit files listed.
    位置谁写的优先级升级会不会覆盖
    /usr/lib/systemd/system/包管理器最低会
    /run/systemd/system/运行时生成中重启即失
    /etc/systemd/system/你最高不会

    自己写的放 /etc/systemd/system/。别改 /usr/lib 下的文件——升级会覆盖,而且排查时看不出被谁改过。

    Terminal window
    sudo tee /etc/systemd/system/lab-hello.service >/dev/null <<'EOF'
    [Unit]
    Description=Lab hello service
    After=network-online.target
    [Service]
    Type=oneshot
    ExecStart=/bin/sh -c 'date >> /var/log/lab-hello.log; echo hello >> /var/log/lab-hello.log'
    RemainAfterExit=yes
    [Install]
    WantedBy=multi-user.target
    EOF
    sudo systemctl daemon-reload
    sudo systemctl start lab-hello.service
    systemctl status lab-hello.service --no-pager | head -6
    ● lab-hello.service - Lab hello service
    Loaded: loaded (/etc/systemd/system/lab-hello.service; disabled; preset: disabled)
    Drop-In: /usr/lib/systemd/system/service.d
    └─10-timeout-abort.conf
    Active: active (exited) since Tue 2026-10-27 17:41:46 UTC; 9ms ago
    Invocation: 45ec53c7e0f24c72883a6d099d034a05

    三处细节值得注意:

    • disabled:文件写好了但没 enable,重启不会自动跑;
    • active (exited):oneshot + RemainAfterExit=yes 的典型状态——脚本跑完了,但服务”仍算活跃”;
    • Drop-In 那两行不是你的:是发行版加的公共超时策略。
    Type什么时候算”起来了”适合
    simple(默认)进程一启动就算常驻前台进程
    oneshot进程跑完才算一次性脚本、初始化
    forking父进程退出才算老式守护进程(自己 daemonize)
    notify程序调用 sd_notify 才算需要”真正就绪”再判活

    systemd 的 unit 关系:enable 在 target.wants 下建符号链接,timer 到点拉起对应 service

    timer 需要一对文件:一个 .service 干活,一个 .timer 定时。

    Terminal window
    sudo tee /etc/systemd/system/lab-tick.service >/dev/null <<'EOF'
    [Unit]
    Description=Lab tick
    [Service]
    Type=oneshot
    ExecStart=/bin/sh -c 'echo tick $(date +%s) >> /var/log/lab-tick.log'
    EOF
    sudo tee /etc/systemd/system/lab-tick.timer >/dev/null <<'EOF'
    [Unit]
    Description=Lab tick every minute
    [Timer]
    OnCalendar=*:0/1
    Persistent=true
    [Install]
    WantedBy=timers.target
    EOF
    sudo systemctl daemon-reload
    sudo systemctl enable --now lab-tick.timer
    Created symlink '/etc/systemd/system/timers.target.wants/lab-tick.timer' → '/etc/systemd/system/lab-tick.timer'.

    enable --now 建的是 timers.target.wants/ 下的符号链接——这就是”开机自启”的实现方式,不是写进某个配置文件。

    看下一次什么时候跑:

    Terminal window
    systemctl list-timers lab-tick.timer --no-pager
    NEXT LEFT LAST PASSED UNIT ACTIVATES
    Tue 2026-10-27 17:43:00 UTC 29s - - lab-tick.timer lab-tick.service

    ACTIVATES 那一列就是它要拉起的 service。

    Persistent=true 的含义:关机期间错过的执行,开机后补跑一次。cron 的 @reboot 做不到这一点。

    写法含义
    *:0/1每分钟
    hourly / daily / weekly固定周期
    Mon..Fri 02:00工作日凌晨 2 点
    *-*-* 03:30:00每天 3:30
    OnUnitActiveSec=5min距上次运行 5 分钟(相对)

    验证表达式对不对,不用等它跑:

    Terminal window
    systemd-analyze calendar "*:0/1"

    故意让一个服务失败:

    ● lab-fail.service - Lab failing service
    Loaded: loaded (/etc/systemd/system/lab-fail.service; static)
    Active: failed (Result: exit-code) since Tue 2026-10-27 17:43:03 UTC
    Process: 24880 ExecStart=/bin/false (code=exited, status=1/FAILURE)
    Main PID: 24880 (code=exited, status=1/FAILURE)
    Mem peak: 1.2M

    Process: 那两行是排查的起点——它给出了退出码,而不是笼统的”启动失败”。

    排查顺序:

    Terminal window
    systemctl list-units --failed # 1) 这次启动有哪些失败
    systemctl status <unit> --no-pager # 2) 退出码与退出原因
    journalctl -u <unit> -n 50 --no-pager # 3) 具体日志
    journalctl -xeu <unit> # 4) 带解释的日志(-x 加说明)
    UNIT LOAD ACTIVE SUB DESCRIPTION
    ● dnf-makecache.service loaded failed failed dnf5 makecache
    ● lab-fail.service loaded failed failed Lab failing service

    不要直接编辑 /usr/lib 下的文件。用:

    Terminal window
    sudo systemctl edit <unit> # 生成 /etc/systemd/system/<unit>.d/override.conf
    sudo systemctl daemon-reload

    edit 打开的是空文件,你只写要覆盖/追加的部分:

    [Service]
    Environment=HTTP_PROXY=http://proxy.internal:3128
    TimeoutStartSec=180

    这样升级时发行版的改动不会丢,你的覆盖也不会被冲掉。

    GNOME 终端里实测:systemctl --failed 无失败单元;systemd-analyze 显示 kernel 1.773s + initrd 15.691s + userspace 33.379s;blame 前五名是 dev-ttyS3.device 等设备等待

    Terminal window
    systemd-analyze
    Startup finished in 1.502s (kernel) + 2.497s (initrd) + 7.888s (userspace) = 11.888s

    三段分别对应:内核、initrd、用户态。用户态占大头就该看 blame:

    Terminal window
    systemd-analyze blame | head -3
    11.713s unbound-anchor.service
    3.798s sys-module-fuse.device
    3.763s dev-vport1p1.device

    unbound-anchor.service 排第一,正是这台机器DNS 解析一直失败的那个服务——它卡在等上游根密钥更新。这种”开机慢”和”网络怪”其实是同一个根因。

    目的命令
    看状态systemctl status <unit> --no-pager
    启动 / 停止systemctl start / stop
    开机自启systemctl enable --now <unit>
    取消自启systemctl disable --now <unit>
    彻底屏蔽systemctl mask <unit>
    列出失败systemctl list-units --failed
    看单元原文systemctl cat <unit>
    看最终生效值systemctl show <unit> -p <属性>
    重载配置systemctl daemon-reload
    定时器systemctl list-timers --all