4. Kubernetes工作负载:Deployment、StatefulSet、DaemonSet、Job、CronJob
工作负载,说白了就是你在Kubernetes上跑的应用。我刚开始接触K8s时,总觉得这五个概念长得差不多,都是跑Pod嘛。但实际用下来,每个都有自己独特的脾气。选错了,后面运维能让你哭出来。
今天咱们一个一个捋清楚。我会结合我踩过的坑,告诉你什么时候该用哪个。
4.1 Deployment:最常用的无状态部署
Deployment是我用得最多的一个。它管理的是无状态应用——说白了就是每个Pod都一样,谁死了换谁上,不用管身份。
它的核心能力有三个:
- 声明式更新:你告诉它「我要3个副本」,它自己会搞定
- 滚动更新:逐个替换Pod,保证服务不中断
- 回滚:出问题了,一条命令回到上一个版本
来个最简单的例子:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deploy
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%,但生产环境我通常设成10%,避免流量波动太大。
Deployment适合什么场景?Web服务、API网关、微服务中的无状态模块。说白了,只要Pod不需要记住自己是谁,就用Deployment。
4.2 StatefulSet:有状态应用的救星
StatefulSet是我曾经最头疼的一个。为什么?因为它要解决一个根本问题:Pod是有身份的。
你想想看,数据库、消息队列、缓存集群——这些应用每个节点都有自己的角色。主节点挂了,新Pod必须知道自己是主;从节点挂了,新Pod得知道该从哪同步数据。
StatefulSet给了三个关键特性:
- 稳定的网络标识:Pod名字固定,比如
mysql-0、mysql-1 - 稳定的持久化存储:每个Pod绑定自己的PVC,重启后数据还在
- 有序的部署和伸缩:从0到N逐个启动,从N到0逐个删除
看个例子:
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql
spec:
serviceName: mysql
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
4.3 DaemonSet:每个节点一个
DaemonSet的逻辑很简单:集群里每个节点上跑一个Pod。新节点加入,自动创建;节点删除,自动清理。
我一般用它来部署基础设施组件:
- 日志采集(Fluentd、Filebeat)
- 监控代理(Prometheus Node Exporter)
- 网络插件(Calico、Flannel)
- 安全代理
代码示例:
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节点上跑AI推理的DaemonSet,就是这么干的。
4.4 Job:跑一次就结束
Job就是用来跑一次性任务的。Pod执行完就退出,Job会帮你确认任务是否成功完成。
我常用它来做:
- 数据库迁移
- 数据批处理
- 定时报表生成
- 初始化脚本
看个例子:
apiVersion: batch/v1
kind: Job
metadata:
name: db-migrate
spec:
template:
spec:
containers:
- name: migrate
image: myapp/migrate:1.0
command: ["python", "migrate.py"]
restartPolicy: Never
backoffLimit: 4
注意:restartPolicy 必须设为 Never 或 OnFailure。设成 Always 的话,Pod会一直重启,Job永远结束不了。我见过有人因为这个把集群资源吃光的。
4.5 CronJob:定时任务
CronJob就是在Job外面包了一层定时器。语法和Linux的crontab一样,但多了Kubernetes的特性。
我一般用它来做:
- 每天凌晨的数据备份
- 每小时的健康检查
- 每周的日志清理
代码示例:
apiVersion: batch/v1
kind: CronJob
metadata:
name: backup
spec:
schedule: "0 2 * * *"
jobTemplate:
spec:
template:
spec:
containers:
- name: backup
image: myapp/backup:1.0
command: ["sh", "-c", "pg_dumpall > /backup/db.sql"]
restartPolicy: OnFailure
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 1
successfulJobsHistoryLimit,半年后集群响应越来越慢,查了半天才发现是历史Job太多。
4.6 如何选择?一张表说清楚
| 工作负载类型 | 适用场景 | Pod身份 | 存储 | 更新策略 |
|---|---|---|---|---|
| Deployment | 无状态Web服务、API | 无身份 | 临时存储 | 滚动更新 |
| StatefulSet | 数据库、消息队列、缓存 | 有身份 | 持久化存储 | 有序更新 |
| DaemonSet | 日志、监控、网络插件 | 按节点分配 | 通常用宿主机路径 | 逐个更新 |
| Job | 一次性任务、批处理 | 临时 | 可选 | 不适用 |
| CronJob | 定时任务、定期备份 | 临时 | 可选 | 不适用 |
嗯,这五种工作负载基本覆盖了你在Kubernetes上跑应用的所有场景。我个人建议:刚开始用K8s时,先死磕Deployment和StatefulSet。这两个搞明白了,剩下的自然就通了。
下一章咱们聊聊服务发现和负载均衡——Service和Ingress。到时候我会讲讲我是怎么用Ingress做灰度发布的,那个坑也挺有意思。