工作负载资源:Deployment、StatefulSet、DaemonSet、Job与CronJob
说实话,Kubernetes 里最核心的概念之一,就是工作负载资源。你想想看,PaaS 平台说白了就是帮用户管应用的,而应用在 K8s 里跑,总得有个载体吧?
我个人习惯把工作负载资源分成两类:一类是跑长期服务的,比如 Web 后端、API 网关;另一类是跑短任务的,比如数据清洗、定时备份。今天咱们就把这五种资源掰开揉碎讲清楚。
Deployment:最常用的无状态部署
Deployment 是我用得最多的资源,没有之一。它管理的是无状态应用——说白了,每个 Pod 都是一样的,谁挂了直接换一个,数据不存本地。
- 支持滚动更新和回滚
- 支持声明式扩缩容
- Pod 名字随机,不保证稳定网络标识
我在项目中遇到过这样一个场景:一个微服务需要从 3 个副本扩到 10 个,应对双十一流量。用 Deployment 就一行命令的事:kubectl scale deployment my-app --replicas=10。但要注意,如果应用本身有状态(比如依赖本地磁盘),那 Deployment 就不合适了。
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.21
ports:
- containerPort: 80
strategy.rollingUpdate.maxSurge 和 maxUnavailable,默认值 25% 有时候会引发问题。我曾经遇到过更新时 Pod 全挂了,就是因为没配这两个参数。
StatefulSet:有状态应用的救星
StatefulSet 是专门为有状态应用设计的。比如数据库、消息队列、缓存集群——这些家伙每个实例都有自己的身份,不能随便替换。
为什么需要它?你想想看,MySQL 主从复制,主库和从库能一样吗?当然不能。StatefulSet 给每个 Pod 分配了稳定的网络标识和持久化存储。
| 特性 | Deployment | StatefulSet |
|---|---|---|
| Pod 名称 | 随机后缀 | 有序编号(如 mysql-0, mysql-1) |
| 存储 | 共享卷或临时卷 | 每个 Pod 独立 PVC |
| 启停顺序 | 无序 | 有序(从 N 到 0 停止,从 0 到 N 启动) |
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql
spec:
serviceName: mysql-headless
replicas: 3
selector:
matchLabels:
app: mysql
template:
metadata:
labels:
app: mysql
spec:
containers:
- name: mysql
image: mysql:8.0
volumeMounts:
- name: data
mountPath: /var/lib/mysql
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 10Gi
DaemonSet:每个节点跑一个
DaemonSet 的用途很明确:确保每个 Node 上都运行一个 Pod 副本。典型的场景包括日志采集(Fluentd)、监控代理(Prometheus Node Exporter)、网络插件(Calico)等。
说白了,DaemonSet 就是「节点级守护进程」。你加一个新节点,它自动在上面跑一个 Pod;你删一个节点,Pod 也跟着消失。
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: fluentd
spec:
selector:
matchLabels:
name: fluentd
template:
metadata:
labels:
name: fluentd
spec:
containers:
- name: fluentd
image: fluent/fluentd:v1.14
volumeMounts:
- name: varlog
mountPath: /var/log
volumes:
- name: varlog
hostPath:
path: /var/log
nodeSelector 或 affinity。比如我只想在 GPU 节点上跑推理服务,就可以打上 gpu=true 的标签。
Job:跑一次就结束
Job 是用来执行一次性任务的。比如数据迁移、批量处理、模型训练。任务跑完,Pod 就进入 Completed 状态,不会重启。
嗯,这里要注意:Job 有两种模式——非并行和并行。非并行就是只跑一个 Pod,跑完结束;并行可以指定 completions 和 parallelism,比如我要处理 100 个文件,同时开 5 个 Worker。
apiVersion: batch/v1
kind: Job
metadata:
name: data-migration
spec:
template:
spec:
containers:
- name: migration
image: myapp/migration-tool:latest
command: ["python", "migrate.py"]
restartPolicy: Never
backoffLimit: 4
activeDeadlineSeconds,超时自动终止。
CronJob:定时跑 Job
CronJob 就是带定时器的 Job。它根据 Cron 表达式来调度 Job 的执行。常见的用途包括:定时备份数据库、清理过期日志、生成日报等。
说白了,CronJob = Cron + Job。你只需要写一个 Cron 表达式,K8s 就会在指定时间帮你创建 Job。
apiVersion: batch/v1
kind: CronJob
metadata:
name: daily-backup
spec:
schedule: "0 2 * * *"
jobTemplate:
spec:
template:
spec:
containers:
- name: backup
image: myapp/backup-tool:latest
command: ["sh", "-c", "pg_dumpall > /backup/db.sql"]
restartPolicy: OnFailure
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 1
schedule:Cron 表达式,支持 5 位(分 时 日 月 周)startingDeadlineSeconds:如果 Job 没按时启动,最多等多久concurrencyPolicy:是否允许并发执行(Allow/Forbid/Replace)successfulJobsHistoryLimit:保留多少个成功的 Job 记录
concurrencyPolicy: Forbid。为什么?因为如果备份任务上次还没跑完,下次又开始了,两个备份同时写同一个文件,后果你懂的。
如何选择?一张图说清楚
最后,我总结一下这五种资源的选择逻辑:
- 无状态服务(Web、API、微服务)→ Deployment
- 有状态服务(数据库、缓存、消息队列)→ StatefulSet
- 节点级守护(日志、监控、网络)→ DaemonSet
- 一次性任务(迁移、批处理)→ Job
- 定时任务(备份、清理、报表)→ CronJob
其实,大部分 PaaS 平台的核心就是把这些资源封装成「应用模板」。用户只需要填几个参数,平台自动生成对应的 YAML。你想想看,如果用户每次都要手写这些配置,那还叫什么 PaaS?
好了,这五种工作负载资源就讲到这里。下一章咱们聊聊服务发现和负载均衡——Service 和 Ingress 的那些事。