Kubernetes笔记(5) - Pod控制器
自主式Pod对象由调度器绑定至目标工作节点后即由相应节点上的kubelet负责监控其容器的存活性,容器主进程崩溃后,kubelet能够自动重启相应的容器,基于存活性探测,在容器出现其他问题时也能作出响应,但如果Pod被意外删除、或者工作节点发生故障,kubelet就无能为力了。
Pod控制器可应对这类情况。Pod控制器由master的kube-controller-manager组件提供。kube-controller-manager是一个独立的单体守护进程,它包含了众多功能不同的控制器,除了Pod Controller,还有NodeLifecycle Controller、Namespace Controller、Service Controller等等。这些控制器不间断地监控着由其负责的资源,并在因故障、更新或其他原因导致系统状态发生变化时,尝试让资源的当前状态向期望状态迁移和逼近。
ReplicaSet控制器
ReplicaSet替代了早期的ReplicationController,用于确保由其管控的Pod对象副本数在任一时刻都能精确满足期望的数量
通过控制器创建的Pod与用户手动创建的Pod功能相同,但相比于手动创建来说,ReplicaSet能够实现以下功能:
- 确保Pod资源对象的数量精确反映期望值
- 确保Pod健康运行,在探测到由其管控的Pod对象因其所在的工作节点故障而不可用时,自动请求由调度器于其他工作节点创建缺失的Pod副本
- 弹性伸缩:在资源需求存在较大波动时,可以通过ReplicaSet控制器动态调整相关Pod资源对象的数量。还可以配合HPA(HroizontalPodAutoscaler)实现Pod资源规模的自动伸缩。
创建ReplicaSet
通常一个Pod控制器的资源清单的spec包含如下基本属性:
- selector:标签选择器,匹配并关联Pod资源对象,并据此完成受其管控的Pod资源计数
- replicas:期望的副本数,期望在集群中精确运行着的Pod资源的对象数量
- template:Pod模板,用于新建Pod资源对象的Pod模板资源
例如:
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: rs-example
spec:
replicas: 2
selector:
matchLabels:
app: rs-demo
template:
metadata:
labels:
app: rs-demo
spec:
containers:
- name: myapp
image: ikubernetes/myapp:v2
imagePullPolicy: Never
ports:
- name: http
containerPort: 80
protocol: TCP
将这份配置清单apply后,使用kubectl get pods -l app=rs-demo
查看,会看到有两个容器,状态从ContainerCreating转为Running,两个pod名称以控制器的名称rs-example为前缀,rs-example-j98fp、rs-example-wj8zg。
运行kubectl get replicaset rs-example
,配合-o json\wide
等选项可查看控制器的详细状态。
ReplicaSet管控下的Pod对象
ReplicaSet之所以能对Pod对象数目的异常及时做出响应,是因为它向API Server注册监听了相关资源及其列表的变动信息,于是API Server会在变动发生时立即通知给监听方。
缺少、多出Pod副本
任何原因导致的相关Pod对象丢失,都会由ReplicaSet控制器自动补足。
手动删除一个Pod,ReplicaSet马上会新建一个;之前创建的ReplicaSet依赖标签app: rs-demo
来管理Pod,那么强制修改这个标签也会触发副本的新建:
kubectl label pods rs-example-wj8zg app=others --overwrite
之前的Pod rs-example-wj8zg还存在,如果它能被别的控制器通过标签选择器选中,就会隶属于这个控制器,否会成为自助式Pod。
多余的部分则会被控制器自动删除,经测试Age最小的会被首先删除。
kubectl describe replicasets/rs-example
命令可打印出ReplicaSet的详细状态以及Events。
更新ReplicaSet
更改template:升级应用
更改template对已经创建完成的活动对象无效,但可以逐个手动关闭其旧版本的Pod资源来实现滚动升级。
相比于ReplicaSet的手动操作方式,Deployment控制器能够自动实现更完善的滚动更新和回滚,并为用户提供自定义更新策略的接口。
更改replicas:扩容和缩容
通过修改replicas属性并apply,可实现应用规模的水平伸缩;kubectl还提供了一个专用的子命令scale用于实现应用规模的伸缩,比如:
kubectl scale replicasets rs-example --replicas=2
使用--current-replicas选项还可实现在副本数量为指定值时才变更replicas,比如下面的命令,如果当前副本数量不等于4就不会执行成功:
kubectl scale replicasets rs-example --current-replicas=4 --replicas=2
如果让ReplicaSet管控有状态应用,例如主从架构的Redis集群,那么上述这些升降级、扩缩容操作都需要精心编排和参与才能进行,针对这类需求,可以使用K8S专门提供的StatefulSet,ReplicaSet通常仅用于管理无状态的应用,如HTTP服务程序等。
删除ReplicaSet资源
删除命令:
kubectl delete -f ...
或者
kubectl delete replicaset ...
删除ReplicaSet对象时默认会一并删除其管控的各Pod对象。但有时考虑到这些Pod资源未必由其创建,或者Pod资源后续可能会再次用到时,可以添加--cascade=false
选项取消级联删除。
Deployment控制器
Deployment控制器构建于ReplicaSet控制器之上,可为Pod和ReplicaSet资源提供声明式更新。Pod和ReplicaSet是较低级别的资源,很少被直接使用。
Deployment控制器在ReplicaSet的基础上,添加了多种增强的功能:
- 事件和状态查看:必要时可以查看Deployment对象升级的详细进度和状态
- 回滚:在升级发现问题时,可以使用回滚机制将应用返回到前一个或由指定的历史版本
- 版本记录:保存每一次操作记录,以供后续可能执行的回滚操作使用
- 暂停和启动:对于每一次升级,都能够随时暂停和启动
- 多种自动更新方案:
- Recreate,重建更新机制,全面停止、删除旧有的Pod后用新版本替代
- RollingUpdate,滚动升级机制,逐步替换旧有的Pod至新的版本。
创建Deployment
Deployment将ReplicaSet对象作为其二级资源,配置清单中除了kind、name,其它字段与ReplicaSet的相同,示例:
kind: Deployment
metadata:
name: myapp-deploy
创建完成后可用kubectl get deployments myapp-deploy
查看其状态:
NAME READY UP-TO-DATE AVAILABLE AGE
myapp-deploy 2/2 2 2 15s
UP-TO-DATE表示已经达到期望状态的Pod副本数量,AVAILABLE则表示当前处于可用状态的应用程序的数量。
查看相关的ReplicaSet资源:
~ kubectl get replicasets -l app=myapp
NAME DESIRED CURRENT READY AGE
myapp-deploy-7cfbdc886d 2 2 2 2m53s
ReplicaSet资源的命名格式为[DEPLOYMENT-NAME]-[POD-TEMPLATE-HASH-VALUE]
查看相关的Pod资源:
~ kubectl get pods -l app=myapp
NAME READY STATUS RESTARTS AGE
myapp-deploy-7cfbdc886d-8lw6f 1/1 Running 0 4m55s
myapp-deploy-7cfbdc886d-xn7xp 1/1 Running 0 4m55s
Pod资源的名称以ReplicaSet资源的名称为前缀,加5位随机字符。
更新策略
Deployment控制器支持RollingUpdate和Recreate两种更新策略,默认为滚动更新。
Recreate
类似于使用ReplicaSet时一次性删除全部Pod对象,而后由控制器基于新模板重新创建出新版本资源对象。这种方式通常只应该在应用的新旧版本不兼容(如依赖的后端数据库的schema不同且无法兼容)时才使用,因为它会导致应用替换期间暂时不可用,但好处在于它不存在中间状态。
RollingUpdate
滚动升级是在删除一部分旧版本Pod资源的同时,补充创建一部分新版本的Pod对象,使用这种方式时,容器中应用提供的服务不会中断,但要求应用程序能够应对新旧版本同时工作的情形,例如新旧版本兼容同一个数据库方案等。
滚动升级时会同时存在新旧版本的ReplicaSet,旧版ReplicaSet的Pod对象数量不断减少的同时,新版ReplicaSet的Pod对象数量不断增加,直到旧版ReplicaSet不再拥有Pod对象,而新版ReplicaSet的副本数量变得完全符合预期。
升级期间还要确保可用的Pod对象数量不低于某阈值以确保可以持续处理客户端的服务请求,变动的方式和Pod对象的数量范围将通过spec.strategy.rollingUpdate下的maxSurge和maxUnavailable两个属性协同定义:
- maxSurge:升级期间存在的总Pod对象数量最多可超出期望值的个数,其值可以是0或正整数,也可以是一个期望值的百分比
- maxUnavailable:升级期间正常可用的Pod副本数(包括新旧版本)最多不能低于期望数值的个数,其值可以是0或正整数,也可以是一个期望值的百分比,默认值为1。
maxSurge和maxUnavailable相当于定义了pod数量的波动范围,所以它们的值不可同时为0,两者作用方式如下图举例:
此外,还可以设置spec.minReadySeconds属性:
- ,控制应用升级的速度,滚动升级时默认新的Pod对象只要就绪探测成功就可用,于是开始开始下一轮的替换操作。而minReadySeconds能够定义在新的Pod对象创建后至少要等待多久才会将其视作就绪,在此期间,更新操作会被阻塞。
升级Deployment
修改Pod模板相关的配置参数便能完成Deployment控制器资源的更新,可以通过apply和patch命令来修改;如果只是修改容器镜像,可以直接使用set image
命令。
为了使得升级过程更易于观测,先使用“kubectl patch”命令指定minReadySeconds=6s:
kubectl patch deployments myapp-deploy -p '{"spec":{"minReadySeconds":6}}'
修改Deployment控制器的minReadySeconds、replicas和strategy等字段的值并不会触发Pod资源的更新操作,因为它们不会对现存的Pod对象不产生任何影响。
使用set image
命令更新镜像版本:
kubectl set image deployments myapp-deploy myapp=ikubernetes/myapp:v2
随后使用如下命令可以观察滚动升级的过程:
kubectl rollout status deployments myapp-deploy --watch
升级完成后,旧版本的ReplicaSet控制器会保留在历史记录中,但其此前的管控Pod对象将会被删除。
金丝雀发布
Deployment控制器支持更新操作的暂停、继续,再结合maxSurge和maxUnavailable,可以实现金丝雀发布(Canary Release),即待第一批新的Pod资源创建完成后立即暂停更新过程,此时,仅存在一小部分新版本的应用,主体部分还是旧的版本。然后将一小部分流量导到新版本的Pod应用,并持续观察其是否能稳定地按期望的方式运行,确定没有问题后再继续完成余下Pod资源的滚动更新,否则立即回滚更新操作。
为了尽可能地降低对现有系统的影响,金丝雀发布过程通常建议采用“先添加、再删除,且可用Pod资源对象总数不低于期望值”的方式进行,首次添加的Pod对象数量取决于其接入的第一批请求的规则及单个Pod的承载能力。
接下来的试验中将maxSurge和maxUnavailable分别设置为1和0,并通过修改升级镜像版本来触发更新,但要在第一批更新启动后就暂停,由于设置了minReadySeconds,可以在这个过程中发出暂停命令,对于kubectl来说,也可以直接以“&&”符号在Shell中连接两个命令:
kubectl set image deployments myapp-deploy myapp=ikubernetes/myapp:v2 --record=true&& kubectl rollout pause deployments myapp-deploy
查看状态可知更新操作已经暂停:
kubectl rollout status deployments myapp-deploy
此时,通过Service或Ingress资源及相关路由策略等设定,即可将一部分用户的流量引入到这些Pod之上进行发布验证,确认没有问题后即可继续更新:
kubectl rollout resume deployments myapp-deploy
回滚
如果因各种原因导致滚动更新无法正常进行,如镜像文件获取失败、“金丝雀”遇险等,则应该将应用回滚到之前的版本或者指定的历史记录中的版本,直接回滚至上一版本:
kubectl rollout undo deployments myapp-deploy
加上--to-revision选项可以回滚至指定版本,如下命令可以查看保存的历史版本:
kubectl rollout history deployments myapp-deploy
需要注意的是,只有回滚至被保存的历史版本,spec.revisionHistoryLimit定义了保存的版本数量,默认为10个。另外rollout history显示的CHANGE-CAUSE一列的内容默认为空,在执行命令时加上--record=true选项,可以记录触发更新的命令的内容。此外,处于暂停状态的更新无法回滚。
扩容、缩容
直接修改配置清单中的spec.replicas并apply可以改变Pod资源的副本数量,也可以使用专用的命令kubectl scale
:
kubectl scale [--resource-version=version] [--current-replicas=count] --replicas=COUNT (-f FILENAME | TYPE NAME)
比如在当前Pod副本数为4个时,调整为2个:
kubectl scale --current-replicas=4 --replicas=2 deployment/myapp-deploy
DaemonSet控制器
DaemonSet用于在集群中的全部或指定节点上同时运行一份指定的Pod资源副本,后续新加入集群的工作节点也会自动创建一个相关的Pod对象,当从集群移除节点时,此类Pod对象也将被自动回收而无须重建。
DaemonSet是一种特殊的控制器,它有特定的应用场景,通常运行那些执行系统级操作任务的应用,如集群存储的守护进程、日志收集守护进程、监控系统的代理守护进程等。
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: daemonset-demo
spec:
selector:
matchLabels:
app: daemonset-pod
template:
metadata:
labels:
app: daemonset-pod
spec:
nodeSelector:
kubernetes.io/hostname: docker-desktop
containers:
- name: myapp
image: ikubernetes/myapp:v1
imagePullPolicy: Never
Job控制器
Job控制器用于调配Pod对象运行一次性任务,容器中的进程在正常运行结束后,Pod对象会被置于Completed状态。Job控制器的配置清单相对简单:
apiVersion: batch/v1
kind: Job
metadata:
name: job-example
spec:
completions: 5
parallelism: 5
template:
metadata:
labels:
app: job1
spec:
restartPolicy: Never
containers:
- name: job1
image: alpine
command: ["/bin/sh", "-c", "sleep 5"]
Pod模板中的spec.restartPolicy默认为“Always”,但对Job控制器来说只支持“Never”或“OnFailure”,因此必须显式设定restartPolicy属性的值。
apply资源清单后,job便开始执行,如果顺利执行完毕,会处于Succeed状态,job资源会被自动添加标签job-name='name',如job-name=job-example,可据此使用标签选择器查看job状态
串行、并行控制
spec.completions表示总任务数,spec.parallelism表示并行度,默认都是1,所以job在执行一次后即完成,如果parallelism设为1,completions设为5,则job会以串行的方式运行5次,如果parallelism也设为5,则会并行运行5个job实例。如果completions大于parallelism,Job控制器就会以串行方式运行多任务。
可以使用--watch选项监控job的运行状态
kubectl get jobs -l job-name=job-example --watch
删除Job
Job控制器待其Pod资源运行完成后,将不再占用系统资源。用户可按需保留或使用资源删除命令将其删除。但如果某Job控制器的容器应用总是无法正常结束运行,而其restartPolicy又定为了重启,则它可能会一直处于不停地重启和错误的循环当中。下面两个属性用于抑制这种情况的发生:
spec.activeDeadlineSeconds,用于指定job的最大执行时长,超出将被终止;
spec.backoffLimit,最大重试次数,超出后被标记为失败,默认值为6。
activeDeadlineSeconds要比backoffLimit优先级高,如果时间到了,但是backoffLimit还未到,该Job也会被强制停止。
CornJob控制器
CronJob控制器用于管理Job控制器资源的运行时间。Job控制器定义的作业任务在其控制器资源创建之后便会立即执行,但CronJob可以以类似于Linux操作系统的周期性任务作业计划(crontab)的方式控制其运行的时间点及重复运行的方式。
apiVersion: batch/v1beta1
kind: CronJob
metadata:
name: cronjob-example
labels:
app: cronjob1
spec:
schedule: "* * * * *"
jobTemplate:
metadata:
labels:
app: cronjob1-job
spec:
template:
spec:
restartPolicy: Never
containers:
- name: job1
image: alpine
command: ["/bin/sh", "-c", "date; echo Hello from the kubernetes cluster; sleep 1"]
这个cronjob没分钟运行一次,schedule的语法可参照这里,schedule的时间基于 kube-controller-manager的时区。
任务启动一段时间后,可使用log命令查看pod输出:
kubectl logs cronjob-example-1620082920-vg7p8
Pod中断预算
Pod中断可大体分为两种:
- 非自愿中断,是指那些由不可控外界因素导致的Pod中断退出操作,例如,硬件或系统内核故障、网络故障以及节点资源不足导致Pod对象被驱逐等;
- 自愿中断,是指那些由用户特地执行的管理操作导致的Pod中断,例如排空节点、人为删除Pod对象、由更新操作触发的Pod对象重建等。
尽管Deployment或ReplicaSet一类的控制器能够确保相应Pod对象的副本数量不断逼近期望的数量,但它却无法保证在某一时刻一定会存在指定数量或比例的Pod对象,然而这种需求在某些强调服务可用性的场景中却是必备的。
Pod中断预算(PodDisruptionBudget,PDB)可用于为自愿中断做好预算方案(Budget),限制可自愿中断的最大Pod副本数或确保最少可用的Pod副本数,以确保服务的高可用性。
定义PDB资源时,spec字段主要嵌套使用以下三个字段:
- selector,当前PDB对象使用的标签选择器,一般是与相关的Pod控制器使用同一个选择器。
- minAvailable,Pod自愿中断的场景中,至少要保证可用的Pod对象数量或比例,要阻止任何Pod对象发生自愿中断,可将其设置为100%
- maxUnavailable,Pod自愿中断的场景中,最多可转换为不可用状态的Pod对象数量或比例,0值意味着不允许Pod对象进行自愿中断,此字段与minAvailable互斥。
apiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
name: pdb1
spec:
minAvailable: 2
selector:
matchLabels:
app: myapp
学习资料
《Kubernetes实战进阶》 马永亮著