环境:centos7.9 docker-ce-20.10.9 kubernetes-version v1.22.6
在上一篇《pod的介绍、命令行创建pod》我们介绍了何为pod,以及如何在命令行创建pod,但这都不是我们在生产环境的常规操作,我们在生产环境中一般是通过控制器来创建pod的,并使用控制器来管理pod。本篇我们来论述何为pod控制器。
pod控制器,是用于管理、部署pod的一种k8s中重要的资源,确保pod资源符合预期的状态,pod的资源出现故障时,会尝试 进行重启,当根据重启策略无效,则会新建pod的资源。这样,当有了控制器来管理pod,我们就能轻松的通过配置控制器,让控制器来帮我们实现我们所期望的pod扩容、缩容,即使当pod出现异常挂掉,控制器也能立即重新启动一个pod,因为控制器的本质其实就是实时检查pod数量使其符合我们预先设定的值。
常用的pod控制器有一下几种:
ReplicationController: 比较原始的pod控制器,已经被废弃,了解即可,有ReplicaSet替代;
ReplicaSet: 保证指定数量的pod运行,并提供pod数据变更,镜像版本变更;
DaemonSet: 在集群每个node上都运行一个pod,确保全部Node 上运行一个 Pod 的副本,适用于每个node工作节点后台日志收集等场景;
CronJob: 用于执行周期性定时任务的pod,主要用于执行周期性计划,类似于Linux的crontab定时任务;
Job: 用于一次性计划任务的pod,执行完毕pod就立即退出,类似于Linux的at命令;
Deployment: 通过创建DaemonSet来创建pod,支持滚动升级,版本回退,Deployment是最常用的pod控制器;
Horizontal Pod Autoscaler: 可以根据集群负载自动调整pod的数量,实现自动扩容缩容,削峰填谷;
StatefulSet: 管理又状态的应用;
ReplicationController,简写rc,是k8s中一种pod控制器,在新版本中,rc已经被取代,所以这里了解rc即可;
ReplicationController会持续监控正在运行的pod列表,并保证相应标签类型的pod数量与期望的相符,如正在运行的pod太少,rc就会根据pod模板来创建新的副本,如果正在运行的pod太多,rc将会删除多余的pod。
总之,ReplicationController的工作就是确保pod数据始终与其定义的标签选择器选中的pod数量相匹配,如果不匹配,rc将会根据所需,采取适当的操作来协调pod数量。
rc的三大部分
1、label selector,标签选择器,用于确定rc作用于哪些pod,反过来说哪些pod可以被rc管理;
2、replica,副本数,指定运行的pod数量;
3、pod template,pod模板,用于创建新的pod副本,pod就是根据模板来创建的。
rc的实现原理
rc与pod其实是通过标签和标签选择器来进行管联的,当改变一个pod的标签,那么该pod就会脱离rc的管理,而rc 发现少了一个pod,又会根据模板重新创建一个pod来满足副本数。如果先独立创建了pod并定义了标签,然后再创建rc,rc的标签筛选器和已存在的pod的标签相同,那么创建rc时,rc直接接管已存在的pod,rc根本不会创建新的pod,这就说明,rc与pod的根本就是通过标签和标签选择器来进行管联的。
更改rc的标签选择器和pod模板会怎样
更改rc的标签选择器和pod模板对正在运行的pod没有影响,更改标签选择器会使现有的pod脱离rc的范围,因此rc会停止关注它们,它们称为无人托管的pod。如果更改了pod模板,该模板仅影响由rc创建的新的pod。
创建一个ReplicationController
下面就来创建一个ReplicationController资源:
[root@master ~]# vim rc_nginx.yaml
apiVersion: v1
kind: ReplicationController #创建一个ReplicationController控制器
metadata:
name: rc-nginx #控制器名称
namespace: default #命名空间
spec:
replicas: 3 #表示pod副本数
selector: #定义标签选择器,用于说明rc控制管理的是带有哪些标签的pod
app: nginx #标签app=nginx
template: #定义pod模板,pod模板就是用来创建pod
metadata:
labels: #pod标签,注意:pod标签必须与上面标签选择器定义的标签相同,否者rc就不知道如何关联pod
app: nginx
spec:
containers:
- image: nginx:1.7.9
name: nginx-container
ports:
- containerPort: 80
[root@master ~]# kubectl apply -f rc_nginx.yaml #应用yaml文件
[root@master ~]# kubectl get rc,pod --show-labels #查看rc和pod
NAME DESIRED CURRENT READY AGE LABELS
replicationcontroller/rc-nginx 3 3 3 43m app=nginx
NAME READY STATUS RESTARTS AGE LABELS
pod/rc-nginx-4drqr 1/1 Running 0 43m app=nginx
pod/rc-nginx-cn8wv 1/1 Running 0 43m app=nginx
pod/rc-nginx-qwk9j 1/1 Running 0 43m app=nginx
[root@master ~]#
1、在创建replicationcontroller时,pod模板中的标签必须要与replicationcontroller的标签选择器匹配,否则rc将无休止的创建pod;
2、可以不写标签选择器,但模板中必须写标签,这时replicationcontroller会根据pod模板中的标签自动匹配。
pod扩容、缩容
使用控制器管理的pod可以很方便的实现扩容和缩容,其原理就是通过变更控制器的副本数,如下所示:
1、通过kubectl scale 对rc控制器的pod进行扩容
[root@master ~]# kubectl scale rc rc-nginx --replicas=5 #通过kubectl scale命令对rc直接变更副本数实现扩容缩容
2、通过kubectl edit 命令直接rc的配置文件中的副本数
[root@master ~]# kubectl edit rc rc-nginx #直接编辑rc的定义中的副本数实现扩容缩容
删除replicationcontroller
当不在需要replicationcontroller时,可以删除replicationcontroller,删除replicationcontroller默认情况会把pod一起删掉,如果仅想删除replicationcontroller,仍想保留pod,可以使用–cascade=orphan,此时的pod就处于未托管状态;
[root@master ~]# kubectl delete rc rc-nginx #replicationcontroller和pod会被一起删除
[root@master ~]# kubectl delete rc rc-nginx --cascade=orphan #仅删除replicationcontroller,pod保留
以上就是replicationcontroller,注意:replicationcontroller已经被淘汰,所以你在创建pod控制器时不应该选择创建replicationcontroller了,下面来介绍ReplicaSet。
ReplicaSet是Replication Controller的下一代副本控制器,目前两者只在标签选择器支持的查找方式有区别,在新版本的 Kubernetes 中使用 ReplicaSet(简称为RS )来取代 ReplicationController。ReplicaSet 跟 ReplicationController 没有本质的不同,语法定义都一样,但是 replicaset 支持标签选择器等式查找与集合查找两种方式。比如rs允许匹配缺少某个标签的pod,或包含特定标签名的pod而不管其标签值如何,ReplicationController就无法做到这些,rc进支持等值匹配的标签。
[root@master ~]# cat rs_nginx.yaml
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: rs-nginx
namespace: default
spec:
replicas: 3
selector:
matchLabels:
app: nginx-rs
matchExpressions:
- {key: env, operator: In, values: [dev]}
template:
metadata:
labels:
app: nginx-rs
env: dev
spec:
containers:
- image: nginx:1.7.9
name: nginx-container
ports:
- containerPort: 80
以上只对ReplicaSet做了简单介绍,实际上kubernetes官方推荐不要直接使用ReplicaSet,用Deployments取而代之,Deployments是比ReplicaSet更高级的概念,它会管理ReplicaSet并提供很多其它有用的特性,最重要的是Deployments支持声明式更新,声明式更好相比于命令式更新的好处是不会丢失历史变更。总结起来就是:不要再直接使用ReplicaSet。
删除ReplicaSet
[root@master ~]# kubectl delete rs rs-nginx-http --cascade=orphan #删除rs,保留pod
[root@master ~]# kubectl delete rs rs-nginx-http #删除rs所有,含pod
DaemonSet 确保全部(或者一些)Node 上运行一个 Pod 的副本。当有 Node加入集群时,也会为他们新增一个 Pod 。当有 Node 从集群移除时,这些 Pod也会被回收。删除 DaemonSet将会删除它创建的所有 Pod。DaemonSet 简写为ds。
使用 DaemonSet 的一些典型用法:
运行集群存储 daemon,例如在每个 Node 上运行 glusterd 、 ceph
在每个 Node 上运行日志收集 daemon,例如 fluentd 、 logstash
在每个 Node 上运行监控 daemon,例如 Prometheus Node Exporter、 collectd 、Datadog 代理、New Relic 代理,或 Ganglia gmond
编写一个DaemonSet
[root@master ~]# vim ds.yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: nginx-ds
namespace: default
spec:
selector:
matchLabels:
app: nginx
release: stable
template:
metadata:
labels:
app: nginx
release: stable
spec:
containers:
- name: nginx
image: nginx:1.14
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 80
[root@master ~]# kubectl apply -f ds.yaml #应用yaml文件创建资源
[root@master ~]# kubectl get pod -o wide #查看pod,发现已经有2个pod,分别位于各个节点上
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-ds-5qnxs 1/1 Running 0 4m10s 10.244.2.22 node2 <none> <none>
nginx-ds-l7kjk 1/1 Running 0 4m10s 10.244.1.40 node1 <none> <none>
再次说明:DaemonSet创建的pod只会在每个节点运行1个,也就是说你不用在创建DaemonSet的yaml文件中指定副本数,没必要;当每个节点的DaemonSet管理的pod被删除或者pod异常退出时,DaemonSet控制器又会重新创建一个pod,因为DaemonSet会实时监管每个节点确保存在一个pod运行。
查看daemonset
[root@master ~]# kubectl get daemonset -n default #查看集群的DaemonSet
NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
nginx-ds 2 2 2 2 2 <none> 14m
删除daemonset
[root@master ~]# kubectl delete pod nginx-ds-5qnxs #删除某个节点上的pod,ds又会重新创建一个pod
pod "nginx-ds-5qnxs" deleted
[root@master ~]# kubectl delete daemonset nginx-ds #删除ds,其创建的pod都被删除
daemonset.apps "nginx-ds" deleted
[root@master ~]#
有时候我们可能需要一种这样的pod控制器,即该控制器能实现pod执行完任务就结束,不需要重启或重建pod,相当于一次性任务。这就可以使用job控制器。(可以简单的理解为,Job相当于Linux中的at一次性计划任务)
创建job
[root@master ~]# cat Job.yaml #编写一个job资源,任务为睡眠120s
apiVersion: batch/v1 #job属于batch API组,版本为v1
kind: Job #资源类型
metadata:
name: busybox-job #job的名称叫busybox-job
namespace: default #命名空间
spec:
template: #pod模板
metadata:
labels: #pod标签
app: busybox
env: dev
spec:
restartPolicy: OnFailure #pod重启策略,默认为Always,但一定不能是Always,因为job的策略就只是一次性执行,执行完不需要重启,可以设置为OnFailure或Never
containers:
- name: busybox
image: busybox:latest
imagePullPolicy: IfNotPresent #镜像拉取策略,默认就是IfNotPresent,不存在才拉取
command: ["/bin/sh","-c","sleep 120s"] #容器的任务为睡眠120s
[root@master ~]#
[root@master ~]# kubectl create -f Job.yaml #创建job
job.batch/busybox-job created
查看job
[root@master ~]# kubectl get pod,job #查看job和pod
NAME READY STATUS RESTARTS AGE
pod/busybox-job--1-2q87r 1/1 Running 0 6s
NAME COMPLETIONS DURATION AGE
job.batch/busybox-job 0/1 6s 6s
#等待120s后再次查看pod,发现pod状态变成Completed,表示pod已经执行完成了
[root@master ~]# kubectl get pod #pod状态变成Completed
NAME READY STATUS RESTARTS AGE
busybox-job--1-2q87r 0/1 Completed 0 3m29s
job还可以设计为运行多个pod实例,换句话来说就是创建一个job,顺序或者并行的运行多个pod,如下演示:
顺序运行 Job pod
假设需要一个job顺序运行多次,那么可以指定completions参数来设置希望job的pod运行多少次,所下代码所示:
[root@master ~]# cat Job.yaml
apiVersion: batch/v1
kind: Job
metadata:
name: busybox-job
namespace: default
spec:
completions: 3 #表示运行3次
template:
metadata:
labels:
app: busybox
env: dev
spec:
restartPolicy: OnFailure
containers:
- name: busybox
image: busybox:latest
imagePullPolicy: IfNotPresent
command: ["/bin/sh","-c","sleep 20s"]
[root@master ~]#
[root@master ~]# kubectl get pod #查看pod,是第一个运行完成之后才创建第2个,依次运行
NAME READY STATUS RESTARTS AGE
busybox-job--1-9f7bs 0/1 Completed 0 4m39s
busybox-job--1-mdqkb 0/1 Completed 0 4m18s
busybox-job--1-s98wc 0/1 Completed 0 5m
并行运行 Job pod
不必一个接着一个运行单个Job pod,也可以让Job 并行运行多个pod,可以通过parallelism Job配置属性,指定允许多个pod并行执行,如下代码所示:
[root@master ~]# vim Job.yaml
apiVersion: batch/v1
kind: Job
metadata:
name: busybox-job
namespace: default
spec:
completions: 4
parallelism: 2 #指定可以并行运行多少个pod
template:
metadata:
labels:
app: busybox
env: dev
spec:
restartPolicy: OnFailure
containers:
- name: busybox
image: busybox:latest
imagePullPolicy: IfNotPresent
command: ["/bin/sh","-c","sleep 20s"]
[root@master ~]# kubectl apply -f Job.yaml && kubectl get pod #查看job和pod,发现已经在并行运行2个pod了
job.batch/busybox-job created
NAME READY STATUS RESTARTS AGE
busybox-job--1-c8jb5 0/1 ContainerCreating 0 0s
busybox-job--1-lsrbv 0/1 ContainerCreating 0 0s
注意:如果restartPolicy:设为OnFailure,就表示pod出现异常时可以重新创建pod,这就意味着completions定义为n次,但实际job创建的pod可能会大于n。
限制Job pod完成任务的时间
通过pod配置中设置activeDeadlineSeconds属性,可以限制pod的运行时间,如果pod运行时间超过次时间,系统将尝试终止pod,并将job标记为失败。
通过指定Job资源清单中的spec.backoffLimit字段,可以配置Job在被标记为失败之前可以重试的重试,如果为指定,默认为6次。
删除Job
默认执行完毕的Job 的pod为Completed 状态,k8s并不会删除pod,是为了可以让你查看日志,如果需要删除Job,直接kubectl delete job Job_name即可,如下所示:
[root@master ~]# kubectl delete job busybox-job #删除job,其pod也会被删除
job.batch "busybox-job" deleted
如果说Job相当于Linux中的at一次性激活任务,那么CronJob就相当于Linux中的Crontab周期性定时任务。Job与CronJob的区别在于,Job在创建时就运行,而CronJob在创建后会定时的运行,在k8s集群中的cron任务就是通过CronJob资源进行实现的。
在配置的时间里,k8s将根据在CronJob对象中配置的Job模板创建Job资源。
cron表达式格式与Linux中cron的表达式一样,格式为:分 时 日 月 周
创建一个CronJob
[root@master ~]# vim CronJob.yaml
apiVersion: batch/v1
kind: CronJob #创建CronJob
metadata:
name: busyboy
namespace: default
spec:
schedule: "*/5 * * * *" #schedule字段指定定时任务:分时日月周
startingDeadlineSeconds: 15 #表示最迟在预定时间后15秒启动pod
jobTemplate: #任务模板,用来指定需要运行的任务
spec:
template:
metadata:
labels:
app: CronJob
env: dev
spec:
restartPolicy: OnFailure
containers:
- name: busybox
image: busybox:latest
imagePullPolicy: IfNotPresent
command: ["/bin/sh","-c","sleep 20s"]
[root@master ~]#
查看CronJob、查看pod
[root@master ~]# kubectl get cronjob #查看CronJob
NAME SCHEDULE SUSPEND ACTIVE LAST SCHEDULE AGE
busyboy */5 * * * * False 0 4m12s 19m
[root@master ~]#
[root@master ~]# kubectl get pods #每隔5分钟就会产生一个pod
NAME READY STATUS RESTARTS AGE
busyboy-27416295--1-k5t5h 0/1 Completed 0 5m23s
busyboy-27416300--1-qs7mt 0/1 Completed 0 23s
删除CronJob
默认执行完毕的CronJob的pod为Completed 状态,k8s并不会删除pod,是为了可以让你查看日志,如果需要删除CronJob,直接kubectl delete cronjob cronjob_name即可,如下所示:
[root@master ~]# kubectl delete cronjob busyboy #删除job,其pod也会被删除
cronjob.batch "busyboy" deleted