理解什么是K8s控制器

什么是控制器

​ 例如生活中的空调,当您设置了温度,告诉空调遥控器您的期望温度为25度,但是此刻房间温度是28度,遥控器就会为您控制空调的制冷、风量等,确保当前的温度接近与您期望温度,在这里遥控器就充当着控制器的角色。

​ 对于K8s中的控制器来说,控制器通过监控集群的公共状态,并致力于将当前状态变为您的期望状态。

​ K8s中存在很多控制器,每个控制器管理集群状态的一个特定方面。最常见的一个控制器是使用一种类型的资源作为它的期望状态,控制器管理控制另外一个资源类型向它的期望状态演化。

​ 例如:您的应用以容器的形式在Pods中运行;但是,直接管理单个Pod的工作量会非常繁琐。当其中一个Pod失败了,您可能希望运行一个新的Pod来替换失败的Pod。K8s控制器会帮您自动完成这些操作。

用于管理工作负载的内置 API 包括

  • Deployment (也间接包括 ReplicaSet) 是在集群上运行应用的最常见方式。Deployment 适合在集群上管理无状态应用工作负载, 其中 Deployment 中的任何 Pod 都是可互换的,可以在需要时进行替换。 (Deployment 替代原来的 ReplicationController API)。
  • StatefulSet 允许你管理一个或多个运行相同应用代码、但具有不同身份标识的 Pod。 StatefulSet 与 Deployment 不同。Deployment 中的 Pod 预期是可互换的。 StatefulSet 最常见的用途是能够建立其 Pod 与其持久化存储之间的关联。 例如,你可以运行一个将每个 Pod 关联到 PersistentVolume 的 StatefulSet。如果该 StatefulSet 中的一个 Pod 失败了,Kubernetes 将创建一个新的 Pod, 并连接到相同的 PersistentVolume。
  • DaemonSet 定义了在特定节点上提供本地设施的 Pod, 例如允许该节点上的容器访问存储系统的驱动。当必须在合适的节点上运行某种驱动或其他节点级别的服务时, 你可以使用 DaemonSet。DaemonSet 中的每个 Pod 执行类似于经典 Unix / POSIX 服务器上的系统守护进程的角色。DaemonSet 可能对集群的操作至关重要, 例如作为插件让该节点访问集群网络, 也可能帮助你管理节点,或者提供增强正在运行的容器平台所需的、不太重要的设施。 你可以在集群的每个节点上运行 DaemonSets(及其 Pod),或者仅在某个子集上运行 (例如,只在安装了 GPU 的节点上安装 GPU 加速驱动)。
  • 你可以使用 Job 和/或 CronJob 定义一次性任务和定时任务。 Job 表示一次性任务,而每个 CronJob 可以根据排期表重复执行。

ReplicaSet控制器

​ ReplicaSet控制器确保在任何时候都有特定数量的Pod副本处于运行状态,也就是说通过ReplicaSet控制器创建的多个Pods都会被ReplicaSet控制器所控制,并保证这些Pods总是处于可用正常运行的状态。

​ 例如:您通过ReplicaSet控制器创建了一组Pod数量为3个,在运行过程中如果其中一个Pod宕机或者被删除了,那么ReplicaSet控制器就会自动创建一个Pod,以保证您期望的3个Pod始终在线。

何时使用ReplicaSet

​ ReplicaSet 确保任何时间都有指定数量的 Pod 副本在运行。 然而,Deployment 是一个更高级的概念,它管理 ReplicaSet,并向 Pod 提供声明式的更新以及许多其他有用的功能。 因此,我们建议使用 Deployment 而不是直接使用 ReplicaSet, 除非你需要自定义更新业务流程或根本不需要更新。

​ 这实际上意味着,你可能永远不需要操作 ReplicaSet 对象:而是使用 Deployment,并在 spec 部分定义你的应用。Deployment后续会相继介绍。

示例

YAML关键参数解析

  • spec.selector.matchLabels:标签选择器,用于ReplicaSet控制器选定哪些Pod归该资源进行控制,这是控制器重要的参数。
  • spec.replicas:副本数,用于设定您期望的Pod副本数,控制器将始终保持该组Pod数接近您期望的数值。
  • spec.template:该模块下面用于定义您的Pod以及容器的创建模板,于之前学习的创建Pod的YAML类似。
  • spec.template.metadata.labels:为Pod定义标签,与selector(标签选择器)相互配合,为Pod创建标签,标签相互匹配后,ReplicaSet控制器即可将该组Pod归为自己管理的Pod组。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
cat > replicaset.yaml << EOF
apiVersion: apps/v1
kind: ReplicaSet
metadata:
namespace: book-k8s
name: nginx-rs
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
protocol: TCP
EOF

​ 运行Yaml文件创建资源,可以使用以下命令

1
[root@k8s-master-01 controller]# kubectl create -f replicaset.yaml

​ 查看资源创建状态,可使用以下命令。

1
2
3
4
5
6
7
8
9
10
11
12
# 查询目前存在一个名为nginx-rs的ReplicaSet资源
# DESIRED期望副本数3,CURRENT当前副本数3,READY正在允许的副本数3
[root@k8s-master-01 controller]# kubectl get -f replicaset.yaml
NAME DESIRED CURRENT READY AGE
nginx-rs 3 3 3 20h
# 通过--show-labels显示资源标签,-l搜索具有app=nginx标签的资源
# 当前具有3个受ReplicaSet管理的Pod资源
[root@k8s-master-01 controller]# kubectl get pods -l app=nginx --show-labels
NAME READY STATUS RESTARTS AGE LABELS
nginx-rs-5p8k8 1/1 Running 1 (16m ago) 21h app=nginx
nginx-rs-9xx7b 1/1 Running 1 (16m ago) 21h app=nginx
nginx-rs-pjlth 1/1 Running 1 (16m ago) 21h app=nginx

​ 当您通过命名删除一个Pod,模拟某一个Pod掉线的情况时,秒删除,秒级速度为您重新拉起一个新的Pod,以保证您该组Pod副本数始终保持为3。

