1、什么是控制器
Kubernetes 中内建了很多 controller(控制器),这些控制器相当于一个状态机,用来控制 Pod 的具体状态和行为
自主式Pod
:如果Pod意外退出,这个类型的Pod不会被重新拉起,就相当于没有管理者
控制器管理的Pod
:在控制器管理的生命周期下,Pod的副本数始终会被控制器所维持着
控制器类型
:# ReplicationController 和 ReplicaSet # Deployment # DaemonSet # StateFulSet # Job/CronJob # Horizontal Pod Autoscaling
ReplicationController(退役选手) 和 ReplicaSet
:# ReplicationController(RC)用来确保容器应用的副本数始终保持在用户定义的副本数 # 即如果有容器异常退出,会自动创建新的 Pod 来替代;而如果异常多出来的容器也会自动回收; # 在新版本的 Kubernetes 中建议使用 ReplicaSet 来取代 ReplicationController 。 # ReplicaSet 跟ReplicationController 没有本质的不同,只是 ReplicaSet 支持集合式的 selector
Depoyment
:Deployment 为 Pod 和 ReplicaSet 提供了一个声明式定义 (declarative) 方法,用来替代以前的 RC 来方便的管理应用。 # 简单提一嘴先 # 声明式编程 声明式编程最经典的代表就是SQL语句,我们不需要知道计算机内部是怎么实现的,我们只用定义语句,比如创建数据库create table ishells; 只用告诉它我要创建一个数据库,DBMS就会理解我们的请求,作为用户来说,我们根本不用管内部是怎么实现的。 # 命令式编程: 它侧重于如何实现程序,强调一步一步按照逻辑实现目的 Deployment典型的应用场景包括 # 定义 Deployment 来创建 Pod 和 ReplicaSet # 滚动升级和回滚 # 应用扩容和缩容 # 暂停和继续 Deploymen
Deployment是通过管理Rs来管理Pod
当实现滚动更新的时候,用户将新的代码封装到镜像中并修改Deployment的image,提交了更新请求后,Deployment会删除一个旧版本的Pod,创建一个新版本的Pod,旧版本的RS并不会删除,如果需要回滚还需要用到旧版本的RS。而且,滚动更新和回退的时候,每次替换Pod的数目是可以自定义的,比如一次更新百分之多少的Pod、多少个Pod
DaemonSet
:DaemonSet确保全部(或者一些)Node 上运行一个指定 Pod 的副本。当有 Node 加入集群时,也会为他们新增一个Pod 。当有 Node 从集群移除时,这些 Pod 也会被回收。删除 DaemonSet 将会删除它创建的所有 Pod
使用 DaemonSet 的一些典型用法: # 运行集群存储 daemon,例如在每个 Node 上运行glusterd、ceph # 在每个 Node 上运行日志收集 daemon,例如fluentd、logstash # 在每个 Node 上运行监控 daemon,例如Prometheus Node Exporter、collectd、Datadog 代理、New Relic # 代理,或 Ganglia gmond
Job
:Job负责批量处理短暂的一次性任务 (short lived one-off tasks) 即仅执行一次的任务,它保证批处理任务的一个或多个Pod成功结束。 Kubernetes支持以下几种Job: # 非并行Job:通常创建仅一个Pod直至其成功结束 # 固定结束次数的Job:设置.spec.completions,创建多个Pod,直到.spec.completions个Pod成功结束 # 带有工作队列的并行Job:设置.spec.Parallelism但不设置.spec.completions,当所有Pod结束并且至少一个成功时,Job就认为是成功 (简单来说,.spec.completions设定成功执行退出的次数,.spec.Parallelism指定并行运行的Pod个数,根据这俩参数会出现下面的几种情况)
根据.spec.completions和.spec.Parallelism的设置,可以将Job划分为以下几种pattern:
Job类型 使用示例 行为 completions Parallelism 一次性Job 数据库迁移 创建一个Pod直至其成功结束 1 1 固定结束次数的Job 处理工作队列的Pod 依次创建仅一个Pod运行直至completions次数成功结束 2+ 1 固定结束次数的并行Job 多个Pod同时处理工作队列 依次创建多个Pod运行直至completions次数成功结束 2+ 2+ 并行Job 多个Pod同时处理工作队列 创建一个或多个Pod直至有一个成功结束 1 2+
CronJob
:Cron Job管理基于时间的 Job,即: # 在给定时间点只运行一次 # 周期性地在给定时间点运行 # 使用CronJob的k8s集群版本需要>=1.8 典型的用法如下所示: # 在给定的时间点调度 Job 运行 # 创建周期性运行的 Job,例如:数据库备份、发送邮件
StatefulSet
:StatefulSet 作为 Controller 为 Pod 提供唯一的标识。它可以保证部署和 scale 的顺序
StatefulSet是为了解决有状态服务的问题(对应Deployments和ReplicaSets是为无状态服务而设计),其应用场景包括:
✦
稳定的持久化存储,即Pod重新调度后还是能访问到相同的持久化数据,基于PVC来实现
✦
稳定的持久化存储就是,被StatefulSet管理的Pod,可以使用相同或不同的存储卷,当pod因故死亡后,为了维持副本数,会重新创建新的Pod,并且会连接到之前使用的存储卷,也就是持久化数据不会丢失
✦
稳定的网络标志,即Pod重新调度后其PodName和HostName不变,基于Headless Service(即没有Cluster IP的Service)来实现
✦
有序部署,有序扩展,即Pod是有顺序的,在部署或者扩展的时候要依据定义的顺序依次依次进行(即从0到N-1,在下一个Pod运行之前所有之前的Pod必须都是Running和Ready状态),基于init containers来实现
✦
有序收缩,有序删除(即从N-1到0)
Horizontal Pod Autoscaling
:应用的资源使用率通常都有高峰和低谷的时候,如何削峰填谷,提高集群的整体资源利用率,让service中的Pod个数自动调整呢?HPA就是实现这种水平伸缩的控制器, 它能在当POD中业务负载上升的时候,创建新的POD来保证业务系统稳定运行,当POD中业务负载下降的时候,可以销毁POD来提高资源利用率。 HPA其实更像是控制器的附属品,也就是说HPA是通过管理控制器而达到控制pod的目的,HPA是根据一些指标来实现自动扩容缩放的,比如CPU、内存啊这些信息(比方说cpu使用到达到80了,那就扩容到10个节点,cpu小于40了,那就缩放到4个节点),而k8s本身并不支持这些数据收集的功能,所以需要借助一些其他的资源收集方案,它来向HPA提供这些性能指标,然后HPA根据这些指标来做调整。 问题: 为什么k8s没有提供收集指标的方案?
什么场景使用什么控制器呢?
也就意味着遇倒批处理级别的任务时,可以选择`Job`,`Cronjob` # 1、遇到需要在多节点运行守护进程的任务时,我们可以选择DaemonSet # 2、遇到需要滚动更新、回滚的任务时,可以选择Rs和Deployment # 3、那我们也知道,容器最适合的处理场景还是无状态服务,那么遇到无状态服务任务的时候,Docker中使用了存储卷的形式来应对无状态服务的应用场景,那在k8s中应该怎么做呢? # 3.1 那就是StatefulSet,由StatefulSet所管理的Pod都会连接到同一个或多个数据卷,当Pod因为某些原因down掉,重启了,新起来的pod仍然会连接到原来的数据卷,这样其实就实现了数据的持久化存储方案, # 3.2 很多服务其实都是基于pod name或Host name进行连接的,而Pod因故重启之后name发生改变了,就需要根据新的pod name或host name重新进行建立连接关系,那这对后续的自动化解决方案肯定是很不友好的。而StatefulSet就可以确保它所管理的Pod在其生命周期中pod name 和 host name 完全不变 # 4、如果我们有一些类似于脚本的功能需要去实现的时候,就可以用到Job、CronJob了,当我们在linux中使用crontab去周期性的执行1个脚本的时候,我们是不是会有这样的忧虑,万一脚本哪里出了错误怎么办?脚本本身如果不具有纠错能力,crontab更不具有这样的能力。 # 4.1 而我们使用job的时候就完全不必有这样的忧虑,job更倾向于`脚本运行管理机制`这样的一个管理方案去管理Pod,`Job`会检查脚本运行是否正常退出了(即返回状态码是否为0),如果没有正常退出就重新执行,job管理Pod时可以`指定一个成功退出的次数`,比如指定Pod执行脚本成功退出次数为2,那么当Pod成功执行一次脚本,次数就从0开始加1,当Pod的脚本成功执行2次,job就会结束退出,也就意味着`job的生命周期就是指定的成功退出次数` # 4.2 而当需要定时、周期性的执行Job的时候,就需要用到另一个控制管理器了,`Crontab`,并且它的工作原理其实就是在特定的时间循环创建`Job` 其实也就意味着遇到批处理级别的任务时,可以选择`Job`,`Cronjob`
为什么有序部署不是基于start、stop命令,而是init容器?
以为你无论是start还是stop命令,都是需要去修改镜像的,而init容器只用在主容器之前启动一个容器就行了,并会对原有的pod结构发生改变
2、部分控制器演示:
资源清单中,控制器所管理的Pod的template.spec部分都是可以根据个人需求来定义所需的功能,可简单可复杂,例如简单进定义镜像、端口,复杂可定义liveness、startupness、readiness、start命令、stop命令等
① RS的创建
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: frontend
spec:
replicas: 3
selector:
matchLabels:
tier: frontend
template:
metadata:
labels:
tier: frontend
spec:
containers:
- name: nginx-1
image: nginx
env:
- name: GET_HOSTS_FROM
value: dns
ports:
- containerPort: 80
# 这个yaml定义rs的时候也就相当于在template下嵌套了一个pod的配置字段
# 注意RS的matchLabels和Pod的metadata.labels下所定义的标签值要是一致的,这样pod才能被RS所管理
在此案例中,由rs根据标签tier:frontend所管理3个pod的副本,如果中途强行将标签值修改了,
那么修改过标签的pod将不再属于RS所管理,因为RS是通过集合式的标签进行管理Pod的,一旦Pod标签值发生改变,那么RS就会认为这个Pod不在属于它,它为了维持原有的副本书目不变就会重新创建一个原有标签的pod
,
1
、 获取pod的标签值# kubectl get pods --show-labels
2
、根据指定的标签值筛选pod# kubectl get pods --selector 标签名=标签值 或 # kubectl get pods -l 标签名=标签值
3
、删除pod的标签值# kubectl label pod pod名称 标签名-
5
、为pod添加标签# kubectl label pods pod名称 标签名=标签值 # # 如果pod原有标签,加上--overwrite参数可覆盖原参数,不加--overwrite参数就会添加一个新的标签 # kubectl label --overwrite pod pod名称 标签名=标签值
删除RS,RS所维持的副本也会被一并删除
② Deployment部署一个简单的nginx应用
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.7.9
ports:
- containerPort: 80
创建Deployment
:# kubectl apply -f yaml文件 --record --record参数可以记录命令,可以很方便的查看每次 revision(修正) 的变化
副本数目的收缩、扩容
:# kubectl scale deployment deployment名称 --replicas=新副本数 # 如果集群支持Horizontal pod autoscaling 的话,还可以为Deployment设置自动扩展 # kubectl autoscale deployment nginx-deployment --min=10 --max=15 --cpu-percent=80
如果通过 scale 命令进行更换副本数,它仅仅是更新了pod的副本数目,而不涉及到版本的回退、滚动等,因为创建/删除的pod副本都是原来pod的模板生成的,RS本身并没有发生改变,
pod的滚动更新、回滚都是涉及到RS的更新与回退的
删除自动扩缩容HPA
通过HPA参数autoscale自动扩缩容的pod并不会随着HPA的删除而改变,如果需要更改pod的数目需要个人重新指定pod的数目
更新镜像/滚动更新
:格式: # kubectl set image deployment/deployment名称 容器名=本地或远程镜像:标签 --record=true # 实际操作得时候,你会发现镜像的更新会触发新的RS的创建,也就是之前提到的滚动更新的过程:创建新的RS,更新一个新的Pod、删除一个旧的Pod # 这里为什么要加上--record=true参数呢?因为如果更新Deployment、DaemonSet、Job等所管理的Pod的时候不加--record=true参数,那么就不会记录你所更新的操作,当你需要使用rollout --to-revision=XX的时候就很难准确找到你想回滚的版本,下面回滚部分详细讲 # 更新镜像,实际上它会触发滚动更新( 但请确保资源清单中imagePolicy: Always )。在实际环境中,涉及到容器化的更新都是将更新好的代码、环境封装到镜像中,更新容器版本其实更新镜像就可
回滚
:# 回滚 # kubectl rollout undo deployment/deployment名称 # 查看指定历史版本 # kubectl rollout history deployment deployment名称 --revision=指定版本 # 查看回滚的状态 # kubectl rollout status deployment/deployment名称 # 使用status查看rollout的状态,如果显示回滚已成功的话,status那条命令的返回状态码会是0 # 回滚到特定历史版本 # kubectl rollout undo deployment/deployment名称 --to-revision=指定历史版本 举例: # kubectl rollout history deployment nginx-deploy deployments "nginx-deploy" REVISION CHANGE-CAUSE 1 <none> 2 <none> 3 kubectl apply --filename=Desktop/nginx-deployment.yaml --record=true # 从上面的输出信息可以看出更新应用时--record=true参数的作用了,应用的回退是根据版本数字来进行的,如果你更新版本的时候没加--record=true参数,后期版本数目多了,时间久了,管理员也忘记了每个版本对应的内容,就很麻烦,虽然可以通过 kubectl rollout history deployment deployment名称 --revision=指定版本 命令来查看版本对应的内容,但是还是挺麻烦的,所以更新应用版本的时候一定要加上--record=true参数 # 查看回滚历史操作 # kubectl rollout history deployment/deployment名称 # kubectl rollout history 看到的历史版本数目与 # kubectl get rs 所看到的数目应该是一致的 # 暂停deployment的更新 # kubectl rollout pause deployent/deployment名称
清理Policy
:用户可以通过设置资源清单的
.spec.revisionHistoryLimit
项来指定deployment最多保留多少version历史记录。默认会保留所有的version,如果将该选项设置为0,Deployment就不允许回退了。因为Deployment的滚动更新、回滚都是基于RS的版本来完成的,如果没有了老旧版本的RS,那也就不存在回滚的操作了。默认情况下,
滚动更新(Rolling Updates)
允许通过使用新的实例逐步更新Pod实例,零停机
进行Deployment更新。新的Pod将在具有可用资源的节点上进行调度。默认情况下,更新期间不可用的Pod的最大值和可以创建的新的Pod数目都是1
,这两个选项都可以配置为(pod)数字或百分比
。在kubernetes中,更新是经过版本控制的,任何Deployment更新都可以恢复到以前的(稳定)版本。
Rollover(多个rollout并行)
当正在创建10个副本的nginx:v1的deployment时,当replica副本数才创建到了3个,此时镜像被更新为nginx:v2版本了且开始更新这个Deployment了,那么这个Deployment权限会立即杀掉已经创建了的3个v1版本的Pod,并开始创建v2版本的Pod,它不会等到v1版本的10个副本都创建完成后才开始改变航道
疑问:
1、kubectl apply 与 kubectl create 的区别
序号 kubectl apply kubectl create 1 根据yaml文件中包含的字段(yaml文件可以只写需要改动的字段),直接升级集群中的现有资源对象 首先删除集群中现有的所有资源,然后重新根据yaml文件(必须是完整的配置信息)生成新的资源对象 2 yaml文件可以不完整,只写需要的字段 yaml文件必须是完整的配置字段内容 3 kubectl apply只工作在yaml文件中的某些改动过的字段 kubectl create工作在yaml文件中的所有字段 4 在只改动了yaml文件中的某些声明时,而不是全部改动,你可以使用kubectl apply 在没有改动yaml文件时,使用同一个yaml文件执行命令kubectl replace,将不会成功(fail掉),因为缺少相关改动信息
2、如果资源清单中,kind字段定义为deployment,然后template下面直接是pod的信息,在创建deployment时,是不是默认会自动创建rs?
答:deployment默认就是通过RS来管理pod的,所以通过资源清单创建deployment的时候默认就会创建RS来管理Pod。
③ DaemonSet 操作
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: deamonset-example
labels:
app: daemonset
spec:
selector:
matchLabels:
name: deamonset-example
template:
metadata:
labels:
name: deamonset-example
spec:
containers:
- name: daemonset-example
image: wangyanglinux/myapp:v1
因为我的k8s集群只有三个节点,分别是master1、node1、node2,因此创建DaemonSet控制器的话,默认会在三个节点各创建一个DaemonSet 指定的 Pod
④ Job
Job spec特殊说明 # spec.template格式同Pod的格式一致,可以根据个人需求加上liveiness、startupness等 # RestartPolicy仅支持Never或OnFailure (Job的机制就是成功执行就退出,如果成功执行退出又重新拉起了那就重复执行了) # 单个Pod时,默认Pod成功运行后Job即结束 # .spec.completions指定任务成功执行的次数,默认为1(即Pod需要成功运行几次所设定的命令) # .spec.parallelism标志并行运行的Pod的个数,默认为1 ( 如果.completions指定为4,parallelism指定为2,那么意思就是可以并行运行2个Pod,这2个Pod总共成功执行4次即可 ) # .spec.activeDeadlineSeconds标志失败Pod的重试最大时间,超过这个时间不会继续重试
apiVersion: batch/v1
kind: Job
metadata:
name: pi
spec:
template:
metadata:
name: pi
spec:
containers:
- name: pi
image: perl
command: ["perl","-Mbignum=bpi","-wle","print bpi(2000)"]
restartPolicy: Never
# kubectl describe pod
⑤ CronJob
CronJob spec特殊说明 # .spec.schedule:调度,必需字段,指定任务运行周期,格式同 Cron # .spec.jobTemplate:Job 模板,必需字段,指定需要运行的任务,格式同 Job # .spec.startingDeadlineSeconds:启动 Job 的期限(秒级别),该字段是可选的。如果因为任何原因而错过了被调度的时间,那么错过执行时间的 Job 将被认为是失败的。如果没有指定,则没有期限. # .spec.concurrencyPolicy:并发策略,该字段也是可选的。它指定了如何处理被 Cron Job 创建的 Job 的并发执行。只允许指定下面策略中的一种: # Allow(默认):允许并发运行 Job # Forbid:禁止并发运行,如果前一个还没有完成,则直接跳过下一个 # Replace:取消当前正在运行的 Job,用一个新的来替换 ( 意思就是,假设定义每1分钟创建1个Job,第1分钟创建了1个Pod,到了第2分钟的时候又要创建Pod了,此时第一个Pod还没有退出, 如果.spec.concurrencyPolicy为Allow的话,创建第2个Pod与第1个Pod并行运行; 如果.spec.concurrencyPolicy为Forbid的话; 如果.spec.concurrencyPolicy为Replace的话,干掉老的,以新的来替换它 ) 注意,当前策略只能应用于同一个 Cron Job 创建的 Job。如果存在多个 Cron Job,它们创建的 Job 之间总是允许并发运行。 # .spec.suspend:挂起,该字段也是可选的。如果设置为true,后续所有执行都会被挂起。它对已经开始执行的 Job 不起作用。默认值为false。 # .spec.successfulJobsHistoryLimit和.spec.failedJobsHistoryLimit:历史限制,是可选的字段。它们指定了可以保留多少完成和失败的 Job。默认情况下,它们分别设置为3和1(即成功保留3份副本,失败保存1份副本)。设置限制的值为0,相关类型的 Job 完成后将不会被保留
apiVersion: batch/v1beta1
kind: CronJob
metadata:
name: hello
spec:
schedule: "*/1 * * * *"
jobTemplate:
spec:
template:
spec:
containers:
- name: hello
image: busybox
args:
- /bin/sh
- -c
- date; echo Hello from the Kubernetes cluster
restartPolicy: OnFailure
# kubectl get cronjob # kubectl get jobs # pods=$(kubectl get pods --seletor=job-name=job名称 --output=jsonpath={.items..metadata.name}) # kubectl logs $pods # 注意,删除 cronjob 的时候不会自动删除 job,这些 job 可以用 kubectl delete job 来删除 # kubectl delete cronjob cronjob名称
CrondJob 本身的一些限制:
a、cronjob是否成功并不容易判断,原因是cronjob连接到一个job任务,job任务的执行状态可以被判断,但是cronjob无法链接到job的成功状态( 即cronjob仅会定期的创建job而已 )
b、创建 Job 操作应该是幂等的