找回密码
立即注册
搜索
发回帖 发新帖

6449

积分

0

好友

809

主题
发表于 昨天 23:25 | 查看: 6| 回复: 0

数据库备份、日志清理、证书检查、健康探测——全是 crontab 里那一堆散落的定时任务。K8s CronJob 把它们搬进集群,自带重试、历史记录、并发控制。

crontab 里有 8 条定时任务:3 点备份数据库,4 点清理日志,5 点检查证书,6 点健康探测。分布在 4 台机器上。上周迁移一台机器,漏了备份任务,两天后才发现。数据差点没了。

Linux crontab 的问题很直接:任务跟机器绑定,机器没了任务就没了,也没有执行记录和失败告警。K8s CronJob 把定时任务变成集群资源——机器挂了自动调度到其他节点,执行失败自动重试,历史记录随时可查。

CronJob vs crontab

维度 Linux crontab K8s CronJob
位置 每台机器各自维护 集群级资源,集中管理
高可用 机器挂了任务就没了 自动调度到其他节点
重试 没有重试 失败自动重试
历史 只能看日志文件 保留最近 N 次执行记录
并发控制 不管 可限制并发执行
执行环境 用机器当前环境 每次在独立 Pod 中运行
资源限制 无 可限制 CPU/内存

CronJob 结构

下面是一个 MySQL 备份 CronJob 的完整定义:

apiVersion: batch/v1
kind: CronJob
metadata:
  name: mysql-backup
  namespace: database
spec:
  schedule: "0 3 * * *" # 每天 3 点执行
  concurrencyPolicy: Forbid # 禁止并发执行
  successfulJobsHistoryLimit: 3 # 保留 3 次成功记录
  failedJobsHistoryLimit: 5 # 保留 5 次失败记录
  jobTemplate:
    spec:
      backoffLimit: 2 # 失败重试 2 次
      activeDeadlineSeconds: 1800 # 超时 30 分钟自动终止
      template:
        spec:
          restartPolicy: OnFailure
          containers:
            - name: backup
              image: mysql:8.0
              env:
                - name: MYSQL_PASSWORD
                  valueFrom:
                    secretKeyRef:
                      name: mysql-secret
                      key: password
              command:
                - /bin/sh
                - -c
                - |
                  mysqldump -h mysql.database.svc -u root -p$MYSQL_PASSWORD \
                    --all-databases --single-transaction | \
                  gzip > /backup/mysql-$(date +%Y%m%d).sql.gz
              volumeMounts:
                - name: backup
                  mountPath: /backup
          volumes:
            - name: backup
              nfs:
                server: 192.168.1.105
                path: /volume1/backups/mysql

逐个参数解释:

schedule:Cron 表达式

schedule 使用标准五位 Cron 表达式,格式如下:

┌───────────── 分钟 (0-59)
│ ┌───────────── 小时 (0-23)
│ │ ┌───────────── 日 (1-31)
│ │ │ ┌───────────── 月 (1-12)
│ │ │ │ ┌───────────── 星期 (0-6, 0=周日)
│ │ │ │ │
* * * * *

常用示例:

schedule: "0 3 * * *"    # 每天 3:00
schedule: "0 3 * * 0"    # 每周日 3:00
schedule: "*/30 * * * *" # 每 30 分钟
schedule: "0 0 1 * *"    # 每月 1 号 0:00
schedule: "0 */6 * * *"  # 每 6 小时

concurrencyPolicy:并发策略

concurrencyPolicy: Forbid   # 上次还没跑完,跳过本次(推荐)
concurrencyPolicy: Replace  # 上次还没跑完,杀掉重新跑
concurrencyPolicy: Allow    # 允许并发执行(默认)

HomeLab 场景推荐 Forbid:防止备份任务因为卡住而堆积。

历史记录

successfulJobsHistoryLimit: 3 # 成功的保留 3 次
failedJobsHistoryLimit: 5     # 失败的保留 5 次

每次执行创建一个 Job + Pod,执行完后保留这些记录。可以用 kubectl get jobs 查看执行历史。

实战示例

示例 1:Redis 数据持久化备份

apiVersion: batch/v1
kind: CronJob
metadata:
  name: redis-backup
  namespace: cache
spec:
  schedule: "0 4 * * *"
  concurrencyPolicy: Forbid
  successfulJobsHistoryLimit: 7
  failedJobsHistoryLimit: 3
  jobTemplate:
    spec:
      backoffLimit: 1
      template:
        spec:
          restartPolicy: OnFailure
          containers:
            - name: redis-backup
              image: redis:7-alpine
              command:
                - /bin/sh
                - -c
                - |
                  redis-cli -h redis.cache.svc -a $REDIS_PASSWORD BGSAVE
                  sleep 10
                  cp /data/dump.rdb /backup/dump-$(date +%Y%m%d).rdb
                  # 保留最近 7 天
                  find /backup -name "dump-*.rdb" -mtime +7 -delete
              env:
                - name: REDIS_PASSWORD
                  valueFrom:
                    secretKeyRef:
                      name: redis-secret
                      key: password
              volumeMounts:
                - name: redis-data
                  mountPath: /data
                  readOnly: true
                - name: backup
                  mountPath: /backup
          volumes:
            - name: redis-data
              persistentVolumeClaim:
                claimName: redis-pvc
            - name: backup
              nfs:
                server: 192.168.1.105
                path: /volume1/backups/redis

