理解resource limit 会如何影响pod 调度
Kubernetes 提供request 和 limit 两种方式设置容器资源。
为了提高资源利用率,k8s调度时根据pod 的request值计算调度策略,从而实现节点资源超卖。
k8s根据limit限制pod使用资源,当内存超过limit时会触发oom。 且限制pod的cpu 不允许超过limit。
根据pod的 request和limit,k8s会为pod 计算服务质量,并分为Guaranteed(requests和limits显示设置且相等或者只设置了limits,那么requests也会与之相等), Burstable(limits和requests显示设置不相等), BestEffort 这3级。当节点资源不足时,发生驱逐或者oom时, Guaranteed 级别的pod 优先保护, Burstable 节点次之(request越大,使用资源量越少 保护级别越高), BestEffort 最先被驱逐。
Kubernetes提供了RequestQuota和LimitRange 用于设置namespace 内pod 的资源范围 和 规模总量。 RequestQuota 用于设置各种类型对象的数量, cpu和内存的总量。 LimitRange 用于设置pod或者容器 request和limit 的默认值,最大最小值, 以及超卖比例(limit / request)。
对于一些重要的线上应用,我们应该合理设置limit和request,limit和request 设置一致,资源不足时k8s会优先保证这些pod正常运行。
为了提高资源利用率。 对一些非核心,并且资源不长期占用的应用,可以适当减少pod的request,这样pod在调度时可以被分配到资源不是十分充裕的节点,提高使用率。但是当节点的资源不足时,也会优先被驱逐或被oom kill。
不使用调度器, 手动调度一个pod
使用static pod可以实现手动调度一个pod。
static pod的特点总结如下:
- 不通过apiserver watch, 由本机的kubelet进程执行watch,并拉起
- 只在本机进行调度
- 没有健康检查
- 不被controller manager调度
- 因为apiserver里有pod的镜像信息, 通过apiserver还是可以看到pod的信息
如何创建?
方法一: kubelet启动时要指定参数kubelet --pod-manifest-path=
方法二: 指定--manifest-url=
如何删除?
当我们直接用kubectl的命令kubectl delete pod删除一个静态pod时, 我们会发现static pod过一段时间又会被kubelet拉起。
要完全删除一个static pod, 可以移除把该static pod的编排文件, 从--pod-manifest-path=
使用场景?
因为static pod有一个特性是我们的应用的docker 容器被删除,或者pod被kubectl误删了之后, static pod还能被kubelet进程拉起。通过这种方式保证了应用的可用性。 有点相当于systemd的功能, 但比systemd好的一点是, static pod的镜像信息会在apiserver中注册。 这样的话, 我们就可以统一对部署信息进行可视化管理。 另外static调度了的是容器, 无需拷贝二进制文件到主机上, 应用封装在镜像里也保证了环境的一致性, 无论是应用的编排文件还是应用的镜像都方便进行版本管理和分发。
实际生产中, static pod可以用来部署kubernetes的kube-proxy, kube-scheduler, kube-controller-manager, kube-apiserver等进程。
当我们要部署一个有状态的应用, 有几种方式:
- 使用statefulset
- 给主机打上label,只允许主机在这几台主机上调度
- 通过挂载ceph盘来存储有状态数据(要求master和node节点上都要安装rbd工具)
- 使用static pod ,但是这种方式适合那些无滚动升级需求, 无扩缩容需求,不频繁进行升级的应用
常用的优选算法介绍:
least_requested分值计算方式 (cpu((capacity-sum(requested))10/capacity) + memory((capacity-sum(requested))10/capacity))/2
其cpu和menmory分值各占一半。
balanced_resource_allocation是选用cpu和内存使用最为接近的节点,基于least_requested分值的计算结果判定。
那么schedule里的policy是啥样子的呢:
{
"kind" : "Policy",
"apiVersion" : "v1",
"predicates" : [
{"name" : "PodFitsPorts"},
{"name" : "PodFitsResources"},
{"name" : "NoDiskConflict"},
{"name" : "MatchNodeSelector"},
{"name" : "HostName"}
],
"priorities" : [
{"name" : "LeastRequestedPriority", "weight" : 1},
{"name" : "BalancedResourceAllocation", "weight" : 1},
{"name" : "ServiceSpreadingPriority", "weight" : 1},
{"name" : "EqualPriority", "weight" : 1}
]
}
*** 4. controller的区别 ***
controller manager作为集群内部的管理控制中心,负责集群内的Node,Pod副本,服务端点(endpoint),命名空间(namespace)等的管理,当某个Node意外宕机,CM会及时发现此故障并执行自动化修复流程,确保集群始终处于预期的工作状态。
controller management包括:replication controller,node controller,rescourseQuota controller,namespaces controller,serviceAccount controller,token controller,service tontroller和endpoint controller。CM是这些controller的核心管理者。
Replication Controller
RC的作用是确保在任何时候集群中一个RC所关联的Pod副本数量保持预设值。
最好不用越过RC而直接创建Pod,因为通过RC管理Pod副本,实现自动创建,补足,替换,删除Pod副本,提供系统的容灾能力。
总结一下RC的职责:
1.确保当前集群中有且仅有N个Pod实例
2.通过调整RC中yaml文件的spec.replicas属性值来实现系统扩容或缩容
3.通过改变RC中的Pod模板来实现系统的滚动升级。
再总结一下RC的典型使用场景,如下
1.重新调度
2.弹性伸缩
3.滚动更新
Node controller
kubelet进程在启动时通过API server注册自身节点信息,并定时向API server汇报状态信息,API server更新这些信息到etcd中。
Node controller通过API server定时获取Node的相关信息,实现管理和监控Node。
endpoint controller 和service controller
endpoint表示了一个service对应的所有pod副本的访问地址,而endpoint controller就是负责生成和维护所有endpoint对象的控制器。负责监听service和对应的Pod副本的变化,若service被删除,则删除与该service同名的endpoint对象,若检测到新的service创建或修改,则先根据该service获取相应的pod列表,然后创建或更新service对应的endpoint对象。
那么endpoint对象如何使用呢。答案是在每个node上的kube-proxy进程,该进程获得每个service的endpoint,实现service的负载均衡功能。
Namespace controller
用户通过API server可以创建新的Namespace并保存在etcd中,namespace controller定时通过API server读取这些namespace信息,若namespace被删除,则NC删除该namespace下的RC,Pod等资源对象。
如果namespace controller观察到namespace设置了删除期限,同时namespace的spec.finalizers域值为空,则NC将通过API server删除该namespace资源。
ReplicaSet
ReplicaSet的目的是在任何时候都maintain一组稳定的pod replicas,常用来确保指定数量的pod replica可用。ReplicaSet具有selector,replicas, podTemplate字段。通过pod的metadata.ownerReferences字段来关联ReplicaSet和Pod。
注意:当创建的pod的label符合ReplicaSet的selector,且该pod不属于任何controller,则该pod会被自动归为该ReplicaSet拥有,导致ReplicaSet下的pod不相同;同时,会影响ReplicaSet本身的replica pod的个数。
建议使用Deployment来管理ReplicaSet,而不是直接使用,除非需要定制化的更新测例或不需要更新。
因为deployment不仅具备了replicaSet的功能,同时还支持事件和状态查看,回滚,版本记录,对于每次升级可以暂停和启动。
Deployment
Deployment对Pod和ReplicaSet提供声明式的更新。在描述预期状态后,Deployment会以可控的频率下使得实际状态符合预期状态。
默认情况下,Deployment保证最多25%的pod不可靠,pod数量最多超出25%。
StatefulSets
StatefulSets主要用来管理有状态的app。跟Deployment一样,StatefulSet用来管理Pod,但不同的是,StatefulSets中的每个pod都维护一个严格的identifier并在reschedule时不变。
StatefulSet具有以下特点:
- Stable, unique network identifiers.
- Stable, persistent storage.
- Ordered, graceful deployment and scaling.
- Ordered, automated rolling updates.
限制:
- Pod的storage要么基于请求的storage class由PersistentVolume Provisioner设置,要么由admin提前设置好;
- 删除或scale down StatefulSet将不会删除相关的volumes,用于保证数据安全;
- StatefulSets目前需要创建headless service来负责pod的netework identity,用户负责创建该headless service;
- StatefulSet删除后不保证pod结束,pod会按顺序gracefully的结束;
- 当使用默认的Pod Management Policy(
OrderedReady
)作为rollupdate policy,可能需要人工干预来走出broken state。 - StatefulSet上部署和扩展的规则:创建pod的顺序由0到N,删除的顺序正好相反;扩展时,前面的pod必须是Running和Ready状态,删除时,后续pod必须是terminated。
K8S 1.7之后,指定.spec.updateStrategy来设置更新策略,当.spec.updateStrategy.type为OnDelete时,在pod template改动后将不会自动更新Pods;为RollingUpdate,默认策略,将自动更新pod。RollingUpdate更新策略可以设置partition,通过设置字段 .spec.updateStrategy.rollingUpdate.partition来实现,当pod template更新后,只有ordinal大于等于partition的pod才会更新,小于partition的pod不更新。当.spec.replicas小于partition,所有pod不更新。
Job
Job创建一个或多个pod并确保指定数量的pod成功终止。当指定数量的pod成功终止后,job结束。场景:当创建的pod不能完成时,job会创建新的pod来完成任务。
下面是Job的示例。
apiVersion: batch/v1
kind: Job
metadata:
name: pi
spec:
backoffLimit:6
activeDeadlineSeconds: 100 #
ttlSecondsAfterFinished: 100
template:
spec:
containers:
- name: pi
image: perl
command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"]
restartPolicy: Never
backoffLimit: 4
restartPolicy只允许Never或OnFailure。
通过设置.spec.completions和.spec.parallelism属性来完成不同的任务,前者表示成功完成的任务数量,后者表示并行任务数量,默认都为1.当.spec.parallelism设置为0时,job将被暂停。
通过.spec.backoffLimit来设置pod失败后的重试次数,默认为6,重试的delay时间指数式增长,最长6分钟。
当Job complete,将不会创建pod,old pod也不会被删除,这样可以查看log。同时,Job也存在,这样可以查看status。可以通过kubectl delete来人工删除。
Job终止可能因为失败次数超过.spec.backoffLimit,或者因为job的运行时间炒超过设置的.spec.activeDeadlineSeconds。当Job的运行时间超过activeDeadlineSeconds,所有运行的pod被终止,Job状态变为Failed,reason: DeadlineExceeded。
-Cron Job
终止的Job不被回收会浪费资源,可以使用cron job来管理job并清理job。
Cron Job创建基于时间调度的job,时区为master node的时区。当Cron job大于100次没有被调度,将不会调度job并打印error信息。Cron job只负责创建符合schedule的job,而job负责pod的管理。
activeDeadlineSeconds
通过指定job存活时间,来结束一个job。当job运行时间达到activeDeadlineSeconds指定的时间后,job会停止由它启动的所有任务(如:pod),并设置job的状态为failed, reason: DeadlineExceeded
TTL Controller提供了一种机制来限制resource object执行完存活的时间,目前只处理Job。
ttlSecondsAfterFinished
使用场景:默认情况下,job异常或者成功结束后,包括job启动的任务(pod),都不会被清理掉,因为你可以依据保存的job和pod,查看状态、日志,以及调试等。这些用户可以手动删除,用户手动删除job,job controller会级联删除对应的pod。
除了手动删除,通过指定参数ttlSecondsAfterFinished也可以实现自动删除job,以及级联的资源,如:pod。如果设置为0,job会被立即删除。如果不指定,job则不会被删除。