1
2
3
4
5
6
7
8
9
# 删除其中一个Pod
[root@k8s-master-01 controller]# kubectl delete pods nginx-rs-5p8k8
pod "nginx-rs-5p8k8" deleted
# ReplicaSet控制器马上新拉起一个名为nginx-rs-fmdqf的Pod,保证副本数处于3
[root@k8s-master-01 controller]# kubectl get pods -l app=nginx --show-labels
NAME READY STATUS RESTARTS AGE LABELS
nginx-rs-9xx7b 1/1 Running 1 (25m ago) 21h app=nginx
nginx-rs-fmdqf 1/1 Running 0 2s app=nginx
nginx-rs-pjlth 1/1 Running 1 (26m ago) 21h app=nginx

重要:由于ReplicaSet控制器是通过标签来进行管理和识别当前的副本数的,如果此刻您的副本数是满足的,您手动创建了一个具备选择器选择的相同标签的Pod,那么您手动创建的这个Pod马上就会被控制器删除掉,因为控制器会检测到这个Pod,发现副本数超了,控制器会把最后创建的给删掉。

如果您手动创建具备同标签的Pod时,刚好副本数当前是不满足的状态,那么你新创建的这个Pod会加入到ReplicaSet控制器进行管理,即便他里面的images不同。

所以我们在使用和管理K8s时,最好使用yaml进行统一创建和删除,同时前期也要对您部署的微服务做好统一的规划,避免冲突出现。

1
2
3
4
5
6
7
8
9
# 手动创建一个名为:centos,使用的镜像为:centos,并且同样具备app=nginx标签的pod
[root@k8s-master-01 controller]# kubectl run --image centos -l app=nginx centos
pod/centos created
# 再次查询发现新创建的Pod不存在,被控制器删掉了
[root@k8s-master-01 controller]# kubectl get pods -l app=nginx --show-labels
NAME READY STATUS RESTARTS AGE LABELS
nginx-rs-9xx7b 1/1 Running 1 (38m ago) 21h app=nginx
nginx-rs-fmdqf 1/1 Running 0 12m app=nginx
nginx-rs-pjlth 1/1 Running 1 (38m ago) 21h app=nginx

Deployments控制器

​ Deployment用于管理运行一个应用负载的一组Pod,通常适用于不保持状态的负载。

Deployment与ReplicaSet区别

​ Deployment是一个可以拥有ReplicaSet并使用声明式方式再服务器端完成对Pod滚动更新的对象。尽管ReplicaSet可以独立使用,但它的主要用途是作为Deployment的编排Pod创建、删除、更新和Pod 自动扩缩器的一种机制,因为有了Deployment使得它们整体更加的健硕。当您使用了Deployment时,您不需要关心如何管理它所创建的ReplicaSet,Deployment 拥有并管理其 ReplicaSet。 因此,建议你在需要 ReplicaSet 时使用 Deployment,并且管理整个架构时仅需对Deployment进行管理,再由Deployment去管理旗下的ReplicaSet或Pod。

示例

​ 创建Deployment的yaml文件,用于创建和管理该Deployment,重点Yaml参数解析如下:

  • spec.replicas:该Deployment所创建管理的ReplicaSet控制器的期望副本数。
  • spec.replicas.selector.matchLabels:用于创建Deployment以及它所创建的ReplicaSet的标签选择器,用于标识归其所管理的Pod资源。
  • spec.template:以下为所创建具体资源Pod的模板类。
  • spec.template.metadata.labels:用于该模板Pod创建时具有的标签,与标签选择器一致,标识这些具有同类标签的为一组资源,归该Deployment所管理。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-dlt
namespace: book-k8s
spec:
replicas: 3
selector:
matchLabels:
app: nginx-dlt
template:
metadata:
labels:
app: nginx-dlt
namespace: book-k8s
spec:
containers:
- name: nginx-dlt
image: nginx:1.14.2
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
protocol: TCP

​ 运行Yaml文件创建资源,可以使用以下命令:

1
2
[root@k8s-master-01 controller]# kubectl create -f deployment.yaml 
deployment.apps/nginx-dlt created

​ 查看Deployment资源创建状态,可使用以下命令。

1
2
3
[root@k8s-master-01 controller]# kubectl get deployments.apps nginx-dlt --show-labels 
NAME READY UP-TO-DATE AVAILABLE AGE LABELS
nginx-dlt 3/3 3 3 10m <none>

​ 根据标签查看Deployment创建并管理的ReplicaSet资源状态,可使用以下命令。

1
2
3
[root@k8s-master-01 controller]# kubectl get rs -l app=nginx-dlt --show-labels 
NAME DESIRED CURRENT READY AGE LABELS
nginx-dlt-7b5fbfc86b 3 3 3 13m app=nginx-dlt,pod-template-hash=7b5fbfc86b

​ 根据标签查看ReplicaSet资源所创建的Pod状态,可使用以下命令。

1
2
3
4
5
[root@k8s-master-01 controller]# kubectl get pod -l app=nginx-dlt --show-labels 
NAME READY STATUS RESTARTS AGE LABELS
nginx-dlt-7b5fbfc86b-89t7p 1/1 Running 0 13m app=nginx-dlt,pod-template-hash=7b5fbfc86b
nginx-dlt-7b5fbfc86b-ckl5k 1/1 Running 0 13m app=nginx-dlt,pod-template-hash=7b5fbfc86b
nginx-dlt-7b5fbfc86b-frkt8 1/1 Running 0 13m app=nginx-dlt,pod-template-hash=7b5fbfc86b

Deployment滚动更新

​ 根据上述示例,我们创建了一个具有nginx:1.14.2镜像的Pod服务,此刻我们希望将镜像更新为nginx:1.16.1版本,可以按照如下操作进行滚动更新。

  • kubectl:二进制命令
  • set:修改
  • image:镜像
  • deployments:修改的资源是deployments资源
  • nginx-dlt:deployments的资源名称
  • ginx-dlt:创建容器时的容器名,标识要修改哪个容器
  • =nginx:1.16.1:要更新的容器镜像版本
1
2
[root@k8s-master-01 controller]# kubectl set image deployments nginx-dlt nginx-dlt=nginx:1.16.1
deployment.apps/nginx-dlt image updated

​ 更新完成后,我们会发现我们的rs会多出一条,但是其中一个rs是之前我们初次创建deployment时生成的rs,当前该rs处于全员下线的状态,这就是我们前面所说的,滚动更新,他会新增一个rs,并仅关闭一定数量的Pod,最新版本的rs中有一个Pod启动了,旧的rs就会下线一个,依次滚动轮换,始终保持更新业务不间断。

