Linux: 任务自动化——cron / systemd / journalctl

服务器上的一切操作都应该自动化——定时备份、日志清理、健康检查。Linux 提供了从简单(cron)到现代(systemd timer)的各种自动化方案。

📋 前置知识:需要先掌握以下内容

1. 你将学到


2. 一个 "再也不用凌晨起来跑脚本" 的故事

(1) 痛点:每天手动备份

小明的备份脚本每天需要手动执行。他设了一个闹钟——每天凌晨 2 点起来跑 ./backup.sh。坚持了一周他就崩溃了。

(2) cron 解放了他

Bob 教他用 crontab:

BASH
crontab -e
# 添加:
0 2 * * * /home/alice/scripts/backup.sh > /dev/null 2>&1

从此每天凌晨 2 点,系统自动执行备份。小明只需要偶尔检查日志。

(3) 收益:机器该做的事,让机器做

小明学会了 cron 后,一口气自动化了:日备份、周报告、日志清理、健康检查 ping。他每天花在运维上的时间从 2 小时降到了 10 分钟。


3. 知识点讲解

(1) crontab——定时任务

cron 语法: 分 时 日 月 周 命令

TEXT 📖 仅展示
 分  时  日  月  周   命令
(0-59)(0-23)(1-31)(1-12)(0-7)
                     (0=周日 7=周日)
TEXT 📖 仅展示
# 特殊的语法
@reboot     # 重启时执行
@daily      # 每天 0:00(同 0 0 * * *)
@hourly     # 每小时(同 0 * * * *)
@weekly     # 每周一 0:00
@monthly    # 每月 1 日 0:00

常见示例:

TEXT 📖 仅展示
# 查看当前用户的 cron
crontab -l

# 编辑 crontab
crontab -e

# 删除全部 cron
crontab -r

# 常用例子
0 2 * * * /scripts/backup.sh              # 每天 2:00
*/5 * * * * /scripts/health-check.sh      # 每 5 分钟
0 0 * * 0 /scripts/weekly-report.sh       # 每周日 0:00
0 0 1 * * /scripts/monthly-cleanup.sh     # 每月 1 日
0 9-17 * * 1-5 /scripts/business-hours.sh # 工作日 9-17 点每小时
⚠️ 注意crontab -r 会直接删除当前用户的所有 cron 任务,且没有确认提示!误删后只能靠备份恢复。建议先用 crontab -l 备份再删除,或养成使用 crontab -e 编辑而非 crontab -r 删除的习惯。

💡 提示:cron 执行环境与交互式 shell 不同,它不会加载 ~/.bashrc~/.profile 等配置文件,PATH 通常只有 /usr/bin:/bin。如果脚本依赖自定义环境变量,需在脚本开头手动 source 配置文件,或在 crontab 中使用命令的绝对路径。

(2) systemd service

BASH
# service 单元路径
/etc/systemd/system/myapp.service

# 基本结构
[Unit]
Description=My Web Application
After=network.target

[Service]
Type=simple
User=alice
WorkingDirectory=/opt/myapp
ExecStart=/usr/bin/node server.js
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target

# 使用
sudo systemctl daemon-reload     # 重载配置
sudo systemctl start myapp       # 启动
sudo systemctl enable myapp      # 开机启动
sudo systemctl status myapp      # 查看状态
sudo journalctl -u myapp -f      # 查看日志

(3) systemd timer

💡 提示:选择 systemd timer 还是 cron?如果你的定时任务需要依赖其他服务、需要日志记录、或者系统关机期间可能错过执行(需要补执行),选 systemd timer(设置 Persistent=true)。如果只是简单的周期性脚本且不需要上述特性,cron 更轻量直观。新项目推荐优先尝试 systemd timer。

BASH
# timer 单元(替代 cron)
/etc/systemd/system/backup.timer

[Unit]
Description=Daily Backup Timer

[Timer]
OnCalendar=daily
Persistent=true

[Install]
WantedBy=timers.target

# 对应的 service
/etc/systemd/system/backup.service

[Unit]
Description=Daily Backup Service

[Service]
Type=oneshot
ExecStart=/opt/scripts/backup.sh

# 使用
sudo systemctl enable --now backup.timer
sudo systemctl list-timers --all

