AI Infra 训练营
总览
  • Day 1 · 集群起步 + CNI
  • Day 2 · 控制面 + etcd
  • Day 3 · CRD + Operator + Webhook
  • Day 4 · 存储深度
  • Day 5 · 卷扩容 + 安全
  • Day 6 · 调度 + 可观测
  • Day 7 · Harbor + ArgoCD + Mesh
  • Day 8 · AI Infra
  • Day 9 · Triton + GPU
  • Day 10 · MIG + HPA + 量化
  • Day 11 · AI Agent 端到端
  • Day 12 · 灾备
  • Day 13 · Operator + 联邦 + Mesh + RAG
  • Day 14 · CKA / CKS + 总结
  • LLM 训练手册
  • RAG + Agent 手册
  • 推理优化手册
  • 上下文工程手册
  • Agent 开发手册
  • 面试深度复盘
  • 训练 v2 深度手册
  • 心智模型
  • 看懂命令输出
  • 容器网络底层
  • K8s 网络深入
  • DNS 全套
  • 故障排查方法论
  • 心智模型
  • 容器挂载完整指南
  • K8s Volumes 大全
  • PV/PVC/CSI 深入
  • NFS 深入
  • 分布式存储概览
  • 故障排查 runbook
HiHuo 主站
GitHub
总览
  • Day 1 · 集群起步 + CNI
  • Day 2 · 控制面 + etcd
  • Day 3 · CRD + Operator + Webhook
  • Day 4 · 存储深度
  • Day 5 · 卷扩容 + 安全
  • Day 6 · 调度 + 可观测
  • Day 7 · Harbor + ArgoCD + Mesh
  • Day 8 · AI Infra
  • Day 9 · Triton + GPU
  • Day 10 · MIG + HPA + 量化
  • Day 11 · AI Agent 端到端
  • Day 12 · 灾备
  • Day 13 · Operator + 联邦 + Mesh + RAG
  • Day 14 · CKA / CKS + 总结
  • LLM 训练手册
  • RAG + Agent 手册
  • 推理优化手册
  • 上下文工程手册
  • Agent 开发手册
  • 面试深度复盘
  • 训练 v2 深度手册
  • 心智模型
  • 看懂命令输出
  • 容器网络底层
  • K8s 网络深入
  • DNS 全套
  • 故障排查方法论
  • 心智模型
  • 容器挂载完整指南
  • K8s Volumes 大全
  • PV/PVC/CSI 深入
  • NFS 深入
  • 分布式存储概览
  • 故障排查 runbook
HiHuo 主站
GitHub

cron / crontab —— 定时任务

一句话定义

cron 是 Linux 上最经典的定时任务调度器——按时间表跑脚本。crontab 是它的配置工具。现代生产更推荐 systemd timer(更强、更可观察),但 cron 普及度第一、几乎任何系统都能用。

典型场景

  • 每天凌晨 2 点 etcd 备份:0 2 * * * /usr/local/bin/etcd-backup.sh
  • 每周清 docker 镜像:0 3 * * 0 docker system prune -af
  • 每分钟监控脚本上报
  • 不需要 K8s CronJob 的"宿主级"任务

K8s 里跑定时任务用 kind: CronJob(K8s 原生)。宿主机级别才用 cron / systemd timer。


配置位置

位置谁的怎么编辑
/var/spool/cron/<user>用户自己crontab -e
/etc/crontab系统级直接 vim
/etc/cron.d/*系统级(多文件)直接 vim
/etc/cron.{daily,weekly,monthly,hourly}/系统级(按周期)把脚本扔进去

推荐用 crontab -e(用户级)—— 不需 root、不污染系统配置。


crontab 基础

crontab -e                              # 编辑当前用户的 crontab
crontab -l                              # 列已配置
crontab -r                              # **删除所有 crontab**(小心,没二次确认)
crontab -u root -e                       # 编辑别人的(要 root)

crontab -e 第一次会让选 editor:

Select an editor:
  1. /usr/bin/vim.basic
  2. /usr/bin/nano

选完保存即生效(不需要 systemctl)。


5 字段时间格式

 ┌──── 分 (0-59)
 │  ┌── 时 (0-23)
 │  │  ┌──── 日 (1-31)
 │  │  │  ┌── 月 (1-12)
 │  │  │  │  ┌── 周 (0-7,0/7 都是周日)
 │  │  │  │  │
 │  │  │  │  │
 *  *  *  *  *  command

例子:

表达式含义
* * * * *每分钟
0 * * * *每小时整点
*/5 * * * *每 5 分钟
0 2 * * *每天凌晨 2 点
0 2 * * 0每周日凌晨 2 点
0 0 1 * *每月 1 号
30 9 * * 1-5工作日上午 9:30
0 */6 * * *每 6 小时
0 8,12,18 * * *每天 8/12/18 点
0 9-17 * * 1-5工作日每天 9 点到 17 点的每个整点

快捷别名

@reboot                  开机时跑一次
@yearly / @annually      每年(= 0 0 1 1 *)
@monthly                 每月(= 0 0 1 * *)
@weekly                  每周(= 0 0 * * 0)
@daily / @midnight        每天(= 0 0 * * *)
@hourly                   每小时(= 0 * * * *)
@daily /usr/local/bin/cleanup.sh
@reboot /usr/local/bin/init-task.sh