1
2
3
4
5
[root@k8s-master-01 controller]# kubectl get rs -l app=nginx-dlt --show-labels 
NAME DESIRED CURRENT READY AGE LABELS
nginx-dlt-6dbffd95f4 0 0 0 10m app=nginx-dlt,pod-template-hash=6dbffd95f4
nginx-dlt-7f77dfcf6c 3 3 3 8m5s app=nginx-dlt,pod-template-hash=7f77dfcf6c

​ 以防万一,我们还可以通过describe选项查看deployment详细资源描述,验证镜像是否已更换为最新版本镜像

1
2
[root@k8s-master-01 ~]# kubectl describe deployments.apps nginx-dlt | grep -i Image:
Image: nginx:1.16.1

Deployment版本回滚

​ 如果我们在版本更新后发现,新版本存在重大BUG,我们需要将新版本回退到之前的版本,我们可以按照如下方法进行操作。

​ 首先使用rollout选项中的history,查看名为nginx-dlt的deployment资源更新记录。最终我们将查看到该资源所更新过的所有记录。

1
2
3
4
5
[root@k8s-master-01 controller]# kubectl rollout history deployment nginx-dlt 
deployment.apps/nginx-dlt
REVISION CHANGE-CAUSE
1 <none>
2 <none>

​ 但是上述给出的信息仅有更新的顺序编号1、2,另外有字段是none(空),这时我们可以通过加上--revision 编号的方式,查看该编号具体对应的是我哪次版本的更新,经过查找,发现序号1是我需要回滚的版本为1.14.2的记录。

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
[root@k8s-master-01 controller]# kubectl rollout history deployment nginx-dlt --revision 1
deployment.apps/nginx-dlt with revision #1
Pod Template:
Labels: app=nginx-dlt
pod-template-hash=6dbffd95f4
Containers:
nginx-dlt:
Image: nginx:1.14.2
Port: 80/TCP
Host Port: 0/TCP
Environment: <none>
Mounts: <none>
Volumes: <none>
Node-Selectors: <none>
Tolerations: <none>
[root@k8s-master-01 controller]# kubectl rollout history deployment nginx-dlt --revision 2
deployment.apps/nginx-dlt with revision #2
Pod Template:
Labels: app=nginx-dlt
pod-template-hash=7f77dfcf6c
Containers:
nginx-dlt:
Image: nginx:1.16.1
Port: 80/TCP
Host Port: 0/TCP
Environment: <none>
Mounts: <none>
Volumes: <none>
Node-Selectors: <none>
Tolerations: <none>

​ 得知我们要回退的记录序号是1后,可通过rollout选项中的undo,对名为nginx-dlt的deployment资源,--to-revision 1将版本退回到1.14.2。

1
2
[root@k8s-master-01 controller]# kubectl rollout undo deployment nginx-dlt --to-revision 1
deployment.apps/nginx-dlt rolled back

​ 查看资源详细信息,确认版本是否回退。

1
2
[root@k8s-master-01 controller]# kubectl describe deployments.apps nginx-dlt | grep -i Image:
Image: nginx:1.14.2

CHANGE-CAUSE变更描述

​ 上述介绍回滚时,我们使用rollout history查看历史更新记录时,发现CHANGE-CAUSE字段是一个为空的,所以我们只能使用--revision 2查看具体版本信息,已确认我们要回退的序号是多少。

​ CHANGE-CAUSE 的内容是从 Deployment 的 kubernetes.io/change-cause 注解复制过来的。 复制动作发生在修订版本创建时。你可以通过以下方式设置 CHANGE-CAUSE 消息:

  • 使用 [root@k8s-master-01 controller]# kubectl annotate deployments.apps nginx-dlt kubernetes.io/change-cause="image updated to 1.14.2" 为 当前版本的Deployment 添加注解。
  • 手动编辑资源的清单。
1
2
3
4
5
6
7
[root@k8s-master-01 controller]# kubectl annotate deployments.apps nginx-dlt kubernetes.io/change-cause="image updated to 1.14.2" 
deployment.apps/nginx-dlt annotated
[root@k8s-master-01 controller]# kubectl rollout history deployment nginx-dlt
deployment.apps/nginx-dlt
REVISION CHANGE-CAUSE
2 <none>
3 image updated to 1.14.2

​ 或者您也可以在滚动更新时,加上–cored字段为其自动将您当前的命令当作注解进行添加。

1
2
3
4
5
6
7
8
[root@k8s-master-01 controller]# kubectl set image deployments nginx-dlt nginx-dlt=nginx:1.16.1 --record
Flag --record has been deprecated, --record will be removed in the future
deployment.apps/nginx-dlt image updated
[root@k8s-master-01 controller]# kubectl rollout history deployment nginx-dlt
deployment.apps/nginx-dlt
REVISION CHANGE-CAUSE
3 image updated to 1.14.2
4 kubectl set image deployments nginx-dlt nginx-dlt=nginx:1.16.1 --record=true

​ 这样既可以清楚的看出每个版本更新的内容介绍,便于版本的回退及管理。

扩缩容Deployment

​ 您可以根据业务压力情况,灵活的对Deployment使用如下指令手动进行扩缩容操作 :

1
2
3
4
5
6
7
8
9
10
[root@k8s-master-01 controller]# kubectl scale deployment nginx-dlt --replicas 2
deployment.apps/nginx-dlt scaled
[root@k8s-master-01 controller]# kubectl get deployments.apps
NAME READY UP-TO-DATE AVAILABLE AGE
nginx-dlt 2/2 2 2 4h1m
[root@k8s-master-01 controller]# kubectl scale deployment nginx-dlt --replicas 4
deployment.apps/nginx-dlt scaled
[root@k8s-master-01 controller]# kubectl get deployments.apps
NAME READY UP-TO-DATE AVAILABLE AGE
nginx-dlt 4/4 4 4 3h59m

​ 假设集群启用了 Pod 的水平自动缩放, 你可以为 Deployment 设置自动缩放器,并基于现有 Pod 的 CPU 利用率选择要运行的 Pod 个数下限和上限。

​ 这样可以让控制器进行自我调节,基于现有Pod的CPU利用率达到80,最大可自动扩容值15个副本,最小保持在10个副本。