OnCalendar 语法:

TEXT 📖 仅展示
OnCalendar=daily                    # 每天 0:00
OnCalendar=hourly                   # 每小时
OnCalendar=*-*-* 02:00:00           # 每天 2:00
OnCalendar=Mon *-*-* 03:00:00       # 每周一 3:00
OnCalendar=*-*-1 00:00:00           # 每月 1 日
OnCalendar=*:0/5                    # 每 5 分钟

(4) journalctl——日志查看

BASH
# 基本
journalctl                           # 所有日志
journalctl -u nginx                  # 特定服务
journalctl -u nginx -f               # 实时追踪
journalctl -u nginx --since "1 hour ago"
journalctl -u nginx --until "2026-07-07 10:00"
journalctl -p err                    # 只显示错误级别以上
journalctl -p err -u nginx           # nginx 的错误日志

# 管理
journalctl --disk-usage              # 查看日志占用
sudo journalctl --vacuum-size=500M   # 限制日志大小
sudo journalctl --vacuum-time=7d     # 保留 7 天

(5) logrotate——日志轮转

💡 提示:logrotate 本身由系统 cron 每天自动执行(脚本位于 /etc/cron.daily/logrotate),你只需在 /etc/logrotate.d/ 下编写配置即可。修改配置后,用 logrotate -d /etc/logrotate.d/你的配置 调试模式预览效果,确认无误再让它自动生效。

BASH
# 配置位置
/etc/logrotate.conf                  # 全局配置
/etc/logrotate.d/                    # 各应用配置

# 示例:Nginx 日志轮转
/var/log/nginx/*.log {
    daily
    rotate 7
    compress
    delaycompress
    missingok
    notifempty
    create 640 www-data adm
    postrotate
        [ -f /var/run/nginx.pid ] && kill -USR1 `cat /var/run/nginx.pid`
    endscript
}

常用指令:

指令 含义
daily/weekly/monthly 轮转频率
rotate N 保留 N 个归档
compress 压缩归档(gzip)
delaycompress 延迟一个周期再压缩
missingok 文件不存在时不报错
notifempty 空文件不轮转
size 100M 文件达到指定大小才轮转
maxage 30 超过 30 天的归档删除

▶ 示例:crontab 配置

TEXT 📖 仅展示
# 编辑 crontab
crontab -e

# 写入以下内容
# ┌───── 分钟 (0-59)
# │ ┌───── 小时 (0-23)
# │ │ ┌───── 日期 (1-31)
# │ │ │ ┌───── 月份 (1-12)
# │ │ │ │ ┌───── 星期 (0-7, 0=周日)
# * * * * * 命令

# 每天 2:00 自动备份
0 2 * * * /home/alice/scripts/backup.sh

# 每 5 分钟检查服务健康
*/5 * * * * /home/alice/scripts/health-check.sh

# 每周一 3:00 生成周报
0 3 * * 1 /home/alice/scripts/weekly-report.sh

# 每月 1 日清理归档
0 0 1 * * /home/alice/scripts/cleanup.sh

▶ 示例:创建 systemd service

BASH
# 创建一个 Node.js 应用的 systemd service
sudo tee /etc/systemd/system/webapp.service << 'EOF'
[Unit]
Description=Node.js Web Application
Documentation=https://example.com/docs
After=network.target

[Service]
Type=simple
User=alice
Group=alice
WorkingDirectory=/opt/webapp
Environment=NODE_ENV=production
Environment=PORT=3000
ExecStart=/usr/bin/node /opt/webapp/server.js
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-failure
RestartSec=10

# 安全选项
ProtectSystem=full
NoNewPrivileges=true
PrivateTmp=true

[Install]
WantedBy=multi-user.target
EOF

sudo systemctl daemon-reload
sudo systemctl enable --now webapp
sudo systemctl status webapp

输出:

TEXT 📖 仅展示
● webapp.service - Node.js Web Application
     Loaded: loaded (/etc/systemd/system/webapp.service; enabled)
     Active: active (running) since Tue 2026-07-07 10:23:45 UTC
   Main PID: 5678 (node)
      Tasks: 12 (limit: 4915)
     Memory: 45.2M
        CPU: 1.234s

▶ 示例:systemd timer 替代 cron