一条完整的 crontab 例子

# m h dom mon dow command
SHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
MAILTO=""

# 每天凌晨 2 点备份 etcd
0 2 * * * /usr/local/bin/etcd-backup.sh >> /var/log/etcd-backup.log 2>&1

# 每 5 分钟健康检查
*/5 * * * * /usr/local/bin/healthcheck.sh

# 每周日清理 docker
0 3 * * 0 docker system prune -af --volumes >> /var/log/docker-clean.log 2>&1

# 开机时设置 sysctl(备份手段)
@reboot sleep 30 && /usr/local/bin/init-sysctl.sh

关键写法:

  • >> file 2>&1 —— 重定向 stdout 和 stderr 到日志(强烈推荐)
  • SHELL=/bin/bash —— 默认 cron 用 /bin/sh,bash 特性用不了
  • PATH=... —— cron 的 PATH 很短,命令找不到就要写绝对路径或自定义 PATH
  • MAILTO="" —— 关掉 cron 自动邮件(默认会发,多数生产没配 mail)

cron 的几个坑爹默认

1. PATH 不完整

* * * * * kubectl get pods                # 失败:kubectl not found

cron 的 PATH 默认只有 /usr/bin:/bin。要么:

PATH=/usr/local/bin:/usr/bin:/bin

* * * * * kubectl get pods

或者用绝对路径:

* * * * * /usr/local/bin/kubectl get pods

2. 当前目录是 $HOME

不是你写 crontab 时的目录。脚本里需要 cd:

0 2 * * * cd /opt/myapp && ./backup.sh

3. 默认发邮件、没 mail 服务器导致日志炸

每次 cron 任务有 stdout/stderr 输出 → cron 发邮件到本地用户邮箱(/var/mail/<user>)。邮件文件越积越大。

修:

MAILTO=""                              # 关掉 cron 邮件

或者把任务输出重定向到日志文件:

0 * * * * /script.sh > /var/log/script.log 2>&1

4. % 在 crontab 里被替换

0 2 * * * mysql -e "INSERT VALUES(NOW(), '50%')"

cron 把 % 当 newline 处理 → 命令被截断。

% 必须转义 \%:

0 2 * * * mysql -e "INSERT VALUES(NOW(), '50\%')"

或者写脚本里、crontab 调脚本:

0 2 * * * /usr/local/bin/insert.sh

5. 时区是系统时区

0 2 * * * /backup.sh         # 2 点是 / etc/timezone 里的 2 点

K8s 集群推荐用 UTC(见 hostnamectl.md)。crontab 里写时间也用 UTC:

date                                  # 看现在的系统时间

调试 cron 任务(经典问题)

* * * * * 设置了但脚本没跑:

1. 看 cron 服务跑没

systemctl status cron                # Debian / Ubuntu
systemctl status crond                # CentOS / RHEL

2. 看 cron 日志

journalctl -u cron --since today      # systemd
# 或者老式
tail -f /var/log/cron
tail -f /var/log/syslog | grep CRON

输出格式:

May 27 02:00:01 m1 CRON[12345]: (root) CMD (/usr/local/bin/etcd-backup.sh)

看到了 = cron 跑了。看不到 = crontab 没生效。

3. 模拟 cron 环境

cron 任务和你 shell 跑的环境不同(PATH、shell、cwd)。模拟:

# 切到 cron 环境
env -i /bin/sh -c '/usr/local/bin/myscript.sh'

env -i 清空环境变量、模拟 cron。

4. 加日志

* * * * * /script.sh >> /tmp/cron.log 2>&1

>> 追加、2>&1 把 stderr 也合并。

5. 检查 crontab 写错

crontab -l                             # 看自己的
sudo crontab -u root -l                # 看 root 的
ls /etc/cron.d/                        # 看系统级

systemd timer —— 现代替代品

systemd 提供 timer 单元,功能更强:

写 service + timer

# /etc/systemd/system/etcd-backup.service
[Unit]
Description=Etcd backup

[Service]
Type=oneshot
ExecStart=/usr/local/bin/etcd-backup.sh
# /etc/systemd/system/etcd-backup.timer
[Unit]
Description=Run etcd backup daily

[Timer]
OnCalendar=daily                       # 每天 0 点
# OnCalendar=02:00                       # 每天 2 点
# OnCalendar=*-*-* 02:00:00              # 同上更明确
# OnCalendar=Mon..Fri 09:00              # 工作日 9 点
Persistent=true                         # 系统关机错过的任务、开机后补跑
RandomizedDelaySec=300                  # 加 0-300 秒随机延迟

[Install]
WantedBy=timers.target

启动:

systemctl daemon-reload
systemctl enable --now etcd-backup.timer

# 看 timer 状态
systemctl list-timers --all
systemctl status etcd-backup.timer
journalctl -u etcd-backup.service       # 看任务日志

cron vs systemd timer