示例 2:自动清理 Docker 镜像缓存

apiVersion: batch/v1
kind: CronJob
metadata:
  name: image-cleanup
  namespace: infra
spec:
  schedule: "0 5 * * 0" # 每周日凌晨 5 点
  jobTemplate:
    spec:
      template:
        spec:
          restartPolicy: OnFailure
          containers:
            - name: cleaner
              image: docker:24
              command:
                - sh
                - -c
                - |
                  # 清理未使用的镜像和构建缓存
                  docker image prune -a --filter "until=168h" --force
                  docker builder prune --force --keep-storage 5GB
                  echo "清理完成"
                  docker system df

示例 3:SSL 证书到期检查

apiVersion: batch/v1
kind: CronJob
metadata:
  name: cert-checker
  namespace: monitoring
spec:
  schedule: "0 8 * * *" # 每天早上 8 点检查
  jobTemplate:
    spec:
      template:
        spec:
          restartPolicy: OnFailure
          containers:
            - name: checker
              image: alpine:latest
              command:
                - /bin/sh
                - -c
                - |
                  DOMAINS="blog.homelab.local vault.homelab.local rss.homelab.local"
                  WARN_DAYS=7
                  for domain in $DOMAINS; do
                    expire_date=$(echo | openssl s_client -connect $domain:443 2>/dev/null \
                      | openssl x509 -noout -enddate 2>/dev/null \
                      | cut -d= -f2)
                    if [ -z "$expire_date" ]; then
                      echo "ERROR: $domain 无法获取证书信息"
                      continue
                    fi
                    expire_ts=$(date -d "$expire_date" +%s)
                    now_ts=$(date +%s)
                    days_left=$(( (expire_ts - now_ts) / 86400 ))
                    if [ $days_left -le $WARN_DAYS ]; then
                      echo "WARNING: $domain 证书将在 ${days_left} 天后到期!"
                    else
                      echo "OK: $domain 证书还有 ${days_left} 天到期"
                    fi
                  done

示例 4:K8s 集群健康检查

apiVersion: batch/v1
kind: CronJob
metadata:
  name: cluster-health-check
  namespace: kube-system
spec:
  schedule: "*/30 * * * *" # 每 30 分钟
  jobTemplate:
    spec:
      template:
        spec:
          restartPolicy: OnFailure
          serviceAccountName: health-checker
          containers:
            - name: kubectl
              image: bitnami/kubectl:latest
              command:
                - /bin/sh
                - -c
                - |
                  echo "=== 节点状态 ==="
                  kubectl get nodes -o wide

                  echo "=== 异常 Pod ==="
                  kubectl get pods --all-namespaces \
                    --field-selector=status.phase!=Running,status.phase!=Succeeded

                  echo "=== PVC 使用率 ==="
                  kubectl top pods --all-namespaces --sort-by=memory | head -20

                  echo "=== 检查完成: $(date) ==="

管理和调试

# 查看所有 CronJob
kubectl get cronjobs -A

# 查看 CronJob 详情
kubectl describe cronjob mysql-backup -n database

# 查看执行历史
kubectl get jobs -n database

# 查看某次执行的日志
kubectl logs job/mysql-backup-28472320 -n database

# 手动触发一次执行
kubectl create job --from=cronjob/mysql-backup manual-trigger -n database

# 临时暂停 CronJob
kubectl patch cronjob mysql-backup -n database -p '{"spec":{"suspend":true}}'

# 恢复
kubectl patch cronjob mysql-backup -n database -p '{"spec":{"suspend":false}}'

常见坑

坑 1:时区问题。 K8s 默认用 UTC,如果你在东八区,0 3 * * * 实际是北京时间 11 点。解决:

spec:
  schedule: "0 19 * * *" # UTC 19:00 = 北京时间 03:00
  # 或者在 CronJob 容器中设置 TZ

坑 2:Pod 一直卡在 Pending。 可能是资源不足或 PVC 未绑定。检查:

kubectl describe pod <pod-name> -n <namespace>

坑 3:任务执行时间超过间隔。 两次执行重叠,用 concurrencyPolicy: Forbid 防止堆积。

小结

把定时任务从 crontab 迁到 K8s CronJob,你获得了:集中管理、高可用、自动重试、执行历史、资源限制。这些在传统 crontab 下需要额外工具才能实现。

下一篇是本系列收官——把这些自动化工具串起来,搭建一个完整的运维巡检系统。




上一篇:自动化巡检系统实战:用 Python 串起 K8s、ArgoCD、GitLab 全链路
下一篇:GitLab CI/CD 实战:一个 .gitlab-ci.yml 实现 git push 自动部署 K8s
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-10-8 01:48 , Processed in 0.077361 second(s), 40 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表