BASH
# 备份 service
sudo tee /etc/systemd/system/backup.service << 'EOF'
[Unit]
Description=Daily Backup

[Service]
Type=oneshot
ExecStart=/opt/scripts/backup.sh
User=alice
EOF

# 备份 timer
sudo tee /etc/systemd/system/backup.timer << 'EOF'
[Unit]
Description=Run backup daily at 2am
Requires=backup.service

[Timer]
OnCalendar=*-*-* 02:00:00
Persistent=true

[Install]
WantedBy=timers.target
EOF

sudo systemctl daemon-reload
sudo systemctl enable --now backup.timer
sudo systemctl list-timers --all

输出:

TEXT 📖 仅展示
NEXT                        LEFT          LAST                        PASSED
Tue 2026-07-07 02:00:00 UTC 15h left      Mon 2026-07-06 02:00:00 UTC 9h ago
n/a                         n/a           n/a                         n/a

1 timers listed.

▶ 示例:journalctl 日志查询

BASH
# 查看 nginx 最近 1 小时的错误
journalctl -u nginx -p err --since "1 hour ago"

# 查看昨天的全部日志
journalctl --since yesterday --until today

# 查看特定 PID 的日志
journalctl _PID=1234

# 实时追踪多个服务
journalctl -u nginx -u webapp -f

# 导出日志到文件
journalctl -u webapp --since "2026-07-06" > webapp-logs.txt

# 查看日志占用
journalctl --disk-usage
# 归档和当前日志占用 512.0M

# 清理
sudo journalctl --vacuum-size=200M

输出:

TEXT 📖 仅展示
Jul 07 10:23:45 server nginx[1234]: GET /api/users 200 1234
Jul 07 10:23:50 server nginx[1234]: POST /api/data 500 0
Jul 07 10:24:01 server webapp[5678]: Error: ECONNREFUSED
-- Logs begin at Mon 2026-07-06 00:00:01 --
Jul 07 09:00:00 server sshd[890]: Accepted publickey for alice
归档和当前日志占用 512.0M
Deleted archived journals: 23
Vacuuming done, freed 312.0M of archived journals

▶ 示例:logrotate 配置

BASH
# 为自定义应用配置日志轮转
sudo tee /etc/logrotate.d/custom-app << 'EOF'
/var/log/custom-app/*.log {
    daily
    rotate 14
    compress
    delaycompress
    missingok
    notifempty
    create 640 www-data www-data
    sharedscripts
    postrotate
        systemctl reload custom-app > /dev/null 2>&1 || true
    endscript
}
EOF

# 测试配置
sudo logrotate -d /etc/logrotate.d/custom-app

# 手动强制执行
sudo logrotate -f /etc/logrotate.d/custom-app

输出:

TEXT 📖 仅展示
reading config file /etc/logrotate.d/custom-app
Handling 1 logs

rotating pattern: /var/log/custom-app/*.log
rotating 14 kept versions
considering log /var/log/custom-app/app.log
  log needs rotating
renaming /var/log/custom-app/app.log to /var/log/custom-app/app.log.1
creating new /var/log/custom-app/app.log mode=0640 user=www-data group=www-data

▶ 综合示例:自动化运维体系

BASH
#!/bin/bash
# setup-automation.sh - 配置自动化运维体系

set -euo pipefail

echo "=== 搭建自动化运维体系 ==="

# 1. 创建脚本目录
mkdir -p /opt/scripts

# 2. 备份脚本
cat > /opt/scripts/backup.sh << 'BACKUPEOF'
#!/bin/bash
# 自动备份 - 每天凌晨 2 点由 cron 或 systemd timer 触发

SOURCE="/var/www"
DEST="/backup"
RETENTION=30

mkdir -p "$DEST"
BACKUP_FILE="${DEST}/backup_$(date +%Y%m%d).tar.gz"

tar -czf "$BACKUP_FILE" \
    --exclude=node_modules \
    --exclude=.git \
    "$SOURCE" 2>/dev/null

# 清理过期备份
find "$DEST" -name "backup_*.tar.gz" -mtime +$RETENTION -delete

# 日志
echo "[$(date)] 备份: $BACKUP_FILE ($(du -h "$BACKUP_FILE" | cut -f1))" >> /var/log/backup.log
BACKUPEOF
chmod +x /opt/scripts/backup.sh

# 3. 健康检查脚本
cat > /opt/scripts/health-check.sh << 'HEALTHEOF'
#!/bin/bash
# 健康检查 - 每 5 分钟执行

WEBHOOK_URL="https://hooks.slack.com/services/YOUR/WEBHOOK/URL"
SERVICES=("nginx" "postgresql" "webapp")

for svc in "${SERVICES[@]}"; do
    if ! systemctl is-active --quiet "$svc"; then
        MESSAGE="❌ $svc 服务异常 $(date)"
        echo "$MESSAGE" | mail -s "服务告警" admin@example.com
        # curl -X POST -d "text=$MESSAGE" "$WEBHOOK_URL"
    fi
done

# 磁盘检查
DISK_USAGE=$(df -h / | tail -1 | awk '{print $5}' | tr -d '%')
if [ "$DISK_USAGE" -gt 85 ]; then
    echo "⚠️ 磁盘使用率: ${DISK_USAGE}%" >> /var/log/disk-alert.log
fi
HEALTHEOF
chmod +x /opt/scripts/health-check.sh

# 4. 配置 cron
(crontab -l 2>/dev/null; echo "") | crontab -
(crontab -l 2>/dev/null; echo "# 自动备份 - 每天 2:00") | crontab -
(crontab -l 2>/dev/null; echo "0 2 * * * /opt/scripts/backup.sh > /dev/null 2>&1") | crontab -
(crontab -l 2>/dev/null; echo "# 健康检查 - 每 5 分钟") | crontab -
(crontab -l 2>/dev/null; echo "*/5 * * * * /opt/scripts/health-check.sh > /dev/null 2>&1") | crontab -
(crontab -l 2>/dev/null; echo "# 日志清理 - 每周日") | crontab -
(crontab -l 2>/dev/null; echo "0 0 * * 0 journalctl --vacuum-size=200M > /dev/null 2>&1") | crontab -