1
2
3
4
5
[root@k8s-master-01 controller]# kubectl autoscale deployment nginx-dlt --max 15 --min 10 --cpu-percent 80
horizontalpodautoscaler.autoscaling/nginx-dlt autoscaled
[root@k8s-master-01 controller]# kubectl get deployments.apps nginx-dlt
NAME READY UP-TO-DATE AVAILABLE AGE
nginx-dlt 10/10 10 10 4h14m

比例缩放

​ RollingUpdate 的 Deployment 支持同时运行应用程序的多个版本。 当自动缩放器缩放处于上线进程(仍在进行中或暂停)中的 RollingUpdate Deployment 时, Deployment 控制器会平衡现有的活跃状态的 ReplicaSet(含 Pod 的 ReplicaSet)中的额外副本, 以降低风险。这称为比例缩放(Proportional Scaling)

1
2
[root@k8s-master-01 controller]# kubectl set image deployments nginx-dlt nginx-dlt=nginx:sometag
deployment.apps/nginx-dlt image updated

​ 例如,你正在运行一个 10 个副本的 Deployment,其中25% max unavailable(最大不可用), 25% max surge(最大激增)

  • 确保有10个副本正在运行
1
2
3
[root@k8s-master-01 controller]# kubectl get deployments.apps 
NAME READY UP-TO-DATE AVAILABLE AGE
nginx-dlt 10/10 10 10 4h14m
  • 更新 Deployment 使用新镜像,碰巧该镜像无法从集群内部解析,这里采用更新一个不存在的镜像来演示阻塞。
1
2
3
[root@k8s-master-01 controller]# kubectl set image deployments nginx-dlt nginx-dlt=nginx:sometag
deployment.apps/nginx-dlt image updated

  • 镜像更新使用 ReplicaSet nginx-dlt-6788c467d8 启动新的上线过程, 但由于上面提到的 maxUnavailable (最大不可用)、maxSurge(最大激增)要求,该进程当被创建出第五个副本时被阻塞了。检查上线状态:
1
2
3
4
[root@k8s-master-01 controller]# kubectl get rs
NAME DESIRED CURRENT READY AGE
nginx-dlt-6788c467d8 5 5 0 3s
nginx-dlt-7f77dfcf6c 8 8 8 4h20m
  • 然后,出现了新的 Deployment 扩缩请求。自动缩放器将 Deployment 副本增加到 15。 Deployment 控制器需要决定在何处添加 5 个新副本。如果未使用比例缩放,所有 5 个副本 都将添加到新的 ReplicaSet 中。使用比例缩放时,可以将额外的副本分布到所有 ReplicaSet。 较大比例的副本会被添加到拥有最多副本的 ReplicaSet,而较低比例的副本会进入到 副本较少的 ReplicaSet。所有剩下的副本都会添加到副本最多的 ReplicaSet。 具有零副本的 ReplicaSet 不会被扩容。
1
2
[root@k8s-master-01 controller]# kubectl scale deployment nginx-dlt --replicas 15
deployment.apps/nginx-dlt scaled

​ 在上面的示例中,新的rs数量是5,旧的是8,通过扩容至15,为满足扩容需要的需求,所以4个副本被添加到旧 ReplicaSet 中,2 个副本被添加到新 ReplicaSet。

1
2
3
4
[root@k8s-master-01 controller]# kubectl get rs
NAME DESIRED CURRENT READY AGE
nginx-dlt-6788c467d8 7 7 0 4m54s
nginx-dlt-7f77dfcf6c 12 12 12 4h25m

​ 假定新的副本都很健康,上线过程最终应将所有副本迁移到新的 ReplicaSet 中。 要确认这一点,请运行:

​ 但是该演示为了演示阻塞,仅有旧副本的12个是正常的,此为演示需要。

1
2
3
[root@k8s-master-01 controller]# kubectl get deploy
NAME READY UP-TO-DATE AVAILABLE AGE
nginx-dlt 12/15 7 12 4h51m

删除水平伸缩扩容

​ 当您要清除水平伸缩策略时,可以通过如下命令来进行删除。如您删除控制器,但是未删除水平伸缩策略,并重新创建同一名称资源,您之前所创建的策略会应用在新创建的资源中。

  • 查看水平伸缩策略
1
2
3
[root@k8s-master-01 controller]# kubectl get horizontalpodautoscalers.autoscaling nginx-dlt 
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
nginx-dlt Deployment/nginx-dlt cpu: <unknown>/80% 10 15 10 108m
  • 删除水平伸缩策略
1
2
[root@k8s-master-01 controller]# kubectl delete horizontalpodautoscalers.autoscaling nginx-dlt 
horizontalpodautoscaler.autoscaling "nginx-dlt" deleted
  • 删除资源,重新创建。
1
2
3
4
5
6
7
[root@k8s-master-01 controller]# kubectl delete -f deployment.yaml 
deployment.apps "nginx-dlt" deleted
[root@k8s-master-01 controller]# kubectl create -f deployment.yaml
deployment.apps/nginx-dlt created
[root@k8s-master-01 controller]# kubectl get deployments.apps nginx-dlt
NAME READY UP-TO-DATE AVAILABLE AGE
nginx-dlt 3/3 3 3 14s

暂停与恢复Deployment 的上线过程

​ 在你更新一个 Deployment 的时候,或者计划更新它的时候, 你可以在触发一个或多个更新之前暂停 Deployment 的上线过程。 当你准备应用这些变更时,你可以重新恢复 Deployment 上线过程。 这样做使得你能够在暂停和恢复执行之间应用多个修补程序,而不会触发不必要的上线操作。

  • 例如,对于一个刚刚创建的 Deployment,获取Deployment、rs上线状态:
1
2
3
4
5
6
[root@k8s-master-01 controller]# kubectl get deployments.apps,replicasets.apps --show-labels -l app=nginx-dlt
NAME READY UP-TO-DATE AVAILABLE AGE LABELS
deployment.apps/nginx-dlt 3/3 3 3 12s app=nginx-dlt

NAME DESIRED CURRENT READY AGE LABELS
replicaset.apps/nginx-dlt-6dbffd95f4 3 3 3 12s app=nginx-dlt,pod-template-hash=6dbffd95f4
  • 使用如下指令对名为nginx-dlt的deployment资源启用暂停上线:
