Kubernetes 学习笔记(十九):Deployment 之外还有哪些工作负载
Han Lv5

前言

这个系列前面大多数示例都围绕 Deployment,因为它最适合长期运行的无状态服务。

但 Kubernetes 不只有 Deployment。不同类型的任务,需要不同的控制器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Deployment
长期运行的无状态服务

DaemonSet
每个节点运行一份

Job
执行一次并完成

CronJob
按时间周期执行

StatefulSet
有稳定身份和存储需求的有状态服务

我目前把它们理解成不同的“任务安排方式”。不是所有事情都适合用 Deployment 硬套。

Deployment:长期运行的无状态服务

Deployment 的特点前面已经写过:

1
2
3
4
保持副本数量
支持滚动发布
支持回滚
Pod 可以互相替换

适合:

  • Web API;
  • 前端服务;
  • 无状态 worker;
  • 多副本等价的服务。
1
2
3
4
5
6
Deployment
|
v
Pod A Pod B Pod C

任意 Pod 都可以处理同类请求

如果一个 Pod 被删除,Deployment 通过 ReplicaSet 补一个新的。对无状态服务来说,这很自然。

但如果应用需要固定名字、固定存储、固定顺序,Deployment 就不一定合适。

DaemonSet:每个节点跑一份

DaemonSet 确保符合条件的每个节点上都运行一个 Pod。

1
2
3
Node A -> Pod
Node B -> Pod
Node C -> Pod

它适合节点级组件,例如:

  • 日志采集 agent;
  • 监控 agent;
  • 网络插件;
  • 存储插件;
  • 节点安全扫描组件。

一个简单 DaemonSet:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: log-agent
spec:
selector:
matchLabels:
app: log-agent
template:
metadata:
labels:
app: log-agent
spec:
containers:
- name: log-agent
image: example/log-agent:1.0.0

当新节点加入集群:

1
2
3
4
Node D 加入
|
v
DaemonSet 自动在 Node D 创建 Pod

当节点被移除,对应 Pod 也会消失。

所以 DaemonSet 的核心不是“保持 N 个副本”,而是“每个目标节点一份”。

Job:跑完就结束

Job 用来执行一次性任务,目标是成功完成。

Deployment 追求的是:

1
一直运行

Job 追求的是:

1
成功跑完

适合:

  • 数据处理任务;
  • 一次性迁移;
  • 批量计算;
  • 手动触发的维护任务。

一个简单 Job:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
apiVersion: batch/v1
kind: Job
metadata:
name: data-migration
spec:
template:
spec:
restartPolicy: OnFailure
containers:
- name: migration
image: example/migration:1.0.0
command:
- sh
- -c
- ./migrate.sh

Job 的 Pod 正常结束后,状态会变成完成:

1
2
3
4
5
6
7
8
9
10
Job
|
v
Pod Running
|
v
Pod Succeeded
|
v
Job Complete

这和 Deployment 不同。Deployment 里的容器如果退出,通常会被重启,因为它被期望长期运行。

CronJob:按时间跑 Job

CronJob 用来按时间表创建 Job。

适合:

  • 定时备份;
  • 定时报表;
  • 定时清理;
  • 周期性同步任务。

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
apiVersion: batch/v1
kind: CronJob
metadata:
name: daily-report
spec:
schedule: "0 2 * * *"
jobTemplate:
spec:
template:
spec:
restartPolicy: OnFailure
containers:
- name: report
image: example/report:1.0.0
command:
- sh
- -c
- ./generate-report.sh

可以理解成:

1
2
3
4
5
6
7
8
CronJob
|
| 按 schedule
v
Job
|
v
Pod

schedule: "0 2 * * *" 表示每天 2 点执行。

CronJob 还有并发策略、成功失败历史保留等配置,真实使用时要关注:

1
2
3
上一次还没跑完,下一次来了怎么办?
失败任务保留多少?
时区怎么处理?

这里先抓住主线:CronJob 是定时创建 Job,不是自己一直跑业务进程。

StatefulSet:稳定身份和存储

StatefulSet 用来管理有状态应用。

它和 Deployment 最明显的区别是 Pod 身份稳定。

Deployment 管理的 Pod 名字像:

1
2
demo-api-7c9d9f-a
demo-api-7c9d9f-b

Pod 被替换后名字会变。

StatefulSet 管理的 Pod 名字像:

1
2
3
mysql-0
mysql-1
mysql-2

这些序号有稳定意义。

1
2
3
4
StatefulSet: mysql
├─ Pod mysql-0 -> PVC mysql-data-mysql-0
├─ Pod mysql-1 -> PVC mysql-data-mysql-1
└─ Pod mysql-2 -> PVC mysql-data-mysql-2

适合:

  • 数据库;
  • 有主从关系的服务;
  • 需要稳定网络身份的服务;
  • 每个副本有独立持久存储的服务。

一个简化 StatefulSet:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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.4
volumeMounts:
- name: data
mountPath: /var/lib/mysql
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi

这篇不深入数据库部署,只先建立边界:StatefulSet 比 Deployment 更适合表达“每个副本都有自己的身份和存储”。

Headless Service

StatefulSet 经常配合 Headless Service。

普通 Service 会提供一个稳定入口:

1
2
Service: mysql
-> 后端 Pod 负载均衡

Headless Service 不分配普通 ClusterIP:

1
2
3
4
5
6
7
8
9
10
apiVersion: v1
kind: Service
metadata:
name: mysql
spec:
clusterIP: None
selector:
app: mysql
ports:
- port: 3306

它可以让客户端发现 StatefulSet 中每个 Pod 的稳定 DNS 名称。

1
2
3
mysql-0.mysql.default.svc.cluster.local
mysql-1.mysql.default.svc.cluster.local
mysql-2.mysql.default.svc.cluster.local

这对有状态服务很有价值,因为有时客户端需要明确连接某个副本,而不是随机打到任意一个。

怎么选择工作负载对象

我现在会先问应用属于哪种类型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
长期运行、无状态、多副本等价
-> Deployment

每个节点都要运行一份
-> DaemonSet

执行一次,成功结束
-> Job

按时间周期执行
-> CronJob

需要稳定身份、稳定存储、有状态
-> StatefulSet

不要为了熟悉 Deployment 就把所有东西都写成 Deployment。

例如:

1
2
3
4
5
6
7
定时清理任务用 Deployment
-> 进程要一直睡眠等待时间
-> 不如 CronJob 清楚

数据库用普通 Deployment
-> Pod 身份和存储关系不稳定
-> 更应该评估 StatefulSet

对象选对,后面的 YAML 和运维逻辑会顺很多。

小结

这一篇整理下来,我对这些工作负载对象的理解可以概括成几句话:

  • Deployment 适合长期运行的无状态服务;
  • DaemonSet 适合每个节点运行一份的组件;
  • Job 适合一次性执行并完成的任务;
  • CronJob 适合按时间周期创建 Job;
  • StatefulSet 适合需要稳定身份和稳定存储的有状态服务;
  • StatefulSet Pod 名字带稳定序号;
  • StatefulSet 常配合 Headless Service 提供稳定 DNS;
  • 选择工作负载对象前,先判断任务的运行模式。

下一篇准备整理 Helm、CRD 和 Operator。前面看的都是 Kubernetes 自带对象,下一篇会看项目如何组织复杂 YAML,以及 Kubernetes 如何扩展出新的资源类型。

参考资料

 评论