echo ""
echo "=== 自动化运维体系搭建完成 ==="
echo "已配置的任务:"
crontab -l | grep -v "^#"
echo ""
echo "手动验证: crontab -l"

❓ 常见问题

Q: crontab 的环境变量和终端一样吗? A: 不一样!cron 执行命令时的 PATH 非常精简(通常是 /usr/bin:/bin)。如果你的脚本依赖 ~/.bashrc 中定义的 PATH,要么在脚本开头设置 PATH,要么在 cron 命令中写绝对路径。

Q: systemd timer 比 cron 好在哪? A: 1)依赖管理:timer 可以依赖其他 service。2)日志集成:自动 journalctl 记录。3)错过执行:Persistent=true 在下次开机时补执行。4)更精确:支持毫秒级别。5)统一管理:systemctl list-timers

Q: journalctl 日志会占满硬盘吗? A: 默认日志最大为磁盘的 10%(但不超过 4GB)。可以用 journalctl --vacuum-size=500M 或配置文件 /etc/systemd/journald.conf 中的 SystemMaxUse=500M 限制。

Q: logrotate 是自动执行还是要配 cron? A: logrotate 由系统自带的 cron job 每天执行一次(在 /etc/cron.daily/logrotate)。你只需要在 /etc/logrotate.d/ 下配置规则,不需要额外配置 cron。

Q: 怎么调试 crontab 不执行的问题? A: 三步排查:1)检查 cron 服务是否在运行 systemctl status cron。2)查看 cron 日志 grep CRON /var/log/syslog。3)手动执行脚本确认没问题 bash /path/to/script.sh


📖 小节


📝 作业

  1. 基础题(难度⭐):配置 crontab 每小时执行一次 date/tmp/cron-test.log,然后用 journalctl 查看 nginx 或 sshd 的启动日志
  2. 进阶题(难度⭐⭐):创建一个简单的 hello.service 并启动(Type=simple),然后创建一个 logrotate 配置,对 /var/log/test-app/*.log 按天轮转,保留 7 天
  3. 挑战题(难度⭐⭐⭐):写一个 systemd timer,每天 3:00 清理 /tmp 中的过期文件,配合对应的 service 文件,并配置 journalctl 查看其执行日志
Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