1
2
[root@k8s-master-01 controller]# kubectl rollout pause deployment nginx-dlt 
deployment.apps/nginx-dlt paused
  • 使用如下指令对资源进行滚动更新
1
2
3
[root@k8s-master-01 controller]# kubectl set image deployments nginx-dlt nginx-dlt=nginx:1.16.1 --record
Flag --record has been deprecated, --record will be removed in the future
deployment.apps/nginx-dlt image updated
  • 但是我们查看更新记录中并未存在我们刚才更新的那条记录
1
2
3
4
[root@k8s-master-01 controller]# kubectl rollout history deployment nginx-dlt 
deployment.apps/nginx-dlt
REVISION CHANGE-CAUSE
1 <none>
  • 再次查看deployment资源中当前的镜像版本是1.16.1是最新版本。
1
2
[root@k8s-master-01 controller]# kubectl describe deployments.apps nginx-dlt | grep -i Image:
Image: nginx:1.16.1
  • 最终再查看rs中还是原来的1.14.2的版本,说明我们实际的Pod并未更新成功,仅是deployment变更了而已,但是实际的rs控制Pod的资源实际还是原本的版本。
1
2
[root@k8s-master-01 controller]# kubectl describe rs nginx-dlt-6dbffd95f4 | grep -i Image:
Image: nginx:1.14.2
  • 那是因为我们之前做了上线暂停的操作导致的,您可以根据需要在暂停和恢复期间做更多的操作,避免未作完更改操作,提前上线,造成不必要的麻烦,例如您还想对该Pod申请更多的cpu=200m、memory=512Micpu和内存资源等。
1
2
[root@k8s-master-01 controller]# kubectl set resources -f deployment.yaml  -c=nginx-dlt --limits=cpu=200m,memory=512Mi
deployment.apps/nginx-dlt resource requirements updated
  • 当您确认修改好后,想要恢复上线过程,可以是使用如下命令进行恢复。
  • 此时即可看到rs正常开始上线新版本,并且版本已变为最新,以及最新的更新记录中也产生了部署记录。
1
2
3
4
5
6
7
8
9
10
11
[root@k8s-master-01 controller]# kubectl get rs 
NAME DESIRED CURRENT READY AGE
nginx-dlt-6dbffd95f4 0 0 0 21m
nginx-dlt-7495f89c5 3 3 3 13s
[root@k8s-master-01 controller]# kubectl describe rs nginx-dlt-7495f89c5 | grep Image:
Image: nginx:1.16.1
[root@k8s-master-01 controller]# kubectl rollout history deployment nginx-dlt
deployment.apps/nginx-dlt
REVISION CHANGE-CAUSE
1 <none>
2 kubectl set image deployments nginx-dlt nginx-dlt=nginx:1.16.1 --record=true

清除策略

​ 您在生产中可能会有如下困惑,每次set更新ReplicaSet都会产生一条记录,并且该记录当积累到10条以后,会开始从旧版本中依次删除替换。

​ 您如果想对ReplicaSet的条数有所调整的话,可以在创建Deployment的YAML文件中设置 .spec.revisionHistoryLimit 字段以指定保留此 Deployment 的多少个旧有 ReplicaSet。其余的 ReplicaSet 将在后台被垃圾回收。 默认情况下,此值为 10。

​ 可参考以下示例,以下示例中revisionHistoryLimit值设置为0,则每次set更新,都只会保留最新的一个rs,旧的会直接被删除,如果您想能够多保留几次,则可以将该值放大。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-dlt
namespace: book-k8s
spec:
revisionHistoryLimit: 0
replicas: 3
selector:
matchLabels:
app: nginx-dlt
template:
metadata:
labels:
app: nginx-dlt
namespace: book-k8s
spec:
containers:
- name: nginx-dlt
image: nginx:1.14.2
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
protocol: TCP
  • 确认当前的rs名称
1
2
3
[root@k8s-master-01 controller]# kubectl get rs
NAME DESIRED CURRENT READY AGE
nginx-dlt-6dbffd95f4 3 3 3 3s
  • 进行更新操作
1
2
[root@k8s-master-01 controller]# kubectl set image deployments nginx-dlt nginx-dlt=nginx:1.16.1
deployment.apps/nginx-dlt image updated
  • 查看rs,由于revisionHistoryLimit设置的是0,所以原先版本的rs会被直接删除,替换为新的rs,旧的不再保留。
1
2
3
[root@k8s-master-01 controller]# kubectl get rs
NAME DESIRED CURRENT READY AGE
nginx-dlt-7f77dfcf6c 3 3 3 22s
  • 但是需要注意,生产环境不能设置为0,因为这同时也会影响回滚的历史部署记录,这回导致您无法通过rollout history进行回滚操作。
1
2
3
4
[root@k8s-master-01 controller]# kubectl rollout history deployment nginx-dlt
deployment.apps/nginx-dlt
REVISION CHANGE-CAUSE
2 <none>

DaemonSet控制器

​ DaemonSet定义了提供节点本地设施的Pod。这些设施可能对于集群的运行至关重要,例如网络辅助工具、资源监控,或者作为add-on的一部分。

​ DaemonSet会确保全部(或者某些)节点上运行一个Pod的副本。当有节点加入集群时,也会为他自动新增一个Pod副本。当有节点被从集群中移除时,这些Pod也会被收回。删除DaemonSet将会删除它所创建的所有Pod。

DaemonSet的一些经典用法

  • 在每个节点上运行集群守护进程。
  • 在每个节点上运行日志收集守护进程。
  • 在每个节点上运行监控守护进程。

一种简单的用法是为每种类型的守护进程在所有的节点上都启动一个 DaemonSet。 一个稍微复杂的用法是为同一种守护进程部署多个 DaemonSet;每个具有不同的标志, 并且对不同硬件类型具有不同的内存、CPU 要求。

编写DaemonSet规约

  • kind: DaemonSet:创建一个资源类型为DaemonSet的资源。
  • spec.template.spec.tolerations:该参数为容忍度,由于目前的要求是在所有节点上都要运行一个这样模板的Pod,在K8s默认的调度机制中,master节点是不允许运行的,所有这里要加个容忍度,让节点能够容忍该污点,具体细节后续章节中详细介绍。
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
[root@k8s-master-01 controller]# cat > daemonSet.yaml << EOF
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: nginx-ds
labels:
app: nginx-ds
spec:
selector:
matchLabels:
app: nginx-ds
template:
metadata:
name: nginx-ds
labels:
app: nginx-ds
spec:
tolerations:
- effect: NoSchedule
key: node-role.kubernetes.io/control-plane
operator: Exists
containers:
- name: nginx-ds
image: nginx
imagePullPolicy: IfNotPresent
EOF

