控制 systemd 服务并编写单元文件
使用 systemctl 管理服务,并为脚本化守护进程编写自定义单元和计时器文件。
控制 systemd 服务并编写单元文件 是 CoddyKit 上的免费 DevOps Bootcamp 课时。 这是第 2 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 DevOps Bootcamp 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 DevOps Bootcamp 课程共包含 4 节课。
systemd 和 systemctl 简介
systemd 是大多数现代 Linux 发行版使用的 init 系统和服务管理器。它负责启动、停止和管理系统服务,同时处理启动序列和系统状态。
与 systemd 交互的主要工具是 systemctl。您可以使用它:
- 启动和停止服务
- 启用或禁用服务在启动时运行
- 查看服务状态和日志
- 重新加载配置而无需重启
由 systemd 管理的所有服务都由单元文件定义。这些声明式配置文件存储在 /etc/systemd/system/(系统范围)或 ~/.config/systemd/user/(按用户划分)下。
常用的 systemctl 命令
以下是日常服务管理中最常用的 systemctl 命令。每条命令都针对一个单元名称操作,例如 nginx.service 或直接使用 nginx。
systemctl start <unit>— 立即启动服务systemctl stop <unit>— 停止正在运行的服务systemctl restart <unit>— 先停止再启动(完全重启)systemctl reload <unit>— 发送 SIGHUP 以重新加载配置,但不停止服务systemctl enable <unit>— 创建符号链接,使单元在启动时自动运行systemctl disable <unit>— 删除这些符号链接systemctl status <unit>— 显示状态、PID 和最近的日志行
#!/usr/bin/env bash
# Quick reference: inspect and control an nginx service
# (Requires nginx to be installed; run as root or with sudo)
systemctl status nginx
systemctl start nginx
systemctl enable nginx
systemctl reload nginx
systemctl restart nginx
systemctl stop nginx
systemctl disable nginx详细检查服务状态
systemctl status 会提供单元的详细快照。理解其输出对于快速诊断问题至关重要。
- 已加载:单元文件的路径,以及该单元是否已设置为在启动时启用
- 活动:当前状态 —
active (running)、inactive (dead)、failed - 主 PID:服务主进程的进程标识符
- 控制组:属于该服务的所有子进程
- 日志行:该单元最近的日志记录
您也可以使用 systemctl is-active 只查询活动状态,或使用 systemctl is-enabled 只查询启用状态。这两个命令非常适合在脚本中使用。
#!/usr/bin/env bash
# Script-friendly service health check
SERVICE="sshd"
if systemctl is-active --quiet "$SERVICE"; then
echo "$SERVICE is running."
else
echo "$SERVICE is NOT running. Attempting restart..."
systemctl restart "$SERVICE"
fi
if systemctl is-enabled --quiet "$SERVICE"; then
echo "$SERVICE is enabled at boot."
else
echo "WARNING: $SERVICE is not enabled at boot."
fi列出并筛选单元
管理大量服务时,您需要高效列出和筛选单元的方法。systemctl list-units 会显示当前已加载的所有单元,而 list-unit-files 会显示所有已安装的单元文件及其启用状态。
实用的筛选选项:
--type=service— 仅显示服务单元--state=failed— 仅显示失败的单元--state=active— 仅显示活动的单元--all— 在列表中包含非活动单元
在维护脚本中将这些筛选条件与 grep 结合使用,可以构建功能强大的检查管道。
#!/usr/bin/env bash
# List all failed services and report them
echo "=== Failed Services ==="
systemctl list-units --type=service --state=failed --no-legend
echo ""
echo "=== Services Enabled at Boot ==="
systemctl list-unit-files --type=service --state=enabled --no-legend | awk '{print $1}'systemd 服务单元文件的结构
服务单元文件是一种纯文本的 INI 风格文件,由多个部分组成。每个部分负责控制单元生命周期的不同方面。
- [Unit] — 元数据:描述信息和排序依赖项(
After=、Requires=、Wants=) - [Service] — 如何启动或停止进程:
ExecStart、ExecStop、Restart、User、WorkingDirectory - [Install] — 何时启用此单元:
WantedBy=multi-user.target表示它会在正常的多用户启动过程中激活
将单元文件放置到或编辑 /etc/systemd/system/ 下的单元文件后,您必须运行 systemctl daemon-reload,以便 systemd 读取这些更改。
编写您的第一个服务单元文件
让我们为自定义的 Python HTTP 服务器脚本创建一个简单的服务单元文件。该单元文件会准确告诉 systemd 如何管理此进程,就像管理其他系统服务一样。
这里使用的关键 [Service] 指令:
Type=simple— systemd 将ExecStart启动的进程视为主进程User=www-data— 使用非 root 账户运行,以提高安全性WorkingDirectory— 设置进程的工作目录Restart=on-failure— 如果进程以非零代码退出,则自动重启RestartSec=5— 每次重启尝试之间等待 5 秒
#!/usr/bin/env bash
# Create a custom service unit file for a simple HTTP server
# Run as root
cat > /etc/systemd/system/mywebserver.service << 'EOF'
[Unit]
Description=My Custom Python Web Server
After=network.target
[Service]
Type=simple
User=www-data
WorkingDirectory=/var/www/mysite
ExecStart=/usr/bin/python3 -m http.server 8080
Restart=on-failure
RestartSec=5
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable --now mywebserver.service
systemctl status mywebserver.service服务类型与进程生命周期
[Service] 中的 Type= 指令会告诉 systemd 如何判断服务何时完成启动。选择错误的类型是排序错误的常见原因。
Type=simple— 默认类型;ExecStart启动后,systemd 就认为服务已启动。不需要就绪信号。Type=forking—ExecStart进程创建子进程后退出;真正的守护进程作为子进程运行。请使用PIDFile=,以便 systemd 跟踪它。Type=notify— 进程在真正就绪时发送sd_notify(READY=1)。对于复杂的守护进程,这是最可靠的方式。Type=oneshot— 运行一次后退出;后续单元会等待其完成。非常适合设置脚本。Type=idle— 类似于 simple,但会延迟执行,直到所有活动任务完成。
#!/usr/bin/env bash
# Example: oneshot service for a database migration script
# /etc/systemd/system/db-migrate.service
cat > /etc/systemd/system/db-migrate.service << 'EOF'
[Unit]
Description=Run Database Migrations
After=postgresql.service
Requires=postgresql.service
[Service]
Type=oneshot
User=appuser
WorkingDirectory=/opt/myapp
ExecStart=/opt/myapp/scripts/migrate.sh
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl start db-migrate.service环境变量与安全强化
服务单元文件支持使用多个指令传递环境变量并应用安全限制 — 这两点对于生产系统都很重要。
环境指令:
Environment="KEY=value"— 在行内设置单个变量EnvironmentFile=/etc/myapp/env— 从文件加载变量(每行一个KEY=value)
常见的安全强化指令:
NoNewPrivileges=true— 防止进程通过 setuid 获取额外权限ProtectSystem=strict— 以只读方式挂载/usr、/boot和/etcPrivateTmp=true— 为服务提供独立隔离的/tmpCapabilityBoundingSet=— 删除所有 Linux 功能权限
#!/usr/bin/env bash
# Hardened service unit with environment file
cat > /etc/systemd/system/secureapp.service << 'EOF'
[Unit]
Description=Secure Application Daemon
After=network.target
[Service]
Type=simple
User=secureapp
Group=secureapp
WorkingDirectory=/opt/secureapp
EnvironmentFile=/etc/secureapp/env
ExecStart=/opt/secureapp/bin/server
Restart=on-failure
RestartSec=10
# Security hardening
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
PrivateTmp=true
CapabilityBoundingSet=
ReadWritePaths=/var/lib/secureapp /var/log/secureapp
[Install]
WantedBy=multi-user.target
EOF
# Create the environment file separately so secrets stay out of the unit file
install -m 600 -o secureapp /dev/null /etc/secureapp/env
echo 'APP_PORT=9000' >> /etc/secureapp/env
echo 'DB_URL=postgresql://localhost/mydb' >> /etc/secureapp/env
systemctl daemon-reloadsystemd 定时器单元简介
systemd 定时器是一种按计划激活另一个单元(通常是 .service)的单元。它用集成度更高、可记录日志且更易控制的机制取代了传统的 cron 任务。
每个定时器单元(foo.timer)都与一个基本名称相同的服务单元(foo.service)配对,由后者执行实际工作。您应启用并启动定时器,而不是直接启动服务。
定时器类型:
- 实时(日历)定时器 — 使用
OnCalendar=表达式在墙上时钟的指定时间触发,例如daily、weekly或Mon *-*-* 02:00:00 - 单调定时器 — 使用
OnBootSec=、OnActiveSec=或OnUnitActiveSec=,相对于系统事件触发
编写定时器单元文件
让我们构建一组完整的定时器与服务单元,使其每天 02:00 运行备份脚本。请注意,[Timer] 部分位于独立的 .timer 文件中,而实际工作由配对的 .service 文件负责。
关键的 [Timer] 指令:
OnCalendar=— 用于定义计划的日历表达式Persistent=true— 如果定时器应触发时系统处于关机状态,则在下次启动时立即运行RandomizedDelaySec=— 增加最多 N 秒的随机延迟,防止许多主机使用相同计划时出现惊群效应Unit=— 明确指定目标单元(名称匹配时可选)
#!/usr/bin/env bash
# Create a daily backup timer pair
# Run as root
# 1. The service that does the work
cat > /etc/systemd/system/daily-backup.service << 'EOF'
[Unit]
Description=Daily Database Backup
After=network.target postgresql.service
[Service]
Type=oneshot
User=backup
ExecStart=/usr/local/bin/backup-db.sh
StandardOutput=journal
StandardError=journal
EOF
# 2. The timer that schedules it
cat > /etc/systemd/system/daily-backup.timer << 'EOF'
[Unit]
Description=Run Daily Database Backup at 02:00
[Timer]
OnCalendar=*-*-* 02:00:00
Persistent=true
RandomizedDelaySec=300
[Install]
WantedBy=timers.target
EOF
systemctl daemon-reload
systemctl enable --now daily-backup.timer
# Verify
systemctl list-timers daily-backup.timer检查日志并排查单元问题
systemd 会将所有服务输出发送到日志系统,您可以使用 journalctl 查询这些内容。这样,所有单元的日志都会集中存储在一个可搜索的地方。
排查服务问题时常用的 journalctl 选项:
-u <unit>— 按单元名称筛选-f— 实时跟踪日志(类似于tail -f)-n 50— 仅显示最后 50 行--since "1 hour ago"— 按时间限制结果-p err— 仅显示错误级别及更高级别的记录-b— 仅显示本次启动期间的日志
当单元启动失败时,首先应运行 journalctl -u <unit> -n 30 --no-pager,然后运行 systemctl status <unit>,以获取最近的错误上下文。
#!/usr/bin/env bash
# Troubleshooting helper: dump unit status and recent logs
# Usage: ./service-debug.sh <unit-name>
UNIT="${1:-nginx.service}"
echo "=============================="
echo "STATUS: $UNIT"
echo "=============================="
systemctl status "$UNIT" --no-pager -l
echo ""
echo "=============================="
echo "RECENT LOGS (last 50 lines): $UNIT"
echo "=============================="
journalctl -u "$UNIT" -n 50 --no-pager
echo ""
echo "=============================="
echo "ERRORS ONLY (current boot)"
echo "=============================="
journalctl -u "$UNIT" -b -p err --no-pager知识检查:systemd 单元文件
测试您对 systemd 服务和定时器单元配置的理解。
课程回顾:控制 systemd 服务并编写单元文件
在本课中,您以专业水平学习了 systemd 服务管理和单元文件编写的核心概念。以下是本课内容总结:
- systemctl 基础 — 使用 start、stop、restart、reload、enable、disable、status、is-active 和 is-enabled,以便执行适合脚本的检查
- 列出与筛选 — 将
list-units和list-unit-files与--type、--state选项结合,筛选出失败或已启用的服务 - 单元文件结构 — 三个部分
[Unit]、[Service]和[Install]及其关键指令 - 服务类型 —
simple、forking、notify、oneshot,以及每种类型的适用场景 - 环境与安全 — 使用
EnvironmentFile、NoNewPrivileges、ProtectSystem和PrivateTmp强化守护进程 - 定时器单元 — 配对
.timer和.service文件,使用OnCalendar表达式,以及Persistent的补执行行为 - Journalctl — 按单元、时间、启动过程和优先级筛选日志,从而高效诊断故障
掌握这些技能后,您就能管理任何系统服务,可靠地安排重复任务,并为自己的守护进程编写适用于生产环境的单元文件。
常见问题解答
「控制 systemd 服务并编写单元文件」课时是免费的吗?
是的 — 「控制 systemd 服务并编写单元文件」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 DevOps Bootcamp 课程的其余内容,请升级到 CoddyKit PRO。 DevOps Bootcamp 课程共包含 4 节课。
「控制 systemd 服务并编写单元文件」这节课中我会学到什么?
使用 systemctl 管理服务,并为脚本化守护进程编写自定义单元和计时器文件。 你通过在浏览器中直接运行的动手代码来练习 DevOps Bootcamp,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 DevOps Bootcamp 需要有经验吗?
无需任何先前经验。CoddyKit 上的 DevOps Bootcamp 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 2 节课,共 4 节。
「控制 systemd 服务并编写单元文件」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 DevOps Bootcamp 课中编写并运行代码吗?
能。每节 DevOps Bootcamp 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。
此课程中的所有课时
- 自动化用户与用户组配置
- 控制 systemd 服务并编写单元文件
- 磁盘、文件系统与挂载自动化
- 构建系统健康检查与告警脚本