cronsystemd timer
普及全 Linuxsystemd 发行版
日志自己 redirectjournalctl 自动收
错过任务补跑不会Persistent=true 支持
随机延迟不支持RandomizedDelaySec
依赖管理不支持Requires= After=
时间格式5 字段OnCalendar 更灵活
配置位置crontabunit 文件

新写定时任务推荐 systemd timer。已有 cron 不用强迁。


K8s CronJob —— 容器化的"cron"

K8s 集群内的定时任务:

apiVersion: batch/v1
kind: CronJob
metadata:
  name: hello
spec:
  schedule: "*/5 * * * *"
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: hello
            image: busybox
            command: ["sh", "-c", "echo Hello at $(date)"]
          restartPolicy: OnFailure
kubectl get cronjob
kubectl get jobs                       # 每次触发产生一个 job
kubectl logs job/hello-1234            # 看具体一次的日志

K8s CronJob 用 K8s 原生时间表(也是 5 字段)。集群内任务用 CronJob,不要 ssh 到节点用 cron。


实战例子

1. etcd 备份脚本

cat > /usr/local/bin/etcd-backup.sh <<'EOF'
#!/bin/bash
set -euo pipefail

DEST=/backup/etcd
TS=$(date +%F-%H%M)
mkdir -p "$DEST"

ETCDCTL_API=3 etcdctl snapshot save "$DEST/etcd-$TS.db" \
  --endpoints=https://127.0.0.1:2379 \
  --cacert=/etc/kubernetes/pki/etcd/ca.crt \
  --cert=/etc/kubernetes/pki/etcd/server.crt \
  --key=/etc/kubernetes/pki/etcd/server.key

# 保留 14 天
find "$DEST" -name 'etcd-*.db' -mtime +14 -delete
EOF

chmod +x /usr/local/bin/etcd-backup.sh

cron:

0 2 * * * /usr/local/bin/etcd-backup.sh >> /var/log/etcd-backup.log 2>&1

或 systemd timer(更推荐,见上面)。

2. 节点磁盘清理

0 3 * * 0 docker system prune -af --volumes >> /var/log/docker-clean.log 2>&1
0 4 * * 0 journalctl --vacuum-size=500M >> /var/log/journal-clean.log 2>&1

3. 监控自检

*/5 * * * * /usr/local/bin/health-probe.sh || /usr/local/bin/alert.sh

常见踩坑

坑 1:crontab -r 误删

crontab -r
# crontab: 都没了,没二次确认

没有二次提示!手快可能误删。

预防:定期备份

crontab -l > ~/crontab.bak

或者用 git 管 crontab 文件、crontab ~/.crontab 加载。

坑 2:脚本里 PATH 找不到命令

# script.sh
kubectl get pods                       # 直接 cron 跑找不到 kubectl
# script.sh 头部加
#!/bin/bash
export PATH=/usr/local/bin:/usr/bin:/bin
kubectl get pods

坑 3:脚本权限

0 2 * * * /home/user/script.sh
# script.sh 不可执行 → 不跑
chmod +x /home/user/script.sh

坑 4:日志没 2>&1,只看到 stdout

0 2 * * * /script.sh >> /var/log/script.log
# stderr 还是发邮件 / 默默丢
0 2 * * * /script.sh >> /var/log/script.log 2>&1

坑 5:daily 任务集中在凌晨同时段、I/O 爆炸

@daily 等价 0 0 * * *——所有 daily 任务都在 0 点 0 分跑。多个 daily 同一时间 → I/O 抖动。

# 分散执行时间
3 2 * * * task1
17 2 * * * task2
42 2 * * * task3

或者 systemd timer 用 RandomizedDelaySec=。

坑 6:cron 没启用

systemctl status cron
# inactive (dead)
systemctl enable --now cron

某些精简镜像 / 容器默认没装 / 没启 cron。

坑 7:crontab 文件最后没空行

某些 cron 实现要求最后必须空行 / 换行:

0 2 * * * /script.sh
                                       ← 最后一行可能要空行

crontab -e 通常自动处理、但手编辑 /etc/cron.d/foo 要注意。

坑 8:时间还没到 / 系统时间错

date
# 显示的时间是 2018 年 ... ?

先校时(见 chrony.md)。

坑 9:用户级 vs 系统级

crontab -e
# 编辑当前用户的
# 跑的时候是当前用户身份

sudo crontab -e
# 编辑 root 的

权限不一样。普通用户的 cron 跑不了需要 root 权限的命令。

坑 10:cron 已经触发但你看不到日志

journalctl -u cron --since today | grep my-script
# 空

可能:

  • crontab 写错(minute / hour 颠倒)
  • 任务跑了但 stdout / stderr 都没 redirect、被 cron 邮件吞了
ls /var/mail/<user>                    # 看是不是被邮件接收

关联命令

  • systemctl —— systemd timer 用 systemd
  • journalctl —— systemd timer 任务日志
  • at —— 一次性定时任务(cron 是周期、at 是单次)
  • hostnamectl —— 时区影响 cron 执行时间
  • anacron —— cron 的"补跑"版(笔记本 / 不 24x7 机器)
  • K8s CronJob —— 集群内定时任务
在 GitHub 上编辑此页