​ 运行yaml,创建资源,可尝试使用如下命令。

1
2
[root@k8s-master-01 controller]# kubectl create -f daemonSet.yaml 
daemonset.apps/nginx-ds created

​ 执行后,查看想要运行结果。

发现该模板中我们并没有像deployment一样设定副本数,但是他能根据我们集群中拥有的节点数创建出对应数量的Pod副本。由于我们使用的允许容忍度,让本不执行实际工作Pod的master节点,也同时运行着该Pod副本。 所以这实验结果也就验证了最开始我们介绍的,DaemonSet的作用是可为您每个节点都运行一个Pod副本,有且只有一个。

1
2
3
4
5
[root@k8s-master-01 controller]# kubectl get pods -l app=nginx-ds --show-labels -o wide 
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES LABELS
nginx-ds-6wzn7 1/1 Running 0 24s 10.244.44.245 k8s-node-02 <none> <none> app=nginx-ds,controller-revision-hash=78b4cc6666,pod-template-generation=1
nginx-ds-9rlrh 1/1 Running 0 24s 10.244.154.234 k8s-node-01 <none> <none> app=nginx-ds,controller-revision-hash=78b4cc6666,pod-template-generation=1
nginx-ds-qm4f5 1/1 Running 0 24s 10.244.151.143 k8s-master-01 <none> <none> app=nginx-ds,controller-revision-hash=78b4cc6666,pod-template-generation=1

DaemonSet的调度

DaemonSet 可用于确保所有符合条件的节点都运行该 Pod 的一个副本。 DaemonSet 控制器为每个符合条件的节点创建一个 Pod,并添加 Pod 的 spec.affinity.nodeAffinity 字段以匹配目标主机。Pod 被创建之后,默认的调度程序通常通过设置 .spec.nodeName 字段来接管 Pod 并将 Pod 绑定到目标主机。如果新的 Pod 无法放在节点上,则默认的调度程序可能会根据新 Pod 的优先级抢占 (驱逐)某些现存的 Pod。

说明:

当 DaemonSet 中的 Pod 必须运行在每个节点上时,通常需要将 DaemonSet 的 .spec.template.spec.priorityClassName 设置为具有更高优先级的 PriorityClass, 以确保可以完成驱逐。

用户通过设置 DaemonSet 的 .spec.template.spec.schedulerName 字段,可以为 DaemonSet 的 Pod 指定不同的调度程序。

当评估符合条件的节点时,原本在 .spec.template.spec.affinity.nodeAffinity 字段上指定的节点亲和性将由 DaemonSet 控制器进行考量,但在创建的 Pod 上会被替换为与符合条件的节点名称匹配的节点亲和性。

ScheduleDaemonSetPods 允许你使用默认调度器而不是 DaemonSet 控制器来调度这些 DaemonSet, 方法是将 NodeAffinity 条件而不是 .spec.nodeName 条件添加到这些 DaemonSet Pod。 默认调度器接下来将 Pod 绑定到目标主机。 如果 DaemonSet Pod 的节点亲和性配置已存在,则被替换 (原始的节点亲和性配置在选择目标主机之前被考虑)。 DaemonSet 控制器仅在创建或修改 DaemonSet Pod 时执行这些操作, 并且不会更改 DaemonSet 的 spec.template

1
2
3
4
5
6
7
8
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchFields:
- key: metadata.name
operator: In
values:
- target-host-name

DaemonSet滚动更新

​ 首先我们需要先确保原有Pod模板中带有 RollingUpdate 更新策略的 DaemonSet,如果没有,可根据如下操作对资源设定一个具有 RollingUpdate 更新策略的 DaemonSet。

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
31
[root@k8s-master-01 controller]# cat > daemonSet.yaml << EOF
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: nginx-ds
labels:
app: nginx-ds
spec:
updateStrategy:
type: RollingUpdate
rollingUpdate:
# 最大不可用
maxUnavailable: 1
selector:
matchLabels:
app: nginx-ds
template:
metadata:
name: nginx-ds
labels:
app: nginx-ds
spec:
tolerations:
- effect: NoSchedule
key: node-role.kubernetes.io/control-plane
operator: Exists
containers:
- name: nginx-ds
image: nginx
imagePullPolicy: IfNotPresent
EOF

​ 更新模板,可使用如下命令。

1
2
3
[root@k8s-master-01 controller]# kubectl apply -f daemonSet.yaml 
Warning: resource daemonsets/nginx-ds is missing the kubectl.kubernetes.io/last-applied-configuration annotation which is required by kubectl apply. kubectl apply should only be used on resources created declaratively by either kubectl create --save-config or kubectl apply. The missing annotation will be patched automatically.
daemonset.apps/nginx-ds configured

​ 最后检查 DaemonSet 的更新策略,确保已经将其设置为 RollingUpdate

1
2
[root@k8s-master-01 controller]# kubectl apply -f daemonSet.yaml --dry-run=client -o go-template='{{.spec.updateStrategy.type}}{{"\n"}}'
RollingUpdate

​ 命令的输出都应该为:

1
RollingUpdate

声明式命令

如果你使用配置文件来更新 DaemonSet,请使用 kubectl apply

1
kubectl apply -f daemonSet.yaml

指令式命令

如果你使用指令式命令来更新 DaemonSets,请使用 kubectl edit

1
kubectl edit ds/nginx-ds -n kube-system
只更新容器镜像

如果你只需要更新 DaemonSet 模板里的容器镜像,比如 .spec.template.spec.containers[*].image, 请使用 kubectl set image

1
kubectl set image ds/nginx-ds nginx-ds=nginx:版本号 -n kube-system

StatefulSet控制器

​ StatefulSet从字面意思来理解,就是有状态的副本集合,那既然是有状态的那就肯定是有无状态的副本。

​ StatefulSet 运行一组 Pod,并为每个 Pod 保留一个稳定的标识。 这可用于管理需要持久化存储或稳定、唯一网络标识的应用。

​ StatefulSet 是用来管理有状态应用的工作负载 API 对象。

