前言
自从公司没了运维,我就开始负责公司的运维工作了,日常运维工作很琐碎,比如每个月月末要做下个月云服务的预算,要进行费用的监测, 还有隔几天要看一下ssl证书有没有过期,要及时更新维护,我们使用的主要是阿里云服务,为此写了脚本来进行费用 监测估算以及 ssl
证书监测,然后发送钉钉通知。没接触运维工作前,并不熟悉 kubernetes
, 因此定时任务放在 本地,然后使用 crontab
来定时执行。现在接触了 kubernetes
后, 知道 kubernetes
有自己的定时任务机制,所以我就想把这些定时任务放到 kubernetes
中。
脚本功能
脚本当然不是本文的重点,简单来说,就是调用阿里提供的 SDK
查询云服务的费用以及即将到期的 ssl
证书, 然后发送钉钉通知。代码就不多说了,效果图如下:


接下来才是本文重点,如何将这个脚本运行在 kubernetes
中。
kubernetes 自己的定时任务机制
Kubernetes CronJob 是一种用于管理定时任务的资源对象,类似于 Linux 系统中的 cron
。它允许用户在指定的时间或周期性地运行 Job,从而执行批处理任务。
CronJob 会创建 Job ,而 Job 又会创建 Pod 来执行实际的任务。因此,CronJob、Job 和 Pod 的关系可以概括为:
CronJob → Job → Pod
CronJob 配置文件详解
一个典型的 CronJob 配置文件(YAML)如下:
yaml
apiVersion: batch/v1
kind: CronJob
metadata:
namespace: default
name: my-cronjob
spec:
schedule: "*/5 * * * *" # 每5分钟执行一次(cron表达式)
concurrencyPolicy: Forbid # 禁止并发执行
startingDeadlineSeconds: 60 # 任务启动的最长等待时间(秒)
successfulJobsHistoryLimit: 3 # 保留成功的Job记录数
failedJobsHistoryLimit: 1 # 保留失败的Job记录数
jobTemplate:
spec:
template:
spec:
containers:
- name: my-job
image: busybox
command: ["/bin/sh", "-c", "echo 'Hello from CronJob!' && date"]
restartPolicy: OnFailure # 失败时重启
关键字段解析
字段 | 说明 | 示例 |
---|---|---|
schedule |
Cron 表达式 ,定义任务执行时间 | "0 * * * *" (每小时执行) |
concurrencyPolicy |
并发策略 ,可选: • Allow (允许并发) • Forbid (禁止并发) • Replace (替换正在运行的 Job) |
Forbid |
startingDeadlineSeconds |
任务启动超时时间 ,超过时间未启动则视为失败 | 60 (60秒) |
successfulJobsHistoryLimit |
保留成功 Job 的历史记录数量 | 3 (保留3个) |
failedJobsHistoryLimit |
保留失败 Job 的历史记录数量 | 1 (保留1个) |
jobTemplate |
Job 模板 ,定义实际运行的 Job | 见示例 |
到这里先脑海里有这个概念,下面结合将监控脚本部署到 kubernetes
案例就会明白。
监控脚本部署到 kubernetes 案例
构建镜像
首先,我们需要将监控脚本打包成镜像。这里我使用的是 docker
来构建镜像,镜像的 Dockerfile
如下:
bash
FROM {你的镜像仓库地址}/python:3.11
WORKDIR /app
COPY . .
RUN chmod +x main.sh
RUN pip install -i https://mirrors.aliyun.com/pypi/simple/ -r requirements.txt
CMD ["./main.sh"]
这里我将 python:3.11 镜像上传到了我们的私有镜像仓库,这样拉取会快一些。
main.sh 脚本内容如下:
bash
#!/bin/bash
python3 main.py --subscription_type=PayAsYouGo
python3 ssl_main.py
一个是费用监控脚本,另一个是 ssl
证书监控脚本。
这个 Dockerfile
文件还是比较简单的,下面来构建镜像,执行如下命令:
ruby
docker build -t ${Image}:${ImageTag} -t ${Image}:latest .
构建完成后,将镜像推送到镜像仓库(我们使用的阿里镜像仓库),执行如下命令:
perl
docker push ${Image}:${ImageTag}
docker push ${Image}:latest
到这里镜像就构建完成了,下面来创建 CronJob 资源对象。
创建 CronJob 资源对象
我们创建一个 cronjob.yaml
文件,内容如下:
yaml
kind: CronJob
metadata:
namespace: kube-ops
name: serverguard-job
spec:
schedule: "*/2 * * * *" # 每2分钟执行一次
concurrencyPolicy: Forbid
jobTemplate:
spec:
template:
spec:
containers:
- name: serverguard
image: {你的镜像仓库地址}/server-guard:latest
imagePullPolicy: Always
restartPolicy: OnFailure
这是我写的第一版,接下来执行,看看有啥问题,执行如下命令
kubectl apply -f cronjob.yaml
执行完成后,查看 CronJob 资源对象,执行如下命令:
arduino
kubectl get cronjob -n kube-ops # cronjob 可以缩写为cj
可以看到如下输出:
sql
NAME SCHEDULE SUSPEND ACTIVE LAST SCHEDULE AGE
serverguard-job */2 * * * * False 0 <none> 13s
可以看到,CronJob 资源对象创建成功了,接下来查看 Job 资源对象,执行如下命令:
arduino
kubectl get job -n kube-ops
可以看到如下输出:
NAME COMPLETIONS DURATION AGE
serverguard-job-29243884 0/1 13s 13s
可以看到,Job 资源对象创建成功了,接下来查看 Pod 资源对象,执行如下命令:
arduino
kubectl get pod -n kube-ops
可以看到如下输出:
sql
NAME READY STATUS RESTARTS AGE
serverguard-job-29243884-6f6gl 1/1 Running 0 18s
** 等待任务执行完成 **,再次查看 Job 和 pod ,因为任务设置的每2分钟执行一次,在写文章时,又执行了一次
再次查看 Job 资源对象,可以看到如下输出:
NAME COMPLETIONS DURATION AGE
serverguard-job-29243884 1/1 48s 2m40s
serverguard-job-29243886 1/1 31s 40s
可以看到,COMPLETIONS 为 1/1 ,说明任务执行成功了。
再次查看 Pod 资源对象,可以看到如下输出:
NAME READY STATUS RESTARTS AGE
serverguard-job-29243884-6f6gl 0/1 Completed 0 2m30s
serverguard-job-29243886-srbdd 0/1 Completed 0 30s
可以看到,Pod 资源对象的状态从 Running 变为 Completed ,说明任务执行成功了。
到这里就算是成功部署到 kubernetes 集群了,但上面这样配置有一个问题,随着任务不断执行,job 和 pod资源对象会不断增加, 下面修改配置文件,添加如下配置:
yaml
spec:
successfulJobsHistoryLimit: 1 # 只保留最近1个成功的Job
failedJobsHistoryLimit: 1 # 只保留最近1个失败的Job
再次执行任务,查看 Job 和 Pod 资源对象,无论执行多少次任务, 可以看到,Job 资源对象的数量只有1个,Pod 资源对象的数量也只有1个。
到这里,我们就完成了监控脚本的部署到 kubernetes 集群,下面来总结一下。
CronJob 与 Job、Pod 的关系
(1)CronJob → Job
- CronJob 负责按照
schedule
创建 Job。 - 每次触发时,都会生成一个新的 Job(除非
concurrencyPolicy
限制)。
(2)Job → Pod
- Job 负责管理 Pod ,确保任务执行完成。
- 默认情况下,Job 会创建 1 个 Pod 来执行任务(可通过
parallelism
调整)。
(3)Pod 的生命周期
- Running → Completed:
常见问题与解决方案
(1)CronJob 每次执行都会创建新的 Pod,导致 Pod 越来越多?
默认情况下,CronJob 不会自动清理旧的 Job 和 Pod,可能导致集群资源浪费。解决方案:
方法 1:设置 successfulJobsHistoryLimit
和 failedJobsHistoryLimit
yaml
spec:
successfulJobsHistoryLimit: 1 # 只保留最近1个成功的Job
failedJobsHistoryLimit: 1 # 只保留最近1个失败的Job
Kubernetes 会自动清理超出限制的 Job 和 Pod。
方法 2:使用 TTL 控制器(K8s 1.12+)
yaml
spec:
ttlSecondsAfterFinished: 3600 # 任务完成后1小时自动删除
(2)CronJob 任务执行失败会怎样?
-
如果 Pod 失败,Job 会根据
restartPolicy
决定是否重启:OnFailure
:Pod 会重启(默认最多重试6次)。Never
:Pod 不会重启,直接标记为Failed
。
最后
将本地任务顺利迁移到 kubernetes 集群,对任务的管理和监控都有了很大的帮助, 同时也解决了本地任务的维护问题,减少了维护成本。
希望这篇博客能帮助你更好地理解 Kubernetes CronJob!🚀