IT策士 10余年一线大厂经验,专注 IT 思维、架构、职场进阶。我会在各个平台持续发布最新文章,助你少走弯路。
在前两篇中,我们用 Deployment 管理了 Flask 应用的副本,实现了滚动更新和自愈。Deployment 适合管理无状态、需要多副本、可随时替换的应用。但不是所有工作负载都长这样------有些需要在每个节点上运行,有些是一次性任务,有些是按周期自动执行的定时任务。
比如:你想在每个节点上部署一个日志采集器(节点级守护),你想跑一次数据库迁移脚本(一次性任务),你想每天凌晨 2 点自动备份 Redis 数据(定时任务)。Deployment 做这些事就像用牛刀杀鸡------不是不能,而是极其别扭。
今天我们来解锁三类专为这些场景设计的控制器:DaemonSet、Job、CronJob。它们与 Deployment 一起构成了 K8s 工作负载管理的完整拼图。
一、DaemonSet:每个节点一个 Pod
1.1 什么是 DaemonSet?
DaemonSet 确保每个节点上恰好运行一个 Pod 副本(或者匹配特定条件的节点上各运行一个)。节点加入集群时自动部署,节点移除时自动回收。
典型使用场景:
-
日志采集:每个节点上运行 Fluentd / Filebeat,采集所有容器的日志
-
监控代理:每个节点上运行 Prometheus Node Exporter,暴露节点指标
-
网络插件:每个节点上运行 Calico / Flannel 的代理组件
-
存储守护进程:每个节点上运行 Ceph / GlusterFS 客户端
1.2 实战:部署 Node Exporter 监控代理
以下 YAML 在每个节点上部署一个 Prometheus Node Exporter,采集节点的 CPU、内存、磁盘等指标。这也是我们贯穿案例从"可运行"走向"可观测"的第一步。
bash
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: node-exporter
labels:
app: node-exporter
spec:
selector:
matchLabels:
app: node-exporter
template:
metadata:
labels:
app: node-exporter
spec:
hostNetwork: true
hostPID: true
containers:
- name: node-exporter
image: prom/node-exporter:latest
args:
- --path.procfs=/host/proc
- --path.sysfs=/host/sys
- --path.rootfs=/host/root
ports:
- containerPort: 9100
volumeMounts:
- name: proc
mountPath: /host/proc
readOnly: true
- name: sys
mountPath: /host/sys
readOnly: true
- name: root
mountPath: /host/root
readOnly: true
volumes:
- name: proc
hostPath:
path: /proc
- name: sys
hostPath:
path: /sys
- name: root
hostPath:
path: /
关键配置说明:
-
kind: DaemonSet声明这是一个 DaemonSet 控制器 -
hostNetwork: true使 Pod 直接使用宿主机网络命名空间(Node Exporter 需要访问宿主机网络指标) -
hostPID: true使 Pod 能看到宿主机的进程列表 -
hostPath卷将宿主机的/proc、/sys、/挂载到容器内,让 Exporter 读取系统指标 -
无需
replicas字段------DaemonSet 自动根据节点数量决定副本数
部署并验证:
bash
kubectl apply -f node-exporter-daemonset.yaml
kubectl get daemonset
# NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
# node-exporter 1 1 1 1 1 <none> 30s
kubectl get pods -l app=node-exporter -o wide
# NAME READY STATUS NODE AGE
# node-exporter-abcde 1/1 Running minikube 30s
Minikube 是单节点集群,所以只运行 1 个 Pod。在多节点集群中,每个节点都会自动运行一个。
1.3 DaemonSet vs Deployment
二、Job:一次性任务
2.1 什么是 Job?
Job 确保指定数量的 Pod 成功完成 (退出码 0)。Job 创建的 Pod 在任务完成后不会自动重启(restartPolicy 通常设为 Never 或 OnFailure),而是保留在集群中以便查看日志和状态。
典型使用场景:
-
数据库迁移(Django
manage.py migrate) -
数据清洗与 ETL 任务
-
一次性报告生成
-
密钥轮换脚本
2.2 实战:Redis 数据备份 Job
为我们的贯穿案例创建一个定期执行的 Redis 备份任务(先看一次性 Job,再升级为 CronJob)。
bash
apiVersion: batch/v1
kind: Job
metadata:
name: redis-backup
spec:
ttlSecondsAfterFinished: 600
template:
spec:
restartPolicy: Never
containers:
- name: backup
image: redis:alpine
command:
- sh
- -c
- |
echo "开始备份,时间: $(date)"
redis-cli -h redis-service -p 6379 --rdb /backup/dump.rdb
if [ $? -eq 0 ]; then
echo "备份成功"
else
echo "备份失败"
exit 1
fi
volumeMounts:
- name: backup-storage
mountPath: /backup
volumes:
- name: backup-storage
emptyDir: {}
关键配置说明:
-
restartPolicy: Never:Job 的 Pod 退出后不重启。如果任务失败,由 Job Controller 决定是否创建新 Pod 重试 -
ttlSecondsAfterFinished: 600:Job 完成后 10 分钟自动清理(K8s v1.21+)。不设则需手动删除 -
command中的exit 1确保备份失败时 Pod 返回非零退出码,Job Controller 据此判断任务是否成功
部署并验证:
bash
kubectl apply -f redis-backup-job.yaml
kubectl get job
# NAME COMPLETIONS DURATION AGE
# redis-backup 1/1 3s 10s
kubectl get pods -l job-name=redis-backup
# NAME READY STATUS RESTARTS AGE
# redis-backup-xyz12 0/1 Completed 0 10s
Pod 的 STATUS=Completed 表示任务已成功完成。查看日志:
bash
kubectl logs job/redis-backup
# 开始备份,时间: Mon May 27 10:00:00 UTC 2025
# 备份成功
2.3 Job 的三种运行模式
2.4 backoffLimit 与失败重试
Job 默认会在 Pod 失败后重试,由 backoffLimit 控制最大重试次数(默认 6):
达到上限后 Job 标记为 Failed。对于幂等性任务(如数据备份),可设置较高重试次数;对于非幂等任务(如数据库迁移),建议设为 1 或 2,避免重复执行产生脏数据。
2.5 并行 Job 的工作队列模式
对于需要并行处理的大规模任务(如批量图片压缩、日志分析),可以配置并行 Job:
bash
spec:
completions: 50 # 总共需要完成 50 个 Pod
parallelism: 10 # 同时运行 10 个 Pod
这种模式下,Job Controller 会始终维持 10 个 Pod 并行运行,直至全部 50 个 Pod 成功完成。适合将大型任务拆分为独立子任务并行处理的场景。
三、CronJob:定时任务
3.1 什么是 CronJob?
CronJob 在指定的时间表上自动创建 Job,语法与 Linux crontab 完全一致。它是对 Job 的定时调度封装。
典型使用场景:
-
每日凌晨数据库备份
-
每小时日志轮转与归档
-
每周生成业务报表
-
定期证书续期检查
3.2 实战:每日凌晨 2 点备份 Redis
将前面的一次性 Job 升级为 CronJob:
bash
apiVersion: batch/v1
kind: CronJob
metadata:
name: redis-daily-backup
spec:
schedule: "0 2 * * *"
jobTemplate:
spec:
ttlSecondsAfterFinished: 86400
template:
spec:
restartPolicy: OnFailure
containers:
- name: backup
image: redis:alpine
command:
- sh
- -c
- |
echo "定时备份开始,时间: $(date)"
redis-cli -h redis-service -p 6379 --rdb /backup/dump.rdb
if [ $? -eq 0 ]; then
echo "备份成功"
else
echo "备份失败"
exit 1
fi
volumeMounts:
- name: backup-storage
mountPath: /backup
volumes:
- name: backup-storage
emptyDir: {}
关键配置说明:
-
schedule: "0 2 * * *"使用标准 crontab 语法------分、时、日、月、周,表示每天凌晨 2:00 执行 -
jobTemplate内嵌了 Job 的完整 spec,结构与 Job YAML 完全一致 -
restartPolicy: OnFailure比Never更适合定时任务------偶发性网络抖动导致的失败会被自动重试 -
ttlSecondsAfterFinished: 86400保留 24 小时后自动清理
部署并验证:
bash
kubectl apply -f redis-cronjob.yaml
kubectl get cronjob
# NAME SCHEDULE SUSPEND ACTIVE LAST SCHEDULE AGE
# redis-daily-backup 0 2 * * * False 0 <none> 10s
3.3 手动触发 CronJob
从 K8s v1.27 起,可以直接从 CronJob 创建一个一次性 Job:
bash
kubectl create job manual-backup --from=cronjob/redis-daily-backup
kubectl get pods -l job-name=manual-backup
这个命令会基于 CronJob 的 jobTemplate 立即创建一个 Job,用于测试定时任务的正确性。
3.4 CronJob 常见参数
三个重要参数的深入说明:
-
startingDeadlineSeconds:当控制平面因故障恢复后,发现某个 CronJob 应该被执行了 5 次但只执行了 3 次。如果设了此参数,超过截止时间的任务会被跳过,避免积压的 Job 同时爆发占用集群资源。 -
concurrencyPolicy:Allow允许同一 CronJob 的多个 Job 并行执行(需保证任务是幂等的);Forbid如果上一个 Job 未结束则跳过本次调度;Replace取消正在运行的 Job 并启动新的。 -
suspend: true可以在不删除 CronJob 对象的情况下暂停调度,适合维护窗口期间临时禁制定时任务。
四、控制器选择指南
至此,我们已经学过了 5 类控制器:Deployment、StatefulSet(将在第 27 篇之后展开)、DaemonSet、Job、CronJob。如何根据业务需求选择合适的控制器?
与 Docker Compose 的对比 :Compose 没有针对不同工作负载提供不同的管理对象------所有服务都用 services 定义,定时任务需要依赖外部 cron 或脚本触发。K8s 的控制器体系将工作负载按特征分类,每种控制器针对特定场景优化了生命周期管理和调度策略。
五、命令速查表
六、本篇总结
-
DaemonSet:每个节点运行一个 Pod,适合日志采集、监控代理等节点级守护任务。
-
Job:一次性任务,运行到完成,支持并行和重试,适合数据库迁移、数据备份等场景。
-
CronJob:定时调度 Job,基于 crontab 表达式,适合定期备份、报表生成等周期性任务。
-
控制器选型:根据工作负载的特征选择合适的控制器------服务类用 Deployment,节点级用 DaemonSet,批处理用 Job/CronJob。
至此,K8s 的核心工作负载控制器你已经全部掌握。下一篇文章------第 28 篇:Service:为 Pod 提供稳定的访问入口,我们将解决一个关键问题:Deployment 管理着不断变化 IP 的 Pod,其他服务如何稳定地找到它们?答案就是 Service。
想了解更多还可以去各个平台搜索「IT策士」,一起升级 IT 思维 !