​ StatefulSet 用来管理某 Pod集合的部署和扩缩, 并为这些 Pod 提供持久存储和持久标识符。

Deployment 类似, StatefulSet 管理基于相同容器规约的一组 Pod。但和 Deployment 不同的是, StatefulSet 为它们的每个 Pod 维护了一个有粘性的 ID。这些 Pod 是基于相同的规约来创建的, 但是不能相互替换:无论怎么调度,每个 Pod 都有一个永久不变的 ID。

​ 如果希望使用存储卷为工作负载提供持久存储,可以使用 StatefulSet 作为解决方案的一部分。 尽管 StatefulSet 中的单个 Pod 仍可能出现故障, 但持久的 Pod 标识符使得将现有卷与替换已失败 Pod 的新 Pod 相匹配变得更加容易。

使用 StatefulSet

StatefulSet 对于需要满足以下一个或多个需求的应用程序很有价值。

  • 稳定的、唯一的网络标识符。
  • 稳定的、持久的存储。
  • 有序的、优雅的部署和扩缩。
  • 有序的、自动的滚动更新。

在上面描述中,“稳定的”意味着 Pod 调度或重调度的整个过程是有持久性的。 如果应用程序不需要任何稳定的标识符或有序的部署、删除或扩缩, 则应该使用由一组无状态的副本控制器提供的工作负载来部署应用程序,比如 Deployment可能更适用于你的无状态应用部署需要。

什么是有状态和无状态?

​ 在了解什么是有状态的和无状态之前,我们先来回顾一下deployments,我们前面了解过,deployments在当遇到某一组Pod异常下线后,控制器会为我们自动创建一个新的Pod来替换下线的Pod,替换过后原有未经过持久化存储的数据就会丢失,又或者无论请求发送到了哪个Pod,返回的结果都是一致的,每个Pod他都是平等的。这叫做无状态的应用

​ 而有状态的应用是需要具有持久化的存储,稳定的网络标识,Pod与Pod之前并不平等,即使它们用的是同一个镜像进行创建的。这类服务我们通常称之为“有状态”的服务。

​ 他的视线不再依靠ReplicaSet,而是用StatefulSet,它具备以下特征:

  • 稳定的持久化存储,Pod重新调度后访问相同的持久化数据,可以使用PVC实现。
  • 稳定的网络标识,Pod重新调度后PodName和HostName不变,基于Headless Service实现。
  • Pod都有一个”序号“,可以有序的进行扩展,部署等操作。

创建有状态的应用的三部曲

image-20251009153313117

创建statefulSet

制作NFS共享目录

1
2
3
4
5
6
7
8
9
10
11
12
13
# 创建共享目录
[root@k8s-master-01 data]# mkdir -p /data/nfs{1..3}
# 如没有NFS服务,则需先安装NFS服务
[root@k8s-master-01 dev]# yum -y install nfs-kernel-server
# 开启自启及重启启动NFS服务
[root@k8s-master-01 dev]# systemctl enable nfs-server.service
[root@k8s-master-01 dev]# systemctl restart nfs-server.service
# 最终查看验证得到我们NFS共享出来的三组共享目录
[root@k8s-master-01 dev]# showmount -e
Export list for k8s-master-01:
/data/nfs3 *
/data/nfs2 *
/data/nfs1 *

准备StorageClass(存储类)的yaml文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[root@k8s-master-01 controller]# cat > my-StorageClass.yaml << EOF
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: my-storage-class
annotations:
storageclass.kubernetes.io/is-default-class: "false"
provisioner: csi-driver.example-vendor.example
reclaimPolicy: Retain # default value is Delete
allowVolumeExpansion: true
mountOptions:
- discard # this might enable UNMAP / TRIM at the block storage layer
volumeBindingMode: WaitForFirstConsumer
parameters:
guaranteedReadWriteLatency: "true" # provider-specific
EOF

准备statefulSet的yaml文件

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
[root@k8s-master-01 controller]# cat > statefulSet.yaml << EOF
# 创建PV
apiVersion: v1
kind: PersistentVolume
metadata:
name: mypv1
spec:
storageClassName: my-storage-class
capacity:
storage: 1Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Recycle
nfs:
path: /data/nfs1
server: 192.168.8.136
---
# 创建service
apiVersion: v1
kind: Service
metadata:
name: nginx
labels:
app: nginx
spec:
ports:
- port: 80
name: web
clusterIP: None
selector:
app: nginx
---
# 创建StatefulSet
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: web
spec:
selector:
matchLabels:
app: nginx # 必须匹配 .spec.template.metadata.labels
serviceName: "nginx"
replicas: 3 # 默认值是 1
minReadySeconds: 10 # 默认值是 0
template:
metadata:
labels:
app: nginx # 必须匹配 .spec.selector.matchLabels
spec:
terminationGracePeriodSeconds: 10
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
name: web
volumeMounts:
- name: www
mountPath: /usr/share/nginx/html
# 在创建StatefulSet的同时,创建PVC
volumeClaimTemplates:
- metadata:
name: www
spec:
accessModes: [ "ReadWriteOnce" ]
storageClassName: "my-storage-class"
resources:
requests:
storage: 1Gi
EOF

创建StatefulSet

​ 在准备好所有所需yaml文件后,可参考以下方式依次执行创建资源

1
2
3
4
5
6
7
8
# 创建存储类
[root@k8s-master-01 controller]# kubectl create -f my-StorageClass.yaml
storageclass.storage.k8s.io/my-storage-class created
# 创建PV、service和PVC及StatefulSet
[root@k8s-master-01 controller]# kubectl create -f statefulSet.yaml
Warning: spec.persistentVolumeReclaimPolicy: The Recycle reclaim policy is deprecated. Instead, the recommended approach is to use dynamic provisioning.
persistentvolume/mypv1 created
service/nginx created

查看StatefulSet运行效果

​ 这里我们也发现它分别运行了2个副本,它们的Pod副本名称都有有序的,web是我们为资源取的名称,它会在我们自定义的名称后增加-0、-1….的序号,它们的启动顺序是固定的,只有等前面的启动完成,后面的才会启动,所以我们这里序号0成功,序号1目前的卡着不动了。

​ 这里可能就会有小伙伴有疑问了,我们要求该StatefulSet具有3个副本,但是第一个运行成功,但是第二个为什么一直在Pending等待,这就跟我们创建的PVC有关系了,我们创建PVC的时候是采用的【ReadWriteOnce】,意思就是一个读写模式,所以当我序号0的副本跟这个PVC绑定后,第二个再来请求绑定时,则就会出现失败。

1
2
3
4
5
6
7
[root@k8s-master-01 data]# kubectl get statefulsets.apps 
NAME READY AGE
web 1/3 12m
[root@k8s-master-01 data]# kubectl get pods --show-labels -l app=nginx
NAME READY STATUS RESTARTS AGE LABELS
web-0 1/1 Running 0 13m app=nginx,apps.kubernetes.io/pod-index=0,controller-revision-hash=web-69cd7d45cc,statefulset.kubernetes.io/pod-name=web-0
web-1 0/1 Pending 0 13m app=nginx,apps.kubernetes.io/pod-index=1,controller-revision-hash=web-69cd7d45cc,statefulset.kubernetes.io/pod-name=web-1

​ 到这里不要感觉懵,我们目前是还未介绍到存储和service服务发现,目前您只需要了解StatefulSet它的作用,及StatefulSet与deployment一个有状态的应用和无状态应用的区别即可。

Job

​ Job 表示一次性任务,运行完成后就会停止。

​ Job 会创建一个或者多个 Pod,并将继续重试 Pod 的执行,直到指定数量的 Pod 成功终止。 随着 Pod 成功结束,Job 跟踪记录成功完成的 Pod 个数。 当数量达到指定的成功个数阈值时,任务(即 Job)结束。 删除 Job 的操作会清除所创建的全部 Pod。 挂起 Job 的操作会删除 Job 的所有活跃 Pod,直到 Job 被再次恢复执行。

​ 一种简单的使用场景下,你会创建一个 Job 对象以便以一种可靠的方式运行某 Pod 直到完成。 当第一个 Pod 失败或者被删除(比如因为节点硬件失效或者重启)时,Job 对象会启动一个新的 Pod。

​ 你也可以使用 Job 以并行的方式运行多个 Pod。

​ 如果你想按某种排期表(Schedule)运行 Job(单个任务或多个并行任务),可参考下面的CronJob。

​ 该job任务负责计算 π 到小数点后 2000 位,并将结果打印出来。 此计算大约需要 10 秒钟完成。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[root@k8s-master-01 controller]# cat > job.yaml << EOF
apiVersion: batch/v1
kind: Job
metadata:
name: pi
spec:
template:
spec:
containers:
- name: pi
image: perl:5.34.0
imagePullPolicy: IfNotPresent
command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"]
restartPolicy: Never
backoffLimit: 4
EOF

​ 可使用如下指令创建资源。

1
2
[root@k8s-master-01 controller]# kubectl create -f job.yaml 
job.batch/pi created

​ 可使用以下指令查看运行结果。

1
2
[root@k8s-master-01 controller]# kubectl logs pi-4r9z8
3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679821480865132823066470938446095505822......

CronJob

​ CronJob 通过重复调度启动一次性的 Job。

CronJob 创建基于时隔重复调度的 Job。

CronJob 用于执行排期操作,例如备份、生成报告等。 一个 CronJob 对象就像 Unix 系统上的 crontab(cron table)文件中的一行。 它用 Cron格式进行编写, 并周期性地在给定的调度时间执行 Job。

CronJob 有所限制,也比较特殊。 例如在某些情况下,单个 CronJob 可以创建多个并发任务。 请参阅下面的限制。

当控制平面为 CronJob 创建新的 Job 和(间接)Pod 时,CronJob 的 .metadata.name 是命名这些 Pod 的部分基础。 CronJob 的名称必须是一个合法的 DNS 子域值, 但这会对 Pod 的主机名产生意外的结果。为获得最佳兼容性,名称应遵循更严格的 DNS 标签规则。 即使名称是一个 DNS 子域,它也不能超过 52 个字符。这是因为 CronJob 控制器将自动在你所提供的 Job 名称后附加 11 个字符,并且存在 Job 名称的最大长度不能超过 63 个字符的限制。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
[root@k8s-master-01 controller]# cat > cronJob.yaml << EOF
apiVersion: batch/v1
kind: CronJob
metadata:
name: hello
spec:
schedule: "* * * * *"
jobTemplate:
spec:
template:
spec:
containers:
- name: hello
image: busybox:1.28
imagePullPolicy: IfNotPresent
command:
- /bin/sh
- -c
- date; echo Hello from the Kubernetes cluster
restartPolicy: OnFailure
EOF

​ 可使用如下指令创建资源。

1
2
[root@k8s-master-01 controller]# kubectl create -f cronJob.yaml 
cronjob.batch/hello created

​ 它会根据您设置的时间,定时执行固定的任务,每次执行都会产生一个随机Pod,这里我设置的是每秒执行一次。

1
2
3
4
5
6
7
8
[root@k8s-master-01 controller]# kubectl get pods
NAME READY STATUS RESTARTS AGE
centos 0/1 ImagePullBackOff 0 49d
centos-01 0/1 ImagePullBackOff 0 79d
hello-29333343-gfkcb 0/1 Completed 0 3m47s
hello-29333344-zjbct 0/1 Completed 0 2m47s
hello-29333345-qvcrv 0/1 Completed 0 107s
hello-29333346-bsb4q 0/1 ContainerCreating 0 47s

​ 可通关logs指令查看运行日志结果:

1
2
3
[root@k8s-master-01 controller]# kubectl logs hello-29333344-zjbct 
Thu Oct 9 09:03:28 UTC 2025
Hello from the Kubernetes cluster

Cron 时间表语法

.spec.schedule 字段是必需的。该字段的值遵循 Cron 语法:

1
2
3
4
5
6
7
8
9
# ┌───────────── 分钟 (0 - 59)
# │ ┌───────────── 小时 (0 - 23)
# │ │ ┌───────────── 月的某天 (1 - 31)
# │ │ │ ┌───────────── 月份 (1 - 12)
# │ │ │ │ ┌───────────── 周的某天 (0 - 6)(周日到周六)
# │ │ │ │ │ 或者是 sun,mon,tue,web,thu,fri,sat
# │ │ │ │ │
# │ │ │ │ │
# * * * * *

例如 0 3 * * 1 表示此任务计划于每周一凌晨 3 点运行。