快捷菜单
常用功能一站直达
更多功能请点顶栏「快捷菜单」

[TOC]
实验环境
1实验环境:
21、win10,vmwrokstation虚机;
32、k8s集群:3台centos7.6 1810虚机,1个master节点,2个node节点
4 k8s version:v1.22.2
5 containerd://1.5.5实验软件
链接:https://pan.baidu.com/s/1P3Z_ujk22dYDXzM37WI5FA?pwd=v01w 提取码:v01w
2022.2.18-39.亲合性调度-实验代码.zip
链接:https://pan.baidu.com/s/1Nbkwz0rvDris8F6qFegJtw?pwd=4dx5
提取码:4dx5
2022.2.20-42.服务质量-实验代码
链接:https://pan.baidu.com/s/1CAC9j5yU-sg-aDfLPYVflg?pwd=b3jp
提取码:b3jp
2022.2.19-40.拓扑分布约束-实验代码
链接:https://pan.baidu.com/s/19x43qNOI2oogXTcgEDCwgQ?pwd=0min
提取码:0min
2022.2.19-41.Descheduler-s实验代码
前置知识
存储:(容器:可写层 & 挂载外部存储)
扩展资源:gpu等;
kube-scheduler 是 kubernetes 的核心组件之一,主要负责整个集群资源的调度功能,根据特定的调度算法和策略,将 Pod 调度到最优的工作节点上面去,从而更加合理、更加充分的利用集群的资源,这也是我们选择使用 kubernetes 一个非常重要的理由。如果一门新的技术不能帮助企业节约成本、提供效率,我相信是很难推进的。
🍂 什么是调度?
调度,可以说是Kubernetes最核心的功能之一了。
Pod是Kubernetes中最小的调度单元,而Pod又是运行在Node之上的。所谓调度,简单来说就是为一个新创建出来的 Pod,寻找一个最适合它运行的Node。
🍂 调度概览

🍂 调度器分类
1.单体调度器:对于大规模批量调度诉求场景,不能胜任!(基于pod的事件调度)!
2.两层调度器:应用平台–hadoop,spark;资源调度器(负责底层计算资源的管理),应用调度器;resource offers,存在的问题:1.资源争抢如何解决?2.分配资源不合理如何处理 解决办法:悲观锁 先锁定资源,再进行资源的腾挪处理。–>效率不高
3.状态共享调度器:基于版本控制/事务控制的基于乐观锁的调度! full state,本地缓存,回写,冲突判断,重试。
kube-scheduler
kube-scheduler负责分配调度Pod 到集群内的节点上,它监听kube-apiserver,查询还未分配Node的Pod,然后根据调度策略为这些Pod分配节点(更新Pod 的NodeName字段)。(生产者-消费者模型,plugin)
默认情况下,kube-scheduler 提供的默认调度器能够满足我们绝大多数的要求,我们前面和大家接触的示例也基本上用的默认的策略,都可以保证我们的 Pod 可以被分配到资源充足的节点上运行。但是在实际的线上项目中,可能我们自己会比 kubernetes 更加了解我们自己的应用,比如我们希望一个 Pod 只能运行在特定的几个节点上,或者这几个节点只能用来运行特定类型的应用,这就需要我们的调度器能够可控。
1例如:随便导出一个pod的yaml文件,可以看到其默认调度器就是`default-scheduler`.
2$ kubectl get po apisix-etcd-0 -napisix -oyaml
3……
4schedulerName: default-scheduler
5……
6
7#也就是k8s集群默认使用的:
8$ kubectl get po -A
9……
10kube-system kube-scheduler-master1 1/1 Running 3 (27d ago) 108dkube-scheduler 的主要作用就是根据特定的调度算法和调度策略将 Pod 调度到合适的 Node 节点上去,是一个独立的二进制程序,启动之后会一直监听 API Server,获取到 **PodSpec.NodeName** 为空的 Pod,对每个 Pod 都会创建一个 binding。

这个过程在我们看来好像比较简单,但在实际的生产环境中,需要考虑的问题就有很多了:
🍂 调度主要分为以下几个部分:
Predicates(过滤)Priorities(打分)Predicates 阶段首先遍历全部节点,过滤掉不满足条件的节点,属于强制性规则,这一阶段输出的所有满足要求的节点将被记录并作为第二阶段的输入,如果所有的节点都不满足条件,那么 Pod 将会一直处于 Pending 状态,直到有节点满足条件,在这期间调度器会不断的重试。
所以我们在部署应用的时候,如果发现有 Pod 一直处于 Pending 状态,那么就是没有满足调度条件的节点,这个时候可以去检查下节点资源是否可用。
Priorities 阶段即再次对节点进行筛选,如果有多个节点都满足条件的话,那么系统会按照节点的优先级(priorites)大小对节点进行排序,最后选择优先级最高的节点来部署 Pod 应用。
01、如果你的pod处于pending状态,那么一定就是调度器出现了问题,那么原因会很多,有可能是你的node资源不足,有可能是你的节点已经被占用了……(因此需要使用kubectl describle pod xxx来查看原因)
02、所谓的bing操作就是如下: $ kubectl get po apisix-etcd-0 -napisix -oyaml …… nodeName: node2 #将pod的配置清单的nodeName字段补充完成。 ……
🍂 下面是调度过程的简单示意图:

更详细的流程是这样的:
binding 操作,然后将结果存储到 etcd 中, 最后被选择出来的 Node 节点对应的 kubelet 去执行创建 Pod 的相关操作(当然也是 watch APIServer 发现的)。🍂 Predicates plugin工作原理
链式过滤器
🍂 调度插件
LeastAllocated:空闲资源多的分高 –使的node上的负载比较合理一点!
MostAllocated:空闲资源少的分高 – 可以退回Node资源!
🍀 最简单的Scheduler实现
k8s 0.2版本的一个架构图,基本调度方式就是随机,轮询会稍微复杂一些;
随机

包括很多时候,默认的调度器已经不能满足业务需求,需要对它做自定义的扩展和实现,具体该怎么做,背后的原理又是什么样的,业界有没有优秀案例可供参考,这些都是非常令人头疼的问题。(extender本身就是一个拉低性能的因素。)


🍂 考虑到实际环境中的各种复杂情况,kubernetes 的调度器采用插件化的形式实现,可以方便用户进行定制或者二次开发,我们可以自定义一个调度器并以插件形式和 kubernetes 进行集成。
开发人员注意即可:
kubernetes 调度器的源码位于
kubernetes/pkg/scheduler中,其中 Scheduler 创建和运行的核心程序,对应的代码在pkg/scheduler/scheduler.go,如果要查看kube-scheduler的入口程序,对应的代码在cmd/kube-scheduler/scheduler.go。
https://github1s.com/kubernetes/kubernetes/tree/v1.22.5


基于Scheduler Framework实现扩展

未来主流的方法。
🍂 目前调度器已经全部通过插件的方式实现了调度框架,默认开启的调度插件如以下代码所示:
1// pkg/scheduler/algorithmprovider/registry.go
2
3func getDefaultConfig() *schedulerapi.Plugins {
4 return &schedulerapi.Plugins{
5 QueueSort: &schedulerapi.PluginSet{
6 Enabled: []schedulerapi.Plugin{
7 {Name: queuesort.Name},
8 },
9 },
10 PreFilter: &schedulerapi.PluginSet{
11 Enabled: []schedulerapi.Plugin{
12 {Name: noderesources.FitName},
13 {Name: nodeports.Name},
14 {Name: podtopologyspread.Name},
15 {Name: interpodaffinity.Name},
16 {Name: volumebinding.Name},
17 },
18 },
19 Filter: &schedulerapi.PluginSet{
20 Enabled: []schedulerapi.Plugin{
21 {Name: nodeunschedulable.Name},
22 {Name: noderesources.FitName},
23 {Name: nodename.Name},
24 {Name: nodeports.Name},
25 {Name: nodeaffinity.Name},
26 {Name: volumerestrictions.Name},
27 {Name: tainttoleration.Name},
28 {Name: nodevolumelimits.EBSName},
29 {Name: nodevolumelimits.GCEPDName},
30 {Name: nodevolumelimits.CSIName},
31 {Name: nodevolumelimits.AzureDiskName},
32 {Name: volumebinding.Name},
33 {Name: volumezone.Name},
34 {Name: podtopologyspread.Name},
35 {Name: interpodaffinity.Name},
36 },
37 },
38 PostFilter: &schedulerapi.PluginSet{
39 Enabled: []schedulerapi.Plugin{
40 {Name: defaultpreemption.Name},
41 },
42 },
43 PreScore: &schedulerapi.PluginSet{ #打分
44 Enabled: []schedulerapi.Plugin{
45 {Name: interpodaffinity.Name},
46 {Name: podtopologyspread.Name},
47 {Name: tainttoleration.Name},
48 },
49 },
50 Score: &schedulerapi.PluginSet{
51 Enabled: []schedulerapi.Plugin{
52 {Name: noderesources.BalancedAllocationName, Weight: 1},
53 {Name: imagelocality.Name, Weight: 1},
54 {Name: interpodaffinity.Name, Weight: 1},
55 {Name: noderesources.LeastAllocatedName, Weight: 1},
56 {Name: nodeaffinity.Name, Weight: 1},
57 {Name: nodepreferavoidpods.Name, Weight: 10000},
58 // Weight is doubled because:
59 // - This is a score coming from user preference.
60 // - It makes its signal comparable to NodeResourcesLeastAllocated.
61 {Name: podtopologyspread.Name, Weight: 2},
62 {Name: tainttoleration.Name, Weight: 1},
63 },
64 },
65 Reserve: &schedulerapi.PluginSet{
66 Enabled: []schedulerapi.Plugin{
67 {Name: volumebinding.Name},
68 },
69 },
70 PreBind: &schedulerapi.PluginSet{
71 Enabled: []schedulerapi.Plugin{
72 {Name: volumebinding.Name},
73 },
74 },
75 Bind: &schedulerapi.PluginSet{
76 Enabled: []schedulerapi.Plugin{
77 {Name: defaultbinder.Name},
78 },
79 },
80 }
81}从上面我们可以看出调度器的一系列算法由各种插件在调度的不同阶段来完成,下面我们就先来了解下调度框架。
调度框架定义了一组扩展点,用户可以实现扩展点定义的接口来定义自己的调度逻辑(我们称之为扩展),并将扩展注册到扩展点上,调度框架在执行调度工作流时,遇到对应的扩展点时,将调用用户注册的扩展。调度框架在预留扩展点时,都是有特定的目的,有些扩展点上的扩展可以改变调度程序的决策方法,有些扩展点上的扩展只是发送一个通知。
我们知道每当调度一个 Pod 时,都会按照两个过程来执行:调度过程和绑定过程。
调度过程为 Pod 选择一个合适的节点,绑定过程则将调度过程的决策应用到集群中(也就是在被选定的节点上运行 Pod),将调度过程和绑定过程合在一起,称之为调度上下文(scheduling context)。需要注意的是调度过程是**同步**运行的(同一时间点只为一个 Pod 进行调度),绑定过程可异步运行(同一时间点可并发为多个 Pod 执行绑定)。
调度过程和绑定过程遇到如下情况时会中途退出:
这个时候,该 Pod 将被放回到 待调度队列,并等待下次重试。
下图展示了调度框架中的调度上下文及其中的扩展点,一个扩展可以注册多个扩展点,以便可以执行更复杂的有状态的任务。


调度阶段: 1.predicate(预选): 2.priority/score(优选): 绑定阶段:
🍂 详细过程:
QueueSort 扩展用于对 Pod 的待调度队列进行排序,以决定先调度哪个 Pod,QueueSort 扩展本质上只需要实现一个方法 Less(Pod1, Pod2) 用于比较两个 Pod 谁更优先获得调度即可,同一时间点只能有一个 QueueSort 插件生效。Pre-filter 扩展用于对 Pod 的信息进行预处理,或者检查一些集群或 Pod 必须满足的前提条件,如果 pre-filter 返回了 error,则调度过程终止。Filter 扩展用于排除那些不能运行该 Pod 的节点,对于每一个节点,调度器将按顺序执行 filter 扩展;如果任何一个 filter 将节点标记为不可选,则余下的 filter 扩展将不会被执行。调度器可以同时对多个节点执行 **filter** 扩展。Post-filter 是一个通知类型的扩展点,调用该扩展的参数是 filter 阶段结束后被筛选为可选节点的节点列表,可以在扩展中使用这些信息更新内部状态,或者产生日志或 metrics 信息。Scoring 扩展用于为所有可选节点进行打分,调度器将针对每一个节点调用 Soring 扩展,评分结果是一个范围内的整数。在 normalize scoring 阶段,调度器将会把每个 scoring 扩展对具体某个节点的评分结果和该扩展的权重合并起来,作为最终评分结果。Normalize scoring 扩展在调度器对节点进行最终排序之前修改每个节点的评分结果,注册到该扩展点的扩展在被调用时,将获得同一个插件中的 scoring 扩展的评分结果作为参数,调度框架每执行一次调度,都将调用所有插件中的一个 normalize scoring 扩展一次。Reserve 是一个通知性质的扩展点,有状态的插件可以使用该扩展点来获得节点上为 Pod 预留的资源,该事件发生在调度器将 Pod 绑定到节点之前,目的是避免调度器在等待 Pod 与节点绑定的过程中调度新的 Pod 到节点上时,发生实际使用资源超出可用资源的情况(因为绑定 Pod 到节点上是异步发生的)。这是调度过程的最后一个步骤,Pod 进入 reserved 状态以后,要么在绑定失败时触发 Unreserve 扩展,要么在绑定成功时,由 Post-bind 扩展结束绑定过程。Permit 扩展用于阻止或者延迟 Pod 与节点的绑定。Permit 扩展可以做下面三件事中的一项:Unreserve 扩展。Pre-bind 扩展用于在 Pod 绑定之前执行某些逻辑。例如,pre-bind 扩展可以将一个基于网络的数据卷挂载到节点上,以便 Pod 可以使用。如果任何一个 pre-bind 扩展返回错误,Pod 将被放回到待调度队列,此时将触发 Unreserve 扩展。Bind 扩展用于将 Pod 绑定到节点上:Post-bind 是一个通知性质的扩展:Unreserve 是一个通知性质的扩展,如果为 Pod 预留了资源,Pod 又在被绑定过程中被拒绝绑定,则 unreserve 扩展将被调用。Unreserve 扩展应该释放已经为 Pod 预留的节点上的计算资源。在一个插件中,reserve 扩展和 unreserve 扩展应该成对出现。🍂 如果我们要实现自己的插件,必须向调度框架注册插件并完成配置,另外还必须实现扩展点接口,对应的扩展点接口我们可以在源码 pkg/scheduler/framework/v1alpha1/interface.go 文件中找到,如下所示:
1// Plugin is the parent type for all the scheduling framework plugins.
2type Plugin interface {
3 Name() string
4}
5
6type QueueSortPlugin interface {
7 Plugin
8 Less(*PodInfo, *PodInfo) bool
9}
10
11// PreFilterPlugin is an interface that must be implemented by "prefilter" plugins.
12// These plugins are called at the beginning of the scheduling cycle.
13type PreFilterPlugin interface {
14 Plugin
15 PreFilter(pc *PluginContext, p *v1.Pod) *Status
16}
17
18// FilterPlugin is an interface for Filter plugins. These plugins are called at the
19// filter extension point for filtering out hosts that cannot run a pod.
20// This concept used to be called 'predicate' in the original scheduler.
21// These plugins should return "Success", "Unschedulable" or "Error" in Status.code.
22// However, the scheduler accepts other valid codes as well.
23// Anything other than "Success" will lead to exclusion of the given host from
24// running the pod.
25type FilterPlugin interface {
26 Plugin
27 Filter(pc *PluginContext, pod *v1.Pod, nodeName string) *Status
28}
29
30// PostFilterPlugin is an interface for Post-filter plugin. Post-filter is an
31// informational extension point. Plugins will be called with a list of nodes
32// that passed the filtering phase. A plugin may use this data to update internal
33// state or to generate logs/metrics.
34type PostFilterPlugin interface {
35 Plugin
36 PostFilter(pc *PluginContext, pod *v1.Pod, nodes []*v1.Node, filteredNodesStatuses NodeToStatusMap) *Status
37}
38
39// ScorePlugin is an interface that must be implemented by "score" plugins to rank
40// nodes that passed the filtering phase.
41type ScorePlugin interface {
42 Plugin
43 Score(pc *PluginContext, p *v1.Pod, nodeName string) (int, *Status)
44}
45
46// ScoreWithNormalizePlugin is an interface that must be implemented by "score"
47// plugins that also need to normalize the node scoring results produced by the same
48// plugin's "Score" method.
49type ScoreWithNormalizePlugin interface {
50 ScorePlugin
51 NormalizeScore(pc *PluginContext, p *v1.Pod, scores NodeScoreList) *Status
52}
53
54// ReservePlugin is an interface for Reserve plugins. These plugins are called
55// at the reservation point. These are meant to update the state of the plugin.
56// This concept used to be called 'assume' in the original scheduler.
57// These plugins should return only Success or Error in Status.code. However,
58// the scheduler accepts other valid codes as well. Anything other than Success
59// will lead to rejection of the pod.
60type ReservePlugin interface {
61 Plugin
62 Reserve(pc *PluginContext, p *v1.Pod, nodeName string) *Status
63}
64
65// PreBindPlugin is an interface that must be implemented by "prebind" plugins.
66// These plugins are called before a pod being scheduled.
67type PreBindPlugin interface {
68 Plugin
69 PreBind(pc *PluginContext, p *v1.Pod, nodeName string) *Status
70}
71
72// PostBindPlugin is an interface that must be implemented by "postbind" plugins.
73// These plugins are called after a pod is successfully bound to a node.
74type PostBindPlugin interface {
75 Plugin
76 PostBind(pc *PluginContext, p *v1.Pod, nodeName string)
77}
78
79// UnreservePlugin is an interface for Unreserve plugins. This is an informational
80// extension point. If a pod was reserved and then rejected in a later phase, then
81// un-reserve plugins will be notified. Un-reserve plugins should clean up state
82// associated with the reserved Pod.
83type UnreservePlugin interface {
84 Plugin
85 Unreserve(pc *PluginContext, p *v1.Pod, nodeName string)
86}
87
88// PermitPlugin is an interface that must be implemented by "permit" plugins.
89// These plugins are called before a pod is bound to a node.
90type PermitPlugin interface {
91 Plugin
92 Permit(pc *PluginContext, p *v1.Pod, nodeName string) (*Status, time.Duration)
93}
94
95// BindPlugin is an interface that must be implemented by "bind" plugins. Bind
96// plugins are used to bind a pod to a Node.
97type BindPlugin interface {
98 Plugin
99 Bind(pc *PluginContext, p *v1.Pod, nodeName string) *Status
100}🍂 对于调度框架插件的启用或者禁用,我们可以使用安装集群时的 KubeSchedulerConfiguration 资源对象来进行配置。下面的例子中的配置启用了一个实现了 reserve 和 preBind 扩展点的插件,并且禁用了另外一个插件,同时为插件 foo 提供了一些配置信息:
1apiVersion: kubescheduler.config.k8s.io/v1alpha1
2kind: KubeSchedulerConfiguration
3
4...
5
6plugins:
7 reserve:
8 enabled:
9 - name: foo
10 - name: bar
11 disabled:
12 - name: baz
13 preBind:
14 enabled:
15 - name: foo
16 disabled:
17 - name: baz
18
19pluginConfig:
20- name: foo
21 args: >
22 foo插件可以解析的任意内容🍂 扩展的调用顺序如下:
KubeSchedulerConfiguration 中扩展的激活 enabled 顺序逐个调用扩展点的扩展enabled 列表中的某个位置激活默认插件的扩展,这种做法可以改变默认插件的扩展被调用时的顺序假设默认插件 foo 实现了 reserve 扩展点,此时我们要添加一个插件 bar,想要在 foo 之前被调用,则应该先禁用 foo 再按照 bar foo 的顺序激活。示例配置如下所示:
1apiVersion: kubescheduler.config.k8s.io/v1beta1
2kind: KubeSchedulerConfiguration
3
4...
5
6plugins:
7 reserve:
8 enabled:
9 - name: bar
10 - name: foo
11 disabled:
12 - name: foo在源码目录 pkg/scheduler/framework/plugins/examples 中有几个示范插件,我们可以参照其实现方式。
因为涉及到代码部分,本次这里不做演示,看下就好。
其实要实现一个调度框架的插件,并不难,我们只要实现对应的扩展点,然后将插件注册到调度器中即可,下面是默认调度器在初始化的时候注册的插件:
1// pkg/scheduler/algorithmprovider/registry.go
2func NewRegistry() Registry {
3 return Registry{
4 // FactoryMap:
5 // New plugins are registered here.
6 // example:
7 // {
8 // stateful_plugin.Name: stateful.NewStatefulMultipointExample,
9 // fooplugin.Name: fooplugin.New,
10 // }
11 }
12}但是可以看到默认并没有注册一些插件,所以要想让调度器能够识别我们的插件代码,就需要自己来实现一个调度器了,当然这个调度器我们完全没必要完全自己实现,直接调用默认的调度器,然后在上面的 NewRegistry() 函数中将我们的插件注册进去即可。在 kube-scheduler 的源码文件 kubernetes/cmd/kube-scheduler/app/server.go 中有一个 NewSchedulerCommand 入口函数,其中的参数是一个类型为 Option 的列表,而这个 Option 恰好就是一个插件配置的定义:
1// Option configures a framework.Registry.
2type Option func(framework.Registry) error
3
4// NewSchedulerCommand creates a *cobra.Command object with default parameters and registryOptions
5func NewSchedulerCommand(registryOptions ...Option) *cobra.Command {
6 ......
7}所以我们完全就可以直接调用这个函数来作为我们的函数入口,并且传入我们自己实现的插件作为参数即可,而且该文件下面还有一个名为 WithPlugin 的函数可以来创建一个 Option 实例:
1// WithPlugin creates an Option based on plugin name and factory.
2func WithPlugin(name string, factory framework.PluginFactory) Option {
3 return func(registry framework.Registry) error {
4 return registry.Register(name, factory)
5 }
6}所以最终我们的入口函数如下所示:
1func main() {
2 rand.Seed(time.Now().UTC().UnixNano())
3
4 command := app.NewSchedulerCommand(
5 app.WithPlugin(sample.Name, sample.New),
6 )
7
8 logs.InitLogs()
9 defer logs.FlushLogs()
10
11 if err := command.Execute(); err != nil {
12 _, _ = fmt.Fprintf(os.Stderr, "%v\n", err)
13 os.Exit(1)
14 }
15
16}其中 app.WithPlugin(sample.Name, sample.New) 就是我们接下来要实现的插件,从 WithPlugin 函数的参数也可以看出我们这里的 sample.New 必须是一个 framework.PluginFactory 类型的值,而 PluginFactory 的定义就是一个函数:
1type PluginFactory = func(configuration *runtime.Unknown, f FrameworkHandle) (Plugin, error)所以 sample.New 实际上就是上面的这个函数,在这个函数中我们可以获取到插件中的一些数据然后进行逻辑处理即可,插件实现如下所示,我们这里只是简单获取下数据打印日志,如果你有实际需求的可以根据获取的数据就行处理即可,我们这里只是实现了 PreFilter、Filter、PreBind 三个扩展点,其他的可以用同样的方式来扩展即可:
1// 插件名称
2const Name = "sample-plugin"
3
4type Args struct {
5 FavoriteColor string `json:"favorite_color,omitempty"`
6 FavoriteNumber int `json:"favorite_number,omitempty"`
7 ThanksTo string `json:"thanks_to,omitempty"`
8}
9
10type Sample struct {
11 args *Args
12 handle framework.FrameworkHandle
13}
14
15func (s *Sample) Name() string {
16 return Name
17}
18
19func (s *Sample) PreFilter(pc *framework.PluginContext, pod *v1.Pod) *framework.Status {
20 klog.V(3).Infof("prefilter pod: %v", pod.Name)
21 return framework.NewStatus(framework.Success, "")
22}
23
24func (s *Sample) Filter(pc *framework.PluginContext, pod *v1.Pod, nodeName string) *framework.Status {
25 klog.V(3).Infof("filter pod: %v, node: %v", pod.Name, nodeName)
26 return framework.NewStatus(framework.Success, "")
27}
28
29func (s *Sample) PreBind(pc *framework.PluginContext, pod *v1.Pod, nodeName string) *framework.Status {
30 if nodeInfo, ok := s.handle.NodeInfoSnapshot().NodeInfoMap[nodeName]; !ok {
31 return framework.NewStatus(framework.Error, fmt.Sprintf("prebind get node info error: %+v", nodeName))
32 } else {
33 klog.V(3).Infof("prebind node info: %+v", nodeInfo.Node())
34 return framework.NewStatus(framework.Success, "")
35 }
36}
37
38//type PluginFactory = func(configuration *runtime.Unknown, f FrameworkHandle) (Plugin, error)
39func New(configuration *runtime.Unknown, f framework.FrameworkHandle) (framework.Plugin, error) {
40 args := &Args{}
41 if err := framework.DecodeInto(configuration, args); err != nil {
42 return nil, err
43 }
44 klog.V(3).Infof("get plugin config args: %+v", args)
45 return &Sample{
46 args: args,
47 handle: f,
48 }, nil
49}完整代码可以前往仓库 https://github.com/cnych/sample-scheduler-framework 获取。
实现完成后,编译打包成镜像即可,然后我们就可以当成普通的应用用一个 Deployment 控制器来部署即可,由于我们需要去获取集群中的一些资源对象,所以当然需要申请 RBAC 权限,然后同样通过 --config 参数来配置我们的调度器,同样还是使用一个 KubeSchedulerConfiguration 资源对象配置,可以通过 plugins 来启用或者禁用我们实现的插件,也可以通过 pluginConfig 来传递一些参数值给插件:
1kind: ClusterRole
2apiVersion: rbac.authorization.k8s.io/v1
3metadata:
4 name: sample-scheduler-clusterrole
5rules:
6 - apiGroups:
7 - ""
8 resources:
9 - endpoints
10 - events
11 verbs:
12 - create
13 - get
14 - update
15 - apiGroups:
16 - ""
17 resources:
18 - nodes
19 verbs:
20 - get
21 - list
22 - watch
23 - apiGroups:
24 - ""
25 resources:
26 - pods
27 verbs:
28 - delete
29 - get
30 - list
31 - watch
32 - update
33 - apiGroups:
34 - ""
35 resources:
36 - bindings
37 - pods/binding
38 verbs:
39 - create
40 - apiGroups:
41 - ""
42 resources:
43 - pods/status
44 verbs:
45 - patch
46 - update
47 - apiGroups:
48 - ""
49 resources:
50 - replicationcontrollers
51 - services
52 verbs:
53 - get
54 - list
55 - watch
56 - apiGroups:
57 - apps
58 - extensions
59 resources:
60 - replicasets
61 verbs:
62 - get
63 - list
64 - watch
65 - apiGroups:
66 - apps
67 resources:
68 - statefulsets
69 verbs:
70 - get
71 - list
72 - watch
73 - apiGroups:
74 - policy
75 resources:
76 - poddisruptionbudgets
77 verbs:
78 - get
79 - list
80 - watch
81 - apiGroups:
82 - ""
83 resources:
84 - persistentvolumeclaims
85 - persistentvolumes
86 verbs:
87 - get
88 - list
89 - watch
90 - apiGroups:
91 - ""
92 resources:
93 - configmaps
94 verbs:
95 - get
96 - list
97 - watch
98 - apiGroups:
99 - "storage.k8s.io"
100 resources:
101 - storageclasses
102 - csinodes
103 verbs:
104 - get
105 - list
106 - watch
107 - apiGroups:
108 - "coordination.k8s.io"
109 resources:
110 - leases
111 verbs:
112 - create
113 - get
114 - list
115 - update
116 - apiGroups:
117 - "events.k8s.io"
118 resources:
119 - events
120 verbs:
121 - create
122 - patch
123 - update
124---
125apiVersion: v1
126kind: ServiceAccount
127metadata:
128 name: sample-scheduler-sa
129 namespace: kube-system
130---
131kind: ClusterRoleBinding
132apiVersion: rbac.authorization.k8s.io/v1
133metadata:
134 name: sample-scheduler-clusterrolebinding
135 namespace: kube-system
136roleRef:
137 apiGroup: rbac.authorization.k8s.io
138 kind: ClusterRole
139 name: sample-scheduler-clusterrole
140subjects:
141- kind: ServiceAccount
142 name: sample-scheduler-sa
143 namespace: kube-system
144---
145apiVersion: v1
146kind: ConfigMap
147metadata:
148 name: scheduler-config
149 namespace: kube-system
150data:
151 scheduler-config.yaml: |
152 apiVersion: kubescheduler.config.k8s.io/v1beta1
153 kind: KubeSchedulerConfiguration
154 leaderElection:
155 leaderElect: true
156 leaseDuration: 15s
157 renewDeadline: 10s
158 resourceLock: endpointsleases
159 resourceName: sample-scheduler
160 resourceNamespace: kube-system
161 retryPeriod: 2s
162 profiles:
163 - schedulerName: sample-scheduler
164 plugins:
165 preFilter:
166 enabled:
167 - name: "sample-plugin"
168 filter:
169 enabled:
170 - name: "sample-plugin"
171 pluginConfig:
172 - name: sample-plugin
173 args: # runtime.Object
174 favorColor: "#326CE5"
175 favorNumber: 7
176 thanksTo: "Kubernetes"
177---
178apiVersion: apps/v1
179kind: Deployment
180metadata:
181 name: sample-scheduler
182 namespace: kube-system
183 labels:
184 component: sample-scheduler
185spec:
186 selector:
187 matchLabels:
188 component: sample-scheduler
189 template:
190 metadata:
191 labels:
192 component: sample-scheduler
193 spec:
194 serviceAccountName: sample-scheduler-sa
195 priorityClassName: system-cluster-critical
196 volumes:
197 - name: scheduler-config
198 configMap:
199 name: scheduler-config
200 containers:
201 - name: scheduler
202 image: cnych/sample-scheduler:v0.2.4
203 imagePullPolicy: IfNotPresent
204 command:
205 - sample-scheduler
206 - --config=/etc/kubernetes/scheduler-config.yaml
207 - --v=3
208 volumeMounts:
209 - name: scheduler-config
210 mountPath: /etc/kubernetes
211# livenessProbe:
212# httpGet:
213# path: /healthz
214# port: 10251
215# initialDelaySeconds: 15
216# readinessProbe:
217# httpGet:
218# path: /healthz
219# port: 10251直接部署上面的资源对象即可,这样我们就部署了一个名为 sample-scheduler 的调度器了,接下来我们可以部署一个应用来使用这个调度器进行调度:
1apiVersion: apps/v1
2kind: Deployment
3metadata:
4 name: test-scheduler
5spec:
6 selector:
7 matchLabels:
8 app: test-scheduler
9 template:
10 metadata:
11 labels:
12 app: test-scheduler
13 spec:
14 schedulerName: sample-scheduler # 指定使用的调度器,不指定使用默认的default-scheduler
15 containers:
16 - image: nginx:1.7.9
17 imagePullPolicy: IfNotPresent
18 name: nginx
19 ports:
20 - containerPort: 80这里需要注意的是我们现在手动指定了一个 schedulerName 的字段,将其设置成上面我们自定义的调度器名称 sample-scheduler。
我们直接创建这个资源对象,创建完成后查看我们自定义调度器的日志信息:
1➜ kubectl get pods -n kube-system -l component=sample-scheduler
2NAME READY STATUS RESTARTS AGE
3sample-scheduler-896658cd7-k7vcl 1/1 Running 0 57s
4➜ kubectl logs -f sample-scheduler-896658cd7-k7vcl -n kube-system
5I0114 09:14:18.878613 1 eventhandlers.go:173] add event for unscheduled pod default/test-scheduler-6486fd49fc-zjhcx
6I0114 09:14:18.878670 1 scheduler.go:464] Attempting to schedule pod: default/test-scheduler-6486fd49fc-zjhcx
7I0114 09:14:18.878706 1 sample.go:77] "Start PreFilter Pod" pod="test-scheduler-6486fd49fc-zjhcx"
8I0114 09:14:18.878802 1 sample.go:93] "Start Filter Pod" pod="test-scheduler-6486fd49fc-zjhcx" node="node2" preFilterState=&{Resource:{MilliCPU:0 Memory:0 EphemeralStorage:0 AllowedPodNumber:0 ScalarResources:map[]}}
9I0114 09:14:18.878835 1 sample.go:93] "Start Filter Pod" pod="test-scheduler-6486fd49fc-zjhcx" node="node1" preFilterState=&{Resource:{MilliCPU:0 Memory:0 EphemeralStorage:0 AllowedPodNumber:0 ScalarResources:map[]}}
10I0114 09:14:18.879043 1 default_binder.go:51] Attempting to bind default/test-scheduler-6486fd49fc-zjhcx to node1
11I0114 09:14:18.886360 1 scheduler.go:609] "Successfully bound pod to node" pod="default/test-scheduler-6486fd49fc-zjhcx" node="node1" evaluatedNodes=3 feasibleNodes=2
12I0114 09:14:18.887426 1 eventhandlers.go:205] delete event for unscheduled pod default/test-scheduler-6486fd49fc-zjhcx
13I0114 09:14:18.887475 1 eventhandlers.go:225] add event for scheduled pod default/test-scheduler-6486fd49fc-zjhcx可以看到当我们创建完 Pod 后,在我们自定义的调度器中就出现了对应的日志,并且在我们定义的扩展点上面都出现了对应的日志,证明我们的示例成功了,也可以通过查看 Pod 的 schedulerName 来验证:
1➜ kubectl get pods
2NAME READY STATUS RESTARTS AGE
3test-scheduler-6486fd49fc-zjhcx 1/1 Running 0 35s
4➜ kubectl get pod test-scheduler-6486fd49fc-zjhcx -o yaml
5......
6restartPolicy: Always
7schedulerName: sample-scheduler
8securityContext: {}
9serviceAccount: default
10......从 Kubernetes v1.17 版本开始,Scheduler Framework 内置的预选和优选函数已经全部插件化,所以要扩展调度器我们应该掌握并理解调度框架这种方式。
作为 kubernetes 集群的默认调度器,kube-scheduler 主要负责将 Pod 调度到集群的 Node 上。在一个集群中,满足一个 Pod 调度请求的所有节点称之为 可调度 Node,调度器先在集群中找到一个 Pod 的可调度 Node,然后根据一系列函数对这些可调度 Node 进行打分,之后选出其中得分最高的 Node 来运行 Pod,最后,调度器将这个调度决定告知 kube-apiserver,这个过程叫做绑定。
在 Kubernetes 1.12 版本之前,kube-scheduler 会检查集群中所有节点的可调度性,并且给可调度节点打分。Kubernetes 1.12 版本添加了一个新的功能,允许调度器在找到一定数量的可调度节点之后就停止继续寻找可调度节点。该功能能提高调度器在大规模集群下的调度性能,这个数值是集群规模的百分比,这个百分比通过 percentageOfNodesToScore 参数来进行配置,其值的范围在 1 到 100 之间,最大值就是 100%,如果设置为 0 就代表没有提供这个参数配置。
Kubernetes 1.14 版本又加入了一个特性,在该参数没有被用户配置的情况下,调度器会根据集群的规模自动设置一个集群比例,然后通过这个比例筛选一定数量的可调度节点进入打分阶段。该特性使用线性公式计算出集群比例,比如100个节点的集群下会取 50%,在 5000节点的集群下取 10%,这个自动设置的参数的最低值是 5%,换句话说,调度器至少会对集群中 5% 的节点进行打分,除非用户将该参数设置的低于 5。
注意
当集群中的可调度节点少于 50 个时,调度器仍然会去检查所有节点,因为可调度节点太少,不足以停止调度器最初的过滤选择。如果我们想要关掉这个范围参数,可以将
percentageOfNodesToScore值设置成 100。
percentageOfNodesToScore 的值必须在 1 到 100 之间,而且其默认值是通过集群的规模计算得来的,另外 50 个 Node 的数值是硬编码在程序里面的,设置这个值的作用在于:当集群的规模是数百个节点并且 percentageOfNodesToScore 参数设置的过低的时候,调度器筛选到的可调度节点数目基本不会受到该参数影响。当集群规模较小时,这个设置对调度器性能提升并不明显,但是在超过 1000 个 Node 的集群中,将调优参数设置为一个较低的值可以很明显的提升调度器性能。
不过值得注意的是,该参数设置后可能会导致只有集群中少数节点被选为可调度节点,很多 Node 都没有进入到打分阶段,这样就会造成一种后果,一个本来可以在打分阶段得分很高的 Node 甚至都不能进入打分阶段。由于这个原因,所以这个参数不应该被设置成一个很低的值,通常的做法是不会将这个参数的值设置的低于 10,很低的参数值一般在调度器的吞吐量很高且对 Node 的打分不重要的情况下才使用。换句话说,只有当你更倾向于在可调度节点中任意选择一个 Node 来运行这个 Pod 时,才使用很低的参数设置。
如果你的集群规模只有数百个节点或者更少,实际上并不推荐你将这个参数设置得比默认值更低,因为这种情况下不太会有效的提高调度器性能。
一般情况下我们部署的 Pod 是通过集群的自动调度策略来选择节点的,默认情况下调度器考虑的是资源足够,并且负载尽量平均。但是有的时候我们需要能够更加细粒度的去控制 Pod 的调度,比如我们希望一些机器学习的应用只跑在有 GPU 的节点上;但是有的时候我们的服务之间交流比较频繁,又希望能够将这服务的 Pod 都调度到同一个的节点上。这就需要使用一些调度方式来控制 Pod 的调度了,主要有两个概念:亲和性和反亲和性,亲和性又分成节点亲和性(nodeAffinity)和 Pod 亲和性(podAffinity)。
Kubernetes基于list-watch机制的控制器架构,实现组件间交互的解耦。
其他组件监控自己负责的资源,当这些资源发生变化时,kube-apiserver会通知这些组件,这个过程类似于发布与订阅。


kubectl run pod4 --image=nginx后,各组件之间的调用流程是如何的呢?11、kubectl向apiserver发送一个创建pod的请求
22、apiserver接收到并向etcd写入存储,写入成功返回一个提示
3
4#这个过程类似于老板和顾客之间的关系:
5 api-server:开店老板
6 etcd:仓库
7 其他组件:顾客
kubectl run pod4 --image=nginx命令时,卡着了,说明什么问题呢?–>说明: etcd数据库写入有问题/达到性能瓶颈,或者,api-server和etcd 2者总有一个有问题:


答:pod会出现通过kubectl get pod根本看不到你刚创建的pod信息的,更别说它的状态了,因为它根本没分配。
因此,如果你创建的pod信息根本看不到,那么会是哪个组件可能有问题?–>scheduler组件可能出在问题。
14、kubelet向apiserver查询分配到自己节点的pod,调用docker api(/var/run/docker.sock)创建容器
25、kubelet获取docker创建容器的状态,并汇报给apiserver,apiserver更新状态到etcd存储
36、kubectl get pods就能查看pod状态
4
5备注:
6kubelet的功能主要是管理容器:
7
8这个是默认调用的docker api接口
9[root@k8s-master ~]#ll /var/run/docker.sock
10srw-rw---- 1 root docker 0 Jun 14 11:46 /var/run/docker.sock

controller-manager是用于管理控制器,例如deployment(rs)、service,因为创建的是一个pod,不受它管理。
如果controller-manager要放在这里,一般是放在etcd后面的:

proxy是用于管理pod网络,例如service,因为没创建service。
kube-proxy的主要功能就是维护好service,service是k8s的抽象资源;
比如,这个容器创建的时候创建失败了,不是一个running状态,也不是一个pending状态。可能就是docker在启动容器时,用你那个镜像启动容器失败了。所以这是你需要用docker去run一个镜像看能不能起来。

注意
resources: {} 资源调度依据这个,挺重要的; 很多大厂都会去二开"schedulerName: default-scheduler"这个调度器的,会去加一些调度策略,进而完成他们的需求;
🍂 调度原因失败分析
1kubectl get pod <NAME> -o wide
2
3查看调度失败原因:kubectl describe pod <NAME>
4• 节点CPU/内存不足
5• 有污点,没容忍 (tolerations)
6• 没有匹配到节点标签 (n)
资源高效利用:装箱率要高!
afinity:微服务,分步式系统,网络调用,本机调用,排除了网络调用,额外的传输时间,物理网卡带宽限制!
anti-affinity:某个业务的不同副本,不能让其跑在一台机器上,一个机架上,一个地域里,使其分布在不同的故障域。
locality:数据本地化,是一个很重要的概念,哪里有数据,我的作业就去哪里,这样可以减少数据拷贝的开销。k8s里的拉取镜像。
🍂
听起来很简单,但这个过程中会涉及Predicate、Priority等各种调度算法,还有优先级(Priority )、**抢占(Preemption)**等各种机制。在实际的调度设计中,有非常多需要考虑的问题,比如:
1. 公平:如何保证每个节点都能被分配资源
2. 资源高效利用:怎样压榨集群的资源能力,让资源被最大化使用
3. 效率:调度的性能要好,能够尽快地对大批量的 pod 完成调度工作
4. 灵活:允许用户根据自己的需求控制调度的逻辑

1.limits:在Cgroups里使用;cpu.cfs_quota/cpu.cfs_period(10w)=1 2.requests:cpu这个requests其实在Cgroup里也起作用。当你多个应用发生资源抢占时,他们抢占的cpu时间比较是多少呢?是通过cpu.share去调节的。k8s是如何实现的呢?这里如果设置的是一个cpu,request是1的话,那么cpu.share是1024。 如果你设置的是100m,相当于是0.1个cpu,那么cpu.share就是0.1*1024=102. 也就是cpu.requests也是最终会体现到Cgroups里面去的。

nodeSelector:用于将Pod调度到匹配Label的Node上,如果没有匹配的标签会调度失败。
作用:
应用场景:
| 💘 实践:nodeSelector测试(测试成功)-2022.5.16 |
|---|
在了解亲和性之前,我们先来了解一个非常常用的调度方式:nodeSelector。我们知道 label 标签是 kubernetes 中一个非常重要的概念,用户可以非常灵活的利用 label 来管理集群中的资源,比如最常见的 Service 对象通过 label 去匹配 Pod 资源,而 Pod 的调度也可以根据节点的 label 来进行调度。
1[root@master1 ~]#kubectl get node --show-labels
2NAME STATUS ROLES AGE VERSION LABELS
3master1 Ready control-plane,master 109d v1.22.2 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=master1,kubernetes.io/os=linux,node-role.kubernetes.io/control-plane=,node-role.kubernetes.io/master=,node.kubernetes.io/exclude-from-external-load-balancers=
4node1 Ready <none> 109d v1.22.2 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=node1,kubernetes.io/os=linux
5node2 Ready <none> 109d v1.22.2 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=node2,kubernetes.io/os=linuxcom=youdianzhishi的标签,命令如下:1[root@master1 ~]#kubectl label nodes node2 com=youdianzhishi
2node/node2 labeled我们可以通过上面的 --show-labels 参数可以查看上述标签是否生效。
1[root@master1 ~]#kubectl get node node2 --show-labels
2NAME STATUS ROLES AGE VERSION LABELS
3node2 Ready <none> 109d v1.22.2 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,com=youdianzhishi,kubernetes.io/arch=amd64,kubernetes.io/hostname=node2,kubernetes.io/os=linux当节点被打上了相关标签后,在调度的时候就可以使用这些标签了,只需要在 Pod 的 spec 字段中添加 nodeSelector 字段,里面是我们需要被调度的节点的 label 标签,比如,下面的 Pod 我们要强制调度到 node2 这个节点上去,我们就可以使用 nodeSelector 来表示了:
$ vim 01-node-selector-demo.yaml
1# 01-node-selector-demo.yaml
2apiVersion: v1
3kind: Pod
4metadata:
5 labels:
6 app: busybox-pod
7 name: test-busybox
8spec:
9 containers:
10 - command:
11 - sleep
12 - "3600"
13 image: busybox
14 imagePullPolicy: Always
15 name: test-busybox
16 nodeSelector: #注意:nodeSelector是和containers同级的;注意,这个放的顺序一定要放在containers后面。不然会报错的!
17 com: youdianzhishi 1hg@LAPTOP-G8TUFE0T:/mnt/c/Users/hg/Desktop/yaml$ kubectl apply -f 01-node-selector-demo.yaml
2pod/test-busybox created
3
4hg@LAPTOP-G8TUFE0T:/mnt/c/Users/hg/Desktop/yaml$ kubectl get po -owide
5NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
6test-busybox 1/1 Running 0 78s 10.244.2.210 node2 <none> <none>
7
8
9[root@master1 ~]#kubectl describe po test-busybox
10Name: test-busybox
11Namespace: default
12Priority: 0
13Node: node2/172.29.9.53
14Start Time: Thu, 17 Feb 2022 19:45:11 +0800
15Labels: app=busybox-pod
16Annotations: <none>
17Status: Running
18IP: 10.244.2.210
19IPs:
20 IP: 10.244.2.210
21Containers:
22 test-busybox:
23 Container ID: containerd://1b4d323942e6d305a4ea25f655eaced77f8cb8e4229eaf1972dc9dfb1246a0c0
24 Image: busybox
25 Image ID: docker.io/library/busybox@sha256:5acba83a746c7608ed544dc1533b87c737a0b0fb730301639a0179f9344b1678
26 Port: <none>
27 Host Port: <none>
28 Command:
29 sleep
30 3600
31 State: Running
32 Started: Thu, 17 Feb 2022 19:45:31 +0800
33 Ready: True
34 Restart Count: 0
35 Environment: <none>
36 Mounts:
37 /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-p5z6t (ro)
38Conditions:
39 Type Status
40 Initialized True
41 Ready True
42 ContainersReady True
43 PodScheduled True
44Volumes:
45 kube-api-access-p5z6t:
46 Type: Projected (a volume that contains injected data from multiple sources)
47 TokenExpirationSeconds: 3607
48 ConfigMapName: kube-root-ca.crt
49 ConfigMapOptional: <nil>
50 DownwardAPI: true
51QoS Class: BestEffort
52Node-Selectors: com=youdianzhishi
53Tolerations: node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
54 node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
55Events:
56 Type Reason Age From Message
57 ---- ------ ---- ---- -------
58 Normal Scheduled 2m45s default-scheduler Successfully assigned default/test-busybox to node2
59 Normal Pulling 2m43s kubelet Pulling image "busybox"
60 Normal Pulled 2m26s kubelet Successfully pulled image "busybox" in 17.583571931s
61 Normal Created 2m26s kubelet Created container test-busybox
62 Normal Started 2m25s kubelet Started container test-busybox
63[root@master1 ~]#我们可以看到 Events 下面的信息,我们的 Pod 通过默认的 default-scheduler 调度器被绑定到了 node2 节点。不过需要注意的是nodeSelector 属于强制性的,如果我们的目标节点没有可用的资源,我们的 Pod 就会一直处于 Pending 状态。
通过上面的例子我们可以感受到 nodeSelector 的方式比较直观,但是还不够灵活,控制粒度偏大。接下来我们再和大家了解下更加灵活的方式:节点亲和性(nodeAffinity)。
测试结束。😘
🍂 问题:
因pod中nodeSelector里的标签未出现在all node节点,但后续给node打好符合要求的标签,原来处于pending状态的pod会自动迁移过去的吗?–>会的。
前面我们了解了 kubernetes 调度器的调度流程,我们知道默认的调度器在使用的时候,经过了 predicates 和 priorities 两个阶段。但是在实际的生产环境中,往往我们需要根据自己的一些实际需求来控制 Pod 的调度,这就需要用到 nodeAffinity(节点亲和性)、podAffinity(pod 亲和性) 以及 podAntiAffinity(pod 反亲和性)。
亲和性调度可以分成软策略和硬策略两种方式:
软策略就是如果现在没有满足调度要求的节点的话,Pod 就会忽略这条规则,继续完成调度过程,说白了就是满足条件最好了,没有的话也无所谓硬策略就比较强硬了,如果没有满足条件的节点的话,就不断重试直到满足条件为止,简单说就是你必须满足我的要求,不然就不干了对于亲和性和反亲和性都有这两种规则可以设置: preferredDuringSchedulingIgnoredDuringExecution 和requiredDuringSchedulingIgnoredDuringExecution,前面的就是软策略,后面的就是硬策略。
节点亲和性(nodeAffinity)主要是用来控制 Pod 要部署在哪些节点上,以及不能部署在哪些节点上的,它可以进行一些简单的逻辑组合了,不只是简单的相等匹配(比如前面的nodeSelector就是标签的)。
nodeAffinity:节点亲和类似于nodeSelector,可以根据节点上的标签来约束Pod可以调度到哪些节点。
相比nodeSelector:
In、NotIn、Exists、DoesNotExist、Gt、Lt这里的匹配逻辑是 label 标签的值在某个列表中,现在 Kubernetes 提供的操作符有下面的几种:
注意:
但是需要注意的是如果
nodeSelectorTerms下面有多个选项的话,满足任何一个条件就可以了;如果matchExpressions有多个选项的话,则必须同时满足这些条件才能正常调度 Pod。
比nodeSelector更高级的一个! matchExpressions比selector更加灵活。
==💘 实践:节点亲和性测试-2022.5.16(测试成功)==
1# 02-node-affinity-demo.yaml
2apiVersion: apps/v1
3kind: Deployment
4metadata:
5 name: node-affinity
6 labels:
7 app: node-affinity
8spec:
9 replicas: 8
10 selector:
11 matchLabels:
12 app: node-affinity
13 template:
14 metadata:
15 labels:
16 app: node-affinity
17 spec:
18 containers:
19 - name: nginx
20 image: nginx:1.7.9
21 ports:
22 - containerPort: 80
23 name: nginxweb
24 affinity: #定义亲和性
25 nodeAffinity: #节点亲和性
26 requiredDuringSchedulingIgnoredDuringExecution: # 硬策略
27 nodeSelectorTerms:
28 - matchExpressions:
29 - key: kubernetes.io/hostname
30 operator: NotIn
31 values:
32 - master1 #相当于只能调度到node1和node2节点。默认就不会调度到master1节点
33 preferredDuringSchedulingIgnoredDuringExecution: # 软策略(尽可能调度到node2节点)
34 - weight: 1
35 preference:
36 matchExpressions:
37 - key: com
38 operator: In
39 values:
40 - youdianzhishi上面这个 Pod 首先是要求不能运行在 master1 这个节点上,如果有个节点满足 com=youdianzhishi 的话就优先调度到这个节点上。
由于上面 node02 节点我们打上了 com=youdianzhishi 这样的 label 标签,所以按要求会优先调度到这个节点来的。
1➜ kubectl apply -f node-affinty-demo.yaml
2deployment.apps/node-affinity created
3➜ kubectl get pods -l app=node-affinity -o wide #老师这个有部分pod被调度到node1节点
4NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
5node-affinity-cdd9d54d9-bgbbh 1/1 Running 0 2m28s 10.244.2.247 node2 <none> <none>
6node-affinity-cdd9d54d9-dlbck 1/1 Running 0 2m28s 10.244.4.16 node1 <none> <none>
7node-affinity-cdd9d54d9-g2jr6 1/1 Running 0 2m28s 10.244.4.17 node1 <none> <none>
8node-affinity-cdd9d54d9-gzr58 1/1 Running 0 2m28s 10.244.1.118 node1 <none> <none>
9node-affinity-cdd9d54d9-hcv7r 1/1 Running 0 2m28s 10.244.2.246 node2 <none> <none>
10node-affinity-cdd9d54d9-kvxw4 1/1 Running 0 2m28s 10.244.2.245 node2 <none> <none>
11node-affinity-cdd9d54d9-p4mmk 1/1 Running 0 2m28s 10.244.2.244 node2 <none> <none>
12node-affinity-cdd9d54d9-t5mff 1/1 Running 0 2m28s 10.244.1.117 node2 <none> <none>从结果可以看出有5个 Pod 被部署到了 node2 节点上,但是可以看到并没有一个 Pod 被部署到 master1 这个节点上,因为我们的硬策略就是不允许部署到该节点上,而 node2 是软策略,所以会尽量满足。
测试结束。😘
Pod 亲和性(podAffinity)主要解决 Pod 可以和哪些 Pod 部署在同一个拓扑域中的问题(其中拓扑域用主机标签实现,可以是单个主机,也可以是多个主机组成的 cluster、zone 等等),而 Pod 反亲和性主要是解决 Pod 不能和哪些 Pod 部署在同一个拓扑域中的问题,它们都是处理的 Pod 与 Pod 之间的关系。比如一个 Pod 在一个节点上了,那么我这个也得在这个节点,或者你这个 Pod 在节点上了,那么我就不想和你待在同一个节点上。
这个是很重要的,线上业务基本要配置这种podAntiAffinity。
这里要重点理解下什么是拓扑域?–>你可以把它看成为一个分组。

==💘 实验:pod亲和性-2022.5.16(测试成功)==
1[root@master1 ~]# kubectl get node --show-labels
2NAME STATUS ROLES AGE VERSION LABELS
3master1 Ready control-plane,master 110d v1.22.2 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=master1,kubernetes.io/os=linux,node-role.kubernetes.io/control-plane=,node-role.kubernetes.io/master=,node.kubernetes.io/exclude-from-external-load-balancers=
4node1 Ready <none> 110d v1.22.2 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=node1,kubernetes.io/os=linux
5node2 Ready <none> 110d v1.22.2 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,com=youdianzhishi,kubernetes.io/arch=amd64,kubernetes.io/hostname=node2,kubernetes.io/os=linux 1# 03-pod-affinity-demo.yaml
2apiVersion: apps/v1
3kind: Deployment
4metadata:
5 name: pod-affinity
6 labels:
7 app: pod-affinity
8spec:
9 replicas: 3
10 selector:
11 matchLabels:
12 app: pod-affinity
13 template:
14 metadata:
15 labels:
16 app: pod-affinity
17 spec:
18 containers:
19 - name: nginx
20 image: nginx
21 ports:
22 - containerPort: 80
23 name: nginxweb
24 affinity:
25 podAffinity:
26 requiredDuringSchedulingIgnoredDuringExecution: # 硬策略
27 - labelSelector: #去选择具有app in ["busybox-pod"]的pod所在的hostname这个域。
28 matchExpressions:
29 - key: app
30 operator: In
31 values:
32 - busybox-pod
33 topologyKey: kubernetes.io/hostname上面这个例子中的 Pod 需要调度到某个指定的节点上,并且该节点上运行了一个带有 app=busybox-pod 标签的 Pod。我们可以查看有标签 app=busybox-pod 的 pod 列表:
1[root@master1 ~]#kubectl get pods -l app=busybox-pod -o wide
2NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
3test-busybox 1/1 Running 5 (23m ago) 18h 10.244.2.210 node2 <none> <none>1$ kubectl apply -f 03-pod-affinity-demo.yaml
2deployment.apps/pod-affinity created
3$ kubectl get pods -o wide -l app=pod-affinity
4NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
5pod-affinity-785f687c5-52t54 1/1 Running 0 60s 10.244.2.228 node2 <none> <none>
6pod-affinity-785f687c5-g594p 1/1 Running 0 60s 10.244.2.229 node2 <none> <none>
7pod-affinity-785f687c5-s6j7h 1/1 Running 0 60s 10.244.2.227 node2 <none> <none> 1$ kubectl delete -f 01-node-selector-demo.yaml
2pod "test-busybox" deleted
3$ kubectl delete -f 03-pod-affinity-demo.yaml
4deployment.apps "pod-affinity" deleted
5
6$ kubectl apply -f 03-pod-affinity-demo.yaml
7deployment.apps/pod-affinity created
8$ kubectl get po
9NAME READY STATUS RESTARTS AGE
10pod-affinity-785f687c5-2256q 0/1 Pending 0 80s
11pod-affinity-785f687c5-7gpz5 0/1 Pending 0 80s
12pod-affinity-785f687c5-97gpj 0/1 Pending 0 80s我们可以看到都处于 Pending 状态了,这是因为现在没有一个节点上面拥有 app=busybox-pod 这个标签的 Pod,而上面我们的调度使用的是硬策略,所以就没办法进行调度了,大家可以去尝试下重新将 test-busybox 这个 Pod 调度到其他节点上,观察下上面的3个副本会不会也被调度到对应的节点上去。(这里可以自己测试下,可以利用node1的kubernetes.io/hostname: node1标签用nodeSelector来实现)
kubernetes.io/hostname 这个拓扑域,意思就是我们当前调度的 Pod 要和目标的 Pod 处于同一个主机上面,因为要处于同一个拓扑域下面。为了说明这个问题,我们把拓扑域改成 beta.kubernetes.io/os,同样的我们当前调度的 Pod 要和目标的 Pod 处于同一个拓扑域中,目标的 Pod 是拥有 beta.kubernetes.io/os=linux 的标签,而我们这里所有节点都有这样的标签,这也就意味着我们所有节点都在同一个拓扑域中,所以我们这里的 Pod 可以被调度到任何一个节点,重新运行上面的 app=busybox-pod 的 Pod,然后再更新下我们这里的资源对象:1$ kubectl get po -owide
2NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
3pod-affinity-6bf5bb4fc4-j6ctw 1/1 Running 0 22s 10.244.2.230 node2 <none> <none>
4pod-affinity-6bf5bb4fc4-xb7tr 1/1 Running 0 22s 10.244.2.231 node2 <none> <none>
5pod-affinity-6bf5bb4fc4-xl6pn 1/1 Running 0 22s 10.244.1.97 node1 <none> <none>
可以看到现在是分别运行在2个节点下面的,因为他们都属于 beta.kubernetes.io/os 这个拓扑域(而busybox-pod也刚好在这个域下,因此符合硬策略要求)。
这里需要注意下:通过上面这个实验可以看到,这2个node节点都属于
beta.kubernetes.io/os这个拓扑域,但只有node1上有app=pod-affitity这个标签的pod,从结果可以看到也是可以调度到node2上的。
实验结束。😘
pod 反亲和性(podAntiAffinity)
Pod 反亲和性(podAntiAffinity)则是反着来的,比如一个节点上运行了某个 Pod,那么我们的模板 Pod 则不希望被调度到这个节点上面去了。
Pod反亲和性可避免单点故障。
1# 软策略,本次后面测试就用软策略了哈。
2affinity:
3 podAntiAffinity:
4 preferredDuringSchedulingIgnoredDuringExecution: # 软策略
5 - weight: 1
6 podAffinityTerm:
7 topologyKey: kubernetes.io/hostname
8 labelSelector:
9 matchExpressions:
10 - key: app
11 operator: In
12 values:
13 - wordpress
14
15
16
17……
18# 硬策略
19 affinity:
20 podAntiAffinity:
21 requiredDuringSchedulingIgnoredDuringExecution: # 硬策略
22 - labelSelector:
23 matchExpressions:
24 - key: app
25 operator: In
26 values:
27 - wordpress
28 topologyKey: kubernetes.io/hostname 注意:层级关系deployment.spec.template.spec.affinity


==💘 实验:pod反亲和性(测试成功)-2022.5.16==
podAffinity 直接改成 podAntiAffinity: 1# 04-pod-antiaffinity-demo.yaml
2apiVersion: apps/v1
3kind: Deployment
4metadata:
5 name: pod-antiaffinity
6 labels:
7 app: pod-antiaffinity
8spec:
9 replicas: 3
10 selector:
11 matchLabels:
12 app: pod-antiaffinity
13 template:
14 metadata:
15 labels:
16 app: pod-antiaffinity
17 spec:
18 containers:
19 - name: nginx
20 image: nginx
21 ports:
22 - containerPort: 80
23 name: nginxweb
24 affinity:
25 podAntiAffinity:
26 requiredDuringSchedulingIgnoredDuringExecution: # 硬策略
27 - labelSelector: #3个pod副本不会调度到具有app=busybox-pod所在的hostanme这个域(节点)上面
28 matchExpressions:
29 - key: app
30 operator: In
31 values:
32 - busybox-pod
33 topologyKey: kubernetes.io/hostname #注意:pod反亲和性是直接不往这个域里直接调度pod的!!!这里的意思就是如果一个节点上面有一个 app=busybox-pod 这样的 Pod 的话,那么我们的 Pod 就别调度到这个节点上面来,上面我们把app=busybox-pod 这个 Pod 固定到了 node2 这个节点上面的,所以正常来说我们这里的 Pod 不会出现在该节点上:
1$ kubectl apply -f 04-pod-antiaffinity-demo.yaml
2deployment.apps/pod-antiaffinity created
3$ kubectl get po -owide
4NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
5pod-antiaffinity-57c57dd9f7-jspkt 1/1 Running 0 25s 10.244.1.100 node1 <none> <none>
6pod-antiaffinity-57c57dd9f7-mm78w 1/1 Running 0 25s 10.244.1.99 node1 <none> <none>
7pod-antiaffinity-57c57dd9f7-x9mft 1/1 Running 0 25s 10.244.1.98 node1 <none> <none>我们可以看到没有被调度到 node2 节点上,因为我们这里使用的是 Pod 反亲和性。
beta.kubernetes.io/os 会怎么样呢?可以自己去测试下看看。1$ kubectl apply -f 04-pod-antiaffinity-demo.yaml
2deployment.apps/pod-antiaffinity created
3$ kubectl get po
4NAME READY STATUS RESTARTS AGE
5pod-antiaffinity-c5fb4db4d-jj5j7 0/1 Pending 0 4s
6pod-antiaffinity-c5fb4db4d-xxnhg 0/1 Pending 0 4s
7pod-antiaffinity-c5fb4db4d-zkbgp 0/1 Pending 0 4s实验结束。😘
k8s train5的 应用案例一节,在做wordpress的Pod反亲和性时,我这里报错了,一个pod一直处于pending,集群资源也够,这个是啥问题,知道不。
k8s里,可调度节点数为2,pod的副本数为2,如果设置了pod反亲和性的硬策略,其中拓扑域为节点名称,那么2个pod的副本应该是可以正常调度的。



是因为mysql的标签也是app: wordpress,这样导致wordpress的一个副本无法被调度。

更改标签后,就可以被调度了:

Taint(污点)与Tolerations(污点容忍)
Taints:避免Pod调度到特定Node上
Tolerations:允许Pod调度到持有Taints的Node上
应用场景:
对于 nodeAffinity 无论是硬策略还是软策略方式,都是调度 Pod 到预期节点上。而污点(Taints)恰好与之相反,如果一个节点标记为 Taints ,除非 Pod 也被标识为可以容忍污点节点,否则该 Taints 节点不会被调度 Pod。
比如用户希望把 Master 节点保留给 Kubernetes 系统组件使用,或者把一组具有特殊资源预留给某些 Pod,则污点就很有用了,Pod 不会再被调度到 taint 标记过的节点。我们使用 kubeadm 搭建的集群默认就给 master 节点添加了一个污点标记,所以我们看到我们平时的 Pod 都没有被调度到 master 上去。
污点:其实是一个label标签,只不过它是一个特殊的label标签。
1[root@master1 ~]#kubectl get node master1 --show-labels
2NAME STATUS ROLES AGE VERSION LABELS
3master1 Ready control-plane,master 110d v1.22.2 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=master1,kubernetes.io/os=linux,node-role.kubernetes.io/control-plane=,node-role.kubernetes.io/master=(注意,这个是一个空标签),node.kubernetes.io/exclude-from-external-load-balancers=
4
5[root@master1 ~]#kubectl describe node master1
6Name: master1
7Roles: master
8Labels: beta.kubernetes.io/arch=amd64
9 beta.kubernetes.io/os=linux
10 kubernetes.io/arch=amd64
11 kubernetes.io/hostname=master1
12 kubernetes.io/os=linux
13 node-role.kubernetes.io/master=
14......
15Taints: node-role.kubernetes.io/master:NoSchedule
16Unschedulable: false
17......我们可以使用上面的命令查看 master 节点的信息,其中有一条关于 Taints 的信息:node-role.kubernetes.io/master:NoSchedule,就表示master 节点打了一个污点的标记,其中影响的参数是 NoSchedule,表示 Pod 不会被调度到标记为 taints 的节点。除了 NoSchedule 外,还有另外两个选项:
kubectl taint node k8s-node1 disktype=ssd:NoExecute
🍂 污点 taint 标记节点的命令如下:
1➜ kubectl taint nodes node2 test=node2:NoSchedule
2node "node2" tainted上面的命名将 node2 节点标记为了污点,影响策略是 NoSchedule,只会影响新的 Pod 调度,如果仍然希望某个 Pod 调度到 taint 节点上,则必须在 Spec 中做出 Toleration 定义,才能调度到该节点。
🍂 最后如果我们要取消节点的污点标记,可以使用下面的命令:
1➜ kubectl taint nodes node2 test-
2node "node2" untainted🍂
Taints和Tolerations用于保证Pod 不被调度到不合适的Node上,其中Taint应用于Node上,而Toleration则应用于Pod上。
🍂 nodeSelector/nodeAffinity与Taint/Tolerations区别
1nodeSelector/nodeAffinity--一种主观意识,我要分配在哪个node节点上去;
2而Taint/Tolerations:是一种排斥行为,在node上排斥pod分配到自己身上;
3
4nodeSelector/nodeAffinity--管理员强迫node
5taint/toleration--node主动的一脸嫌弃
对于 tolerations 属性的写法,其中的 key、value、effect 与 Node 的 Taint 设置需保持一致, 还有以下几点说明:
Exists,则 value 属性可省略Equal,则表示其 key 与 value 之间的关系是 equal(等于)Equal另外,还有两个特殊值:
Exists就能匹配所有的 key 与 value,也就是是能容忍所有节点的所有 Taints !!!😋🍀 例子
master节点上,为什么除了静态pod之外,上面还有calico/kube-proxy组件可以运行。
为什么呢?原因在这里:污点容忍是可以直接放宽条件写的,例如calico,不分什么key什么value,符合带有污点是NoSchedule都可以允许分配。





1# 05-taint-demo.yaml
2apiVersion: apps/v1
3kind: Deployment
4metadata:
5 name: taint
6 labels:
7 app: taint
8spec:
9 replicas: 3
10 selector:
11 matchLabels:
12 app: taint
13 template:
14 metadata:
15 labels:
16 app: taint
17 spec:
18 containers:
19 - name: nginx
20 image: nginx
21 ports:
22 - name: http
23 containerPort: 80
24 tolerations:
25 - key: "node-role.kubernetes.io/master"
26 operator: "Exists"
27 effect: "NoSchedule"由于 master 节点被标记为了污点,所以我们这里要想 Pod 能够调度到改节点去,就需要增加容忍的声明:
1tolerations:
2- key: "node-role.kubernetes.io/master"
3 operator: "Exists"
4 effect: "NoSchedule" 1➜ kubectl apply -f taint-demo.yaml
2deployment.apps "taint" created
3
4➜ kubectl get pods -o wide
5NAME READY STATUS RESTARTS AGE IP NODE
6......
7taint-845d8bb4fb-57mhm 1/1 Running 0 1m 10.244.4.247 node2
8taint-845d8bb4fb-bbvmp 1/1 Running 0 1m 10.244.0.33 master1
9taint-845d8bb4fb-zb78x 1/1 Running 0 1m 10.244.4.246 node2
10......我们可以看到有一个 Pod 副本被调度到了 master 节点,这就是容忍的使用方法。
那么问题来了:我这个pod可以调度到没打这个污点的node上去吗?还是说会优先调度到这个打了污点的节点上去呢?。。。
–>经测试:是可以调度上去的。pod配置了容忍之后,原来打了污点的node就可以和其他节点一样去负载pod了,具体调度策略是看调度器的。
1➜ kubectl taint nodes node2 test-
2node "node2" untainted实验结束。😘
nodeName:指定节点名称,用于将Pod调度到指定的Node上,不经过调度器 (nodeName指哪打哪😂,nodeName一般在测试中用,但工作中根本没怎么用到)
| 💘 实践:nodeName测试(测试成功)-2022.5.16 |
|---|
1[root@k8s-master ~]#kubectl run pod6 --image=nginx --dry-run=client -o yaml > pod6.yaml
2[root@k8s-master ~]#vim pod6.yaml
3apiVersion: v1
4kind: Pod
5metadata:
6 name: pod-example
7 labels:
8 app: nginx
9spec:
10 nodeName: k8s-node1
11 containers:
12 - name: nginx
13 image: nginx:1.151[root@k8s-master ~]#kubectl describe nodes |grep Taint
2Taints: node-role.kubernetes.io/master:NoSchedule
3Taints: ssd=ok:NoSchedule
4Taints: <none>
5[root@k8s-master ~]#kubectl describe nodes k8s-node1 |grep Taint
6Taints: ssd=ok:NoSchedule
7[root@k8s-master ~]#1[root@k8s-master ~]#kubectl apply -f pod6.yaml
2pod/pod6 created
3[root@k8s-master ~]#kubectl get pod -o wide
如果在pod里指定了
nodeName字段,那么即使该pod没有配置污点容忍,也会被强制开通的指定的节点上去的。
测试结束。😘
与前面所讲的**调度优选策略中的优先级(Priorities)**不同,前面所讲的优先级指的是节点优先级,而我们这里所说的优先级指的是 Pod 的优先级,高优先级的 Pod 会优先被调度,或者在资源不足的情况牺牲低优先级的 Pod,以便于重要的 Pod 能够得到资源部署。
🍂 要定义 Pod 优先级,就需要先定义 PriorityClass 对象,该对象没有 Namespace 的限制:
1apiVersion: v1
2kind: PriorityClass
3metadata:
4 name: high-priority
5value: 1000000
6globalDefault: false
7description: "This priority class should be used for XYZ service pods only."其中:
PriorityClass 将其设置为 true🍂 然后通过在 Pod 的 spec.priorityClassName 中指定已定义的 PriorityClass 名称即可:
1apiVersion: v1
2kind: Pod
3metadata:
4 name: nginx
5 labels:
6 app: nginx
7spec:
8 containers:
9 - name: nginx
10 image: nginx
11 imagePullPolicy: IfNotPresent
12 priorityClassName: high-priority另外一个值得注意的是当节点没有足够的资源供调度器调度 Pod,导致 Pod 处于 pending 时,抢占(preemption)逻辑就会被触发,抢占会尝试从一个节点删除低优先级的 Pod,从而释放资源使高优先级的 Pod 得到节点资源进行部署。
QoS 是 Quality of Service 的缩写,即服务质量,为了实现资源被有效调度和分配的同时提高资源利用率,Kubernetes 针对不同服务质量的预期,通过 QoS 来对 pod 进行服务质量管理。对于一个 pod 来说,服务质量体现在两个具体的指标:CPU 和内存。当节点上内存资源紧张时,Kubernetes 会根据预先设置的不同 QoS 类别进行相应处理。
QoS 主要分为 Guaranteed、Burstable 和 Best-Effort三类,优先级从高到低。我们先分别来介绍下这三种服务类型的定义。
如果未做过节点 nodeSelector、节点亲和性(node affinity)或 pod 亲和、反亲和性等高级调度策略设置,我们没有办法指定服务部署到指定节点上,这样就可能会造成 CPU 或内存等密集型的 pod 同时分配到相同节点上,造成资源竞争。
另一方面,如果未对资源进行限制,一些关键的服务可能会因为资源竞争因 OOM 等原因被 kill 掉,或者被限制 CPU 使用。
我们知道对于每一个资源,container 可以指定具体的资源需求(requests)和限制(limits),requests 申请范围是0到节点的最大配置,而 limits 申请范围是 requests 到无限,即 0 <= requests <= Node Allocatable, requests <= limits <= Infinity。
==🚩 可压缩资源和不可压缩资源==
1、CPU特性
CPU 是一种可压缩的资源,这意味着它可以被拉伸,以满足所有的需求。
如果进程要求太多的 CPU,其中一些将被节制。
对于 CPU,如果 pod 中服务使用的 CPU 超过设置的 limits,pod 不会被 kill 掉但会被限制,因为 CPU 是可压缩资源,如果没有设置 limits,pod 可以使用全部空闲的 CPU 资源。
主机cpu资源利用率满时,机器出现的现象–>卡顿。
问题:默认情况下,宿主机和容器都没做资源限制的话,容器理论上是可以无限制获取到主机的计算资源(如cpu和内存)。如果某个容器应用特别耗cpu,当它把主机cpu耗尽时(资源利用率超高,达到98,99%以上),问主机会出现什么现象?
答:会出现卡顿现象,执行一条命令,老半天才出来,像老年痴呆一样;
是因为cpu是按时间片来分配的,cpu的分配原则:为了尽可能地公平地给大家一个分配,但cpu没有特别硬性的限制;
2、内存特性
内存是一种不可压缩的资源,意味着它不能像 CPU 那样被拉伸。如果一个进程没有得到足够的内存来工作,这个进程就会被杀死。
对于内存,当一个 pod 使用内存超过了设置的 limits,pod 中容器的进程会被 kernel 因 OOM kill 掉,当 container 因为 OOM 被 kill 掉时,系统倾向于在其原所在的机器上重启该 container 或本机或其他重新创建一个 pod。(包括我们的磁盘也是一个不可压缩资源)。
==🚩 单位换算==
1.cpu(可压缩资源)
在 Kubernetes 中,CPU 资源的单位是被指定为核数(cores)或者 CPU 时间片(millicores)。当你在 Kubernetes Pod 的配置文件中定义 CPU 资源时,可以使用整数或小数来表示核数,也可以使用带有“m”后缀的整数来表示千分之一的核数。
1 表示1个核的 CPU。0.5 表示半个核的 CPU,也可以写作 500m,因为 1000m(milli)相等于 1 核心。例如,如果你将一个容器的 CPU 请求设置为 100m,那么意味着你请求为该容器保留 0.1 核的 CPU 时间片。如果你把容器的 CPU 限制设置为 1,那意味着该容器最多可以使用 1 个核的 CPU 时间。
这里是一个配置CPU资源请求和限制的例子:
1apiVersion: v1
2kind: Pod
3metadata:
4 name: cpu-demo
5 namespace: cpu-example
6spec:
7 containers:
8 - name: cpu-demo-ctr
9 image: vish/stress
10 resources:
11 limits:
12 cpu: "1"
13 requests:
14 cpu: "500m"在这个例子中,容器cpu-demo-ctr启动时会请求至少有半核 CPU 可用(500m),而且该容器使用的 CPU 资源会被限制在最多1个核。如果在集群中没有足够的 CPU 资源来满足请求量,Pod 会等待直到有足够的资源可用。通过这种配置方式,Kubernetes 能够更有效地管理资源,确保在多租户或资源受限的环境中合理分配 CPU 使用权。
2.内存(不可压缩资源)
在 Kubernetes 中,内存的单位是字节。
你可以用,**E,P,T,**G,M,k 来代表 Exabyte,Petabyte,Terabyte,Gigabyte,Megabyte 和 kilobyte,尽管只有最后四个是常用的。(例如,500M, 4G)
警告:不要用小写的 m 表示内存(这代表 Millibytes,低得离谱)
你可以用 Mi 来定义 Mebibytes,其余的也可以用 Ei、Pi、Ti 来定义(例如,500Mi)
一个 Mebibyte(以及它们的类似物 Kibibyte、Gibibyte…)是 20 字节的 2 次方。它的出现是为了避免与公制中的 Kilo、Mega 定义相混淆。你应该使用这个符号,因为它是字节的典型定义,而 Kilo 和 Mega 是 1000 的倍数。
==🚩 yaml案例==
Kubernetes 将 Limits 定义为一个容器使用的最大资源量,这意味着容器的消耗量永远不能超过所显示的内存量或 CPU 量。
另一方面,Requests 是指为容器保留的资源的最小保证量。
容器资源限制: • resources.limits.cpu • resources.limits.memory
容器使用的最小资源需求,作为容器调度时资源分配的依据: • resources.requests.cpu • resources.requests.memory
1apiVersion: v1
2kind: Pod
3metadata:
4 name: web
5spec:
6 containers:
7 - name: web
8 image: nginx
9 resources:
10 requests:
11 memory: "64Mi" #特别注意:内存的单位是Mi,cpu的单位是m或者整数
12 cpu: "250m"
13 limits:
14 memory: "128Mi"
15 cpu: "500m" #cpu: "0.5"
16
17#K8s会根据Request的值去查找有足够资源的Node来调度此PodKubelet 提供 QoS 服务质量管理,支持系统级别的 OOM 控制。
QoS 分类并不是通过一个配置项来直接配置的,而是通过配置 CPU/内存的 limits 与 requests 值的大小来确认服务质量等级的,我们通过使用 kubectl get pod xxx -o yaml 可以看到 pod 的配置输出中有 qosClass 一项,该配置的作用是为了给资源调度提供策略支持,调度算法根据不同的服务质量等级可以确定将 pod 调度到哪些节点上。
| 名称 | 优先级 | 匹配条件 |
|---|---|---|
| 1、Guaranteed(有保证的) | 最高 | 1.pod里所有容器只配置了内存和cpu的limit; 2.pod里所有容器配置了内存和cpu的limit、requests,且limits==requests; |
| 2、Burstable(不稳定的) | 高 | 1.pod里部分容器配置了resources,但部分容器没配置; 2.pod里部分容器只配置了cpu的resources,部分容器只配置了内存的resources; |
| 3、Best-Effort(尽最大努力) | 低 | 1.pod里所有容器都没配置 内存和cpu的limit、requests; |
==1.Guaranteed(有保证的)==
系统用完了全部内存,且没有其他类型的容器可以被 kill 时,该类型的 pods 会被 kill 掉,也就是说最后才会被考虑 kill 掉,属于该级别的 pod 有以下两种情况:
requests==limits(requests不等于0)1️⃣ pod 中的所有容器都且仅设置了 limits:
1containers:
2 name: foo
3 resources:
4 limits:
5 cpu: 10m
6 memory: 1Gi
7 name: bar
8 resources:
9 limits:
10 cpu: 100m
11 memory: 100Mi因为如果一个容器只指明 limit 而未设定 requests,则 requests 的值等于 limit 值,所以上面 pod 的 QoS 级别属于 Guaranteed。
⚠️ 另外需要注意若容器指定了 requests 而未指定 limits,则 limits 的值等于节点资源的最大值;若容器指定了 limits 而未指定 requests,则 requests 的值等于 limits。
2️⃣ 另外一个就是 pod 中的所有容器都明确设置了 requests 和 limits,且单个容器内的 requests==limits:
1containers:
2 name: foo
3 resources:
4 limits:
5 cpu: 10m
6 memory: 1Gi
7 requests:
8 cpu: 10m
9 memory: 1Gi
10 name: bar
11 resources:
12 limits:
13 cpu: 100m
14 memory: 100Mi
15 requests:
16 cpu: 100m
17 memory: 100Mi容器 foo 和 bar 内 resources 的 requests 和 limits 均相等,该 pod 的 QoS 级别属于 Guaranteed。
==2.Burstable(不稳定的)==
系统用完了全部内存,且没有 Best-Effort 类型的容器可以被 kill 时,该类型的 pods 会被 kill 掉。pod 中只要有一个容器的 requests 和 limits 的设置不相同,该 pod 的 QoS 即为 Burstable。
1️⃣ 比如容器 foo 指定了 resource,而容器 bar 未指定:
1containers:
2 name: foo
3 resources:
4 limits:
5 cpu: 10m
6 memory: 1Gi
7 requests:
8 cpu: 10m
9 memory: 1Gi
10 name: bar2️⃣ 或者容器 foo 设置了内存 limits,而容器 bar 设置了 CPU limits:
1containers:
2 name: foo
3 resources:
4 limits:
5 memory: 1Gi
6 name: bar
7 resources:
8 limits:
9 cpu: 100m上面两种情况定义的 pod 都属于 Burstable 类别的 QoS。
==3.Best-Effort(尽最大努力)==
系统用完了全部内存时,该类型 pods 会最先被 kill 掉。如果 pod 中所有容器的 resources 均未设置 requests 与 limits,那么该 pod 的 QoS 即为 Best-Effort。
1️⃣ 比如容器 foo 和容器 bar 均未设置 requests 和 limits:
1containers:
2 name: foo
3 resources:
4 name: bar
5 resources:Kubernetes 通过 CGroup 给 Pod 设置 QoS 级别,当资源不足时会优先 kill 掉优先级低的 Pod。在实际使用过程中,通过 OOM 分数值来实现,OOM 分数值范围为 0-1000,OOM 分数值根据 OOM_ADJ参数计算得出。
对于 Guaranteed 级别的 Pod,OOM_ADJ 参数设置成了-998,对于 Best-Effort 级别的 Pod,OOM_ADJ 参数设置成了1000,对于 Burstable 级别的 Pod,OOM_ADJ 参数取值从 2 到 999。(可以使用 cat /proc/$PID/oom_score 命令查看进程的 OOMScore。)
QoS Pods 被 kill 掉的场景和顺序如下所示:
所以如果资源充足,可将 QoS Pods 类型设置为 Guaranteed,用计算资源换业务性能和稳定性,减少排查问题时间和成本。如果想更好的提高资源利用率,业务服务可以设置为 Guaranteed,而其他服务根据重要程度可分别设置为 Burstable 或 Best-Effort,这就要看具体的场景了。
比如我们这里如果想要尽可能提高 Wordpress 应用的稳定性,我们可以将其设置为 Guaranteed 类型的 Pod,我们现在没有设置 resources 资源,所以现在是 Best-Effort 类型的 Pod。
==扩展:==
Kubernetes 是通过 cgroup 给 pod 设置 QoS 级别的,kubelet 中有一个 --cgroups-per-qos 参数(默认启用),启用后 kubelet 会为不同 QoS 创建对应的 level cgroups,在 Qos level cgroups 下也会为 pod 下的容器创建对应的 level cgroups,从 Qos –> pod –> container,层层限制每个 level cgroups 的资源使用量。
由于我们这里使用的是 containerd 这种容器运行时,则 cgroup 的路径与之前的 docker 不太一样:
RootCgroup/system.slice/containerd.service/kubepods-pod<uid>.slice:cri-containerd:<container-id> 下RootCgroup/system.slice/containerd.service/kubepods-burstable-pod<uid>.slice:cri-containerd:<container-id> 下RootCgroup/system.slice/containerd.service/kubepods-besteffort-pod<uid>.slice:cri-containerd:<container-id> 下这里的路径和底层容器运行时及是否使用systemd还是cgroupfs都是不一样的。


当在 Kubernetes 中使用容器时,重要的是要知道所涉及的资源是什么以及如何需要它们。有些进程比其他进程需要更多的 CPU 或内存。有些是关键的,不应该被饿死。 知道了这一点,我们应该正确配置我们的容器和 Pod,以获得两者的最佳效果。
关键是你作为运维,你要大概清楚每个服务消耗资源的情况
我们知道,tomcat一般是比较耗费资源的,起码需要1c以上;(java应用特别消耗cpu资源、内存资源)
如果你的pod里的requests配置为0.5c,而limits就配置为了0.8c,那么有可能你的容器都启动不起来;不像nginx,你给它个100m,200m,它都能起来。
现在如果要想给应用设置资源大小,就又有一个问题了,应该如何设置合适的资源大小呢?其实这就需要我们对自己的应用非常了解才行了,一般情况下我们可以先不设置资源,然后可以根据我们的应用的并发和访问量来进行压力测试,基本上可以大概计算出应用的资源使用量。
可以利用fortio工具来进行压力测试。
下面是一个示例:
我们这里使用 Apache Bench(AB Test) 或者 Fortio(Istio 测试工具) 这样的测试工具来测试,我们这里使用 Fortio 这个测试工具,比如每秒 1000 个请求和 8 个并发的连接的测试命令如下所示:
1$ fortio load -a -c 5 -qps 1000 -t 60s "http://k8s.youdianzhishi.com:32411"
216:42:13 I scli.go:88> Starting Φορτίο 1.53.1 h1:aeAWrnkXIBEdhueQLRwvaM5INulybDP+ZgOChgKz/NQ= go1.19.1 arm64 darwin
3Fortio 1.53.1 running at 1000 queries per second, 10->10 procs, for 1m0s: http://k8s.youdianzhishi.com:32411
416:42:13 I httprunner.go:99> Starting http test for http://k8s.youdianzhishi.com:32411 with 5 threads at 1000.0 qps and parallel warmup
5Starting at 1000 qps with 5 thread(s) [gomax 10] for 1m0s : 12000 calls each (total 60000)
6# ......
7Sockets used: 19 (for perfect keepalive, would be 5)
8Uniform: false, Jitter: false, Catchup allowed: true
9IP addresses distribution:
1043.137.1.207:32411: 19
11Code -1 : 14 (8.4 %)
12Code 200 : 153 (91.6 %)
13Response Header Sizes : count 167 avg 290.86228 +/- 45.56 min 0 max 298 sum 48574
14Response Body/Total Sizes : count 167 avg 50216.443 +/- 9456 min 0 max 52409 sum 8386146
15All done 167 calls (plus 5 warmup) 1837.452 ms avg, 2.7 qps
16Successfully wrote 6213 bytes of Json data to 2023-03-14-164213_k8s_youdianzhishi_com_32411_bogon.json也可以通过浏览器(执行 fortio server 命令)查看到最终测试结果:

在测试期间我们可以用如下所示的命令查看应用的资源使用情况:
1$ kubectl top pods -l app=wordpress -n kube-example
2NAME CPU(cores) MEMORY(bytes)
3mysql-0 15m 213Mi
4wordpress-55b57b745f-f28tr 88m 95Mi
5wordpress-55b57b745f-gn8jc 89m 90Mi
6wordpress-55b57b745f-r7x2w 90m 94Mi我们可以看到内存基本上都是处于 100Mi 以内,CPU 消耗也很小,但是由于 CPU 是可压缩资源,也就是说超过了限制应用也不会挂掉的,只是会变慢而已。所以我们这里可以给 Wordpress 应用添加如下所示的资源配置,如果你集群资源足够的话可以适当多分配一些资源:
1resources:
2 limits:
3 cpu: 150m
4 memory: 150Mi
5 requests:
6 cpu: 150m
7 memory: 150Mi
配置后更新应用。
==💘 实践:resources.requests测试(测试成功)-2022.5.17==
1[root@k8s-master ~]#kubectl run pod6 --image=nginx --dry-run=client -o yaml > resources.yaml
2root@k8s-master ~]#vim resources.yaml
3apiVersion: v1
4kind: Pod
5metadata:
6 labels:
7 run: pod6
8 name: pod6
9spec:
10 containers:
11 - image: nginx
12 name: pod6部署并查看:
1[root@k8s-master ~]#kubectl apply -f resources.yaml
2[root@k8s-master ~]#kubectl get pod
1[root@k8s-master ~]#cp resources.yaml resources2.yaml
2[root@k8s-master ~]#vim resources2.yaml
3apiVersion: v1
4kind: Pod
5metadata:
6 labels:
7 run: resource-2
8 name: resource-2
9spec:
10 containers:
11 - image: nginx
12 name: resource-2
13 resources:
14 #最小资源
15 requests:
16 cpu: 1500m
17 memory: 512Mi #这里的单位写错了,应该是Mi才对的。。。部署并查看,resource-2也起来了:

1[root@k8s-master ~]#cp resources2.yaml resources3.yaml
2[root@k8s-master ~]#vim resources3.yaml
3[root@k8s-master ~]#kubectl apply -f resources3.yaml
4pod/resource-3 created
5[root@k8s-master ~]#
部署并查看效果:
此时发现最后创建的那个pod 一直是pending状态:


是因为node节点的cpu不足,导致pod调度失败的;
sufficient adj.充足的,足够的 insufficient adj.不充足的,不足够的

查看宿主机的资源使用率情况:
1[root@k8s-master ~]#kubectl describe node k8s-node1可分配的:



而此时resource3.yaml里的pod需求为1900m,很明显以上2个node均无法满足其需求,因此和这个pod将无法被分配,一直处于pending状态。
实验结束。😘
==💘 实践:resources.limits测试(测试成功)-2022.5.17==
1[root@k8s-master ~]#cp resources3.yaml resources4.yaml
2[root@k8s-master ~]#vim resources4.yaml
3apiVersion: v1
4kind: Pod
5metadata:
6 labels:
7 run: resource-4
8 name: resource-4
9spec:
10 containers:
11 - image: nginx
12 name: resource-4
13 resources:
14 #最小资源
15 requests:
16 cpu: 500m
17 memory: 512Mi
18 #最大资源,一般是requests 20%左右
19 limits:
20 cpu: 600m
21 memory: 612Mi最大资源,一般是requests 20%左右
1[root@k8s-master ~]#kubectl apply -f resources4.yaml #这个是不会用影响调度的
2pod/resource-4 created
3[root@k8s-master ~]#kubectl get pod
4NAME READY STATUS RESTARTS AGE
5pod4 1/1 Running 0 33h
6pod6 1/1 Running 0 81m
7resource-2 1/1 Running 0 75m
8resource-3 0/1 Pending 0 70m
9resource-4 1/1 Running 0 6s #已成功启动pod
10web-96d5df5c8-wwc96 1/1 Running 0 41h
11[root@k8s-master ~]#1[root@k8s-master ~]#kubectl describe pod resource-4
1[root@k8s-master ~]#cp resources4.yaml resources5.yaml
2[root@k8s-master ~]#vim resources5.yaml
3apiVersion: v1
4kind: Pod
5metadata:
6 labels:
7 run: resource-5
8 name: resource-5
9spec:
10 containers:
11 - image: nginx
12 name: resource-5
13 resources:
14 #最小资源
15 requests:
16 cpu: 500m
17 memory: 512Mi
18 #最大资源,一般是requests 20%左右
19 limits:
20 cpu: 1900m #把这个改成1900m,看会不会影响它开通
21 memory: 612Mi 1[root@k8s-master ~]#kubectl apply -f resources5.yaml
2pod/resource-5 created
3[root@k8s-master ~]#kubectl get pod
4NAME READY STATUS RESTARTS AGE
5pod4 1/1 Running 0 33h
6pod6 1/1 Running 0 88m
7resource-2 1/1 Running 0 81m
8resource-3 0/1 Pending 0 77m
9resource-4 1/1 Running 0 6m45s
10resource-5 1/1 Running 0 6s #可正常
11web-96d5df5c8-wwc96 1/1 Running 0 41h
12[root@k8s-master ~]#是不会受影响的,就可以正常开通。
1[root@k8s-master ~]#vim resources5.yaml
2apiVersion: v1
3kind: Pod
4metadata:
5 labels:
6 run: resource-5
7 name: resource-5
8spec:
9 containers:
10 - image: nginx
11 name: resource-5
12 resources:
13 #最小资源
14 requests:
15 cpu: 500m
16 memory: 512Mi
17 #最大资源,一般是requests 20%左右
18 limits:
19 cpu: 600m
20 memory: 612Mi实验结束。😘
让我们来看看下面这个 deployment,我们需要为两个不同的容器在 CPU 和内存上设置 Limits 和 Requests。
1kind: Deployment
2apiVersion: extensions/v1beta1
3…
4template:
5 spec:
6 containers:
7 - name: redis
8 image: redis:5.0.3-alpine
9 resources:
10 limits:
11 memory: 600Mi
12 cpu: 1
13 requests:
14 memory: 300Mi
15 cpu: 500m
16 - name: busybox
17 image: busybox:1.28
18 resources:
19 limits:
20 memory: 200Mi
21 cpu: 300m
22 requests:
23 memory: 100Mi
24 cpu: 100m假如,我们要把该 deployment 部署到 4C16G 配置的节点上,可以得到如下信息。

Pod 的有效请求是 400 MiB 的内存和 600 millicores 的 CPU,你需要一个有足够自由可分配空间的节点来安排 pod。
Redis 容器的 CPU 份额将是 512,而 busybox 容器是 102,==Kubernetes 总是为每个核心分配 1024 个份额==,因此 redis:1024 * 0.5 cores ≅ 512 和 busybox:1024 * 0.1 核 ≅ 102
如果 Redis 容器试图分配超过 600MB 的 RAM,它将被 ==OOM== 杀死,很可能使 pod 失败。
如果 Redis 试图在每 100ms 内使用超过 100ms 的 CPU,(因为我们有 4 个核心,可用时间为每 100ms 400ms),它将遭受 CPU 节流,导致性能下降。
如果 Busybox 容器试图分配超过 200MB 的 RAM,它将被 OOM 杀死,导致一个失败的 Pod。
如果 Busybox 试图每 100ms 使用超过 30ms 的 CPU,它将遭受 CPU 节流,导致性能下降。
==💘 实践:Qos解析(测试成功)-2022.5.17==
mount | grep cgroup 命令查看 RootCgroup: 1➜ mount | grep cgroup
2tmpfs on /sys/fs/cgroup type tmpfs (ro,nosuid,nodev,noexec,mode=755)
3cgroup on /sys/fs/cgroup/systemd type cgroup (rw,nosuid,nodev,noexec,relatime,xattr,release_agent=/usr/lib/systemd/systemd-cgroups-agent,name=systemd)
4cgroup on /sys/fs/cgroup/blkio type cgroup (rw,nosuid,nodev,noexec,relatime,blkio)
5cgroup on /sys/fs/cgroup/cpuset type cgroup (rw,nosuid,nodev,noexec,relatime,cpuset)
6cgroup on /sys/fs/cgroup/cpu,cpuacct type cgroup (rw,nosuid,nodev,noexec,relatime,cpuacct,cpu)
7cgroup on /sys/fs/cgroup/memory type cgroup (rw,nosuid,nodev,noexec,relatime,memory)
8cgroup on /sys/fs/cgroup/perf_event type cgroup (rw,nosuid,nodev,noexec,relatime,perf_event)
9cgroup on /sys/fs/cgroup/devices type cgroup (rw,nosuid,nodev,noexec,relatime,devices)
10cgroup on /sys/fs/cgroup/pids type cgroup (rw,nosuid,nodev,noexec,relatime,pids)
11cgroup on /sys/fs/cgroup/hugetlb type cgroup (rw,nosuid,nodev,noexec,relatime,hugetlb)
12cgroup on /sys/fs/cgroup/net_cls,net_prio type cgroup (rw,nosuid,nodev,noexec,relatime,net_prio,net_cls)
13cgroup on /sys/fs/cgroup/freezer type cgroup (rw,nosuid,nodev,noexec,relatime,freezer)在 cgroup 的每个子系统下都会创建 QoS level cgroups, 此外在对应的 QoS level cgroups 还会为 pod 创建 Pod level cgroups。
1# 01-qos-demo.yaml
2apiVersion: v1
3kind: Pod
4metadata:
5 name: qos-demo
6spec:
7 containers:
8 - name: nginx
9 image: nginx:latest
10 resources:
11 requests:
12 cpu: 250m
13 memory: 1Gi
14 limits:
15 cpu: 500m
16 memory: 2Gi直接创建上面的资源对象即可:
1$ kubectl apply -f 01-qos-demo.yaml
2pod/qos-demo created
3$ kubectl get po qos-demo -owide
4NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
5qos-demo 1/1 Running 0 27s 10.244.1.241 node1 <none> <none>
6
7
8$ kubectl get po qos-demo -owide -oyaml|grep qosClass
9 qosClass: Burstable
10$ kubectl get po qos-demo -owide -oyaml|grep uid
11 uid: 562d098f-8346-4e8f-b23d-9d8c24b75b6a由于该 pod 的设置的资源 requests != limits,所以其属于 Burstable 类别的 pod。
kubelet 会在其所属 QoS 下创建 RootCgroup/system.slice/containerd.service/kubepods-burstable-pod<uid>.slice:cri-containerd:<container-id> 这个 cgroup level,比如我们查看内存这个子系统的 cgroup:
1# 还有一个 pause 容器的 cgroup level
2➜ ls /sys/fs/cgroup/memory/system.slice/containerd.service/kubepods-burstable-pod489a19f2_8d75_474c_988f_5854b61b839f.slice:cri-containerd:4782243ba3260125513af20689fcea31b52eae1cbabeafeb1f7a52bcdcd5b44b
3cgroup.clone_children memory.kmem.tcp.max_usage_in_bytes memory.oom_control
4cgroup.event_control memory.kmem.tcp.usage_in_bytes memory.pressure_level
5cgroup.procs memory.kmem.usage_in_bytes memory.soft_limit_in_bytes
6memory.failcnt memory.limit_in_bytes memory.stat
7memory.force_empty memory.max_usage_in_bytes memory.swappiness
8memory.kmem.failcnt memory.memsw.failcnt memory.usage_in_bytes
9memory.kmem.limit_in_bytes memory.memsw.limit_in_bytes memory.use_hierarchy
10memory.kmem.max_usage_in_bytes memory.memsw.max_usage_in_bytes notify_on_release
11memory.kmem.slabinfo memory.memsw.usage_in_bytes tasks
12memory.kmem.tcp.failcnt memory.move_charge_at_immigrate
13memory.kmem.tcp.limit_in_bytes memory.numa_stat上面创建的应用容器进程 ID 会被写入到上面的 tasks 文件中:
1➜ cat tasks
264133
364170
464171
564172
664173
7➜ ps -aux |grep nginx
8root 64133 0.0 0.0 8840 3488 ? Ss 15:56 0:00 nginx: master process nginx -g daemon off;
9101 64170 0.0 0.0 9228 1532 ? S 15:56 0:00 nginx: worker process
10101 64171 0.0 0.0 9228 1532 ? S 15:56 0:00 nginx: worker process
11101 64172 0.0 0.0 9228 1532 ? S 15:56 0:00 nginx: worker process
12101 64173 0.0 0.0 9228 1532 ? S 15:56 0:00 nginx: worker process这样我们的容器进程就会受到该 cgroup 的限制了,在 pod 的资源清单中我们设置了 memory 的 limits 值为 2Gi,kubelet 则会将该限制值写入到 memory.limit_in_bytes 中去:
1➜ cat memory.limit_in_bytes
22147483648 # 2147483648 / 1024 / 1024 / 1024 = 2 1➜ ls /sys/fs/cgroup/cpu/system.slice/containerd.service/kubepods-burstable-pod489a19f2_8d75_474c_988f_5854b61b839f.slice:cri-containerd:4782243ba3260125513af20689fcea31b52eae1cbabeafeb1f7a52bcdcd5b44b
2cgroup.clone_children cpuacct.stat cpu.cfs_period_us cpu.rt_runtime_us notify_on_release
3cgroup.event_control cpuacct.usage cpu.cfs_quota_us cpu.shares tasks
4cgroup.procs cpuacct.usage_percpu cpu.rt_period_us cpu.stat
5➜ cat tasks
664133
764170
864171
964172
1064173
11➜ cat cpu.cfs_quota_us
1250000 # 500m cpu.cfs_quota_us/100us实验结束。😘
我们要明确在调度时调度器只会根据 requests 值进行调度。
在 Kubernetes 中:
如果你想避免饥饿(确保每个重要的进程都能得到它的份额),你应该首先使用请求。
通过设置限制,你只是防止进程在特殊情况下检索额外的资源,在内存方面造成 ==OOM 杀戮==,在 CPU 方面造成 ==Throttling==(进程将需要等待 CPU 可以再次使用)。
所以,我们工作中,一定要配置requests和limits,来解决资源争抢问题;
**如前所述,除非在非常特殊的情况下,否则不应该使用 Kubernetes 限制,因为它们可能会造成更大的伤害。**在内存不足的情况下,容器有可能被杀死,在 CPU 不足的情况下,容器有可能被节流。
对于请求,当你需要确保一个进程获得一个有保障的资源份额时,可以使用它们。
如果你在一个 Pod 的所有容器中设置一个等于限制的请求值,该 Pod 将获得保证的(最高优先级)服务质量。
还需要注意的是,==资源使用量高于请求的 Pod 更有可能被驱逐,所以设置非常低的请求会造成弊大于利==。
宿主机只有2c,这里的limits可以设置为100c,但是毫无意义,只是可以这样设置。limits是最大可用资源,一般是requests的20%左右,不太超出太多,否则limits限制就没多少意义了;
limits不能小于requests;
reqeusts只是一个预留性质,不是pod配置写多少,宿主机就会占用多少资源;
==Namespace ResourceQuata==
资源配额 ResourceQuota:限制命名空间总容量。
由于命名空间的存在,我们可以将 Kubernetes 资源隔离到不同的组,也称为租户。
当多个团队、多个用户共享使用K8s集群时,会出现不均匀资源使用,默认情况下先到先得,这时可以通过ResourceQuota来对命名空间资源使用总量做限制,从而解决这个问题。
通过 ResourceQuota,你可以为整个命名空间设置一个内存或 CPU 限制,确保其中的实体不能消耗超过这个数量。
1apiVersion: v1
2kind: ResourceQuota
3metadata:
4 name: mem-cpu-demo
5spec:
6 hard:
7 requests.cpu: 2
8 requests.memory: 1Gi
9 limits.cpu: 3
10 limits.memory: 2Girequests.cpu:这个命名空间中所有请求的最大 CPU 数量。
requests.memory:这个命名空间中所有请求的最大内存量。
limits.cpu:这个命名空间中所有限制的最大 CPU 数量。
limits.memory:这个命名空间中所有限制的总和的最大内存量。
然后,将其应用于你的命名空间:
1kubectl apply -f resourcequota.yaml --namespace=mynamespace你可以用以下方法列出一个命名空间的当前 ResourceQuota:
1kubectl get resourcequota -n mynamespace🚩 注意:
注意,如果你为命名空间中的特定资源设置了 ResourceQuota,那么你就需要为该命名空间中的每个 Pod 指定相应的限制或请求。否则,Kubernetes 将返回一个 “failed quota” 的错误。
1Error from server (Forbidden): error when creating "mypod.yaml": pods "mypod" is forbidden: failed quota: mem-cpu-demo: must specify limits.cpu,limits.memory,requests.cpu,requests.memory如果你试图添加一个新的 Pod,其容器限制或请求超过了当前的 ResourceQuota,Kubernetes 将返回一个 “exceeded quota” 的错误。
1Error from server (Forbidden): error when creating "mypod.yaml": pods "mypod" is forbidden: exceeded quota: mem-cpu-demo, requested: limits.memory=2Gi,requests.memory=2Gi, used: limits.memory=1Gi,requests.memory=1Gi, limited: limits.memory=2Gi,requests.memory=1Gi使用流程:k8s管理员为每个命名空间创建一个或多个ResourceQuota对象,定义资源使用总量,K8s会跟踪命名空间资源使用情况,当超过定义的资源配额会返回拒绝。

==💘 实战:资源配额 ResourceQuota-2023.5.25(测试成功)==

1实验环境:
21、win10,vmwrokstation虚机;
32、k8s集群:3台centos7.6 1810虚机,1个master节点,2个node节点
4 k8s version:v1.20.0
5 docker://20.10.7链接:https://pan.baidu.com/s/1-NzdrpktfaUOAl6QO_hqsA?pwd=0820 提取码:0820 2023.5.25-ResourceQuota-code



为1master,2个node节点。
1[root@k8s-master1 ~]#kubectl create ns test
2namespace/test created 1[root@k8s-master1 ~]#mkdir ResourceQuota
2[root@k8s-master1 ~]#cd ResourceQuota/
3[root@k8s-master1 ResourceQuota]#vim compute-resources.yaml
4apiVersion: v1
5kind: ResourceQuota
6metadata:
7 name: compute-resources
8 namespace: test
9spec:
10 hard:
11 requests.cpu: "1"
12 requests.memory: 1Gi
13 limits.cpu: "2"
14 limits.memory: 2Gi
15
16#部署:
17[root@k8s-master1 ResourceQuota]#kubectl apply -f compute-resources.yaml
18resourcequota/compute-resources configured
19
20#查看当前配置的ResourceQuota
21[root@k8s-master1 ResourceQuota]#kubectl get quota -ntest
22NAME AGE REQUEST LIMIT
23compute-resources 2m37s requests.cpu: 0/1, requests.memory: 0/1Gi limits.cpu: 0/2, limits.memory: 0/2Gi 1[root@k8s-master1 ResourceQuota]#kubectl run web --image=nginx --dry-run=client -oyaml > pod.yaml
2[root@k8s-master1 ResourceQuota]#vim pod.yaml
3#删除没用的配置,并配置上resources
4apiVersion: v1
5kind: Pod
6metadata:
7 labels:
8 run: web
9 name: web
10 namespace: test
11spec:
12 containers:
13 - image: nginx
14 name: web
15 resources:
16 requests:
17 cpu: 0.5
18 memory: 0.5Gi
19 limits:
20 cpu: 1
21 memory: 1Gi
22
23#部署pod
24[root@k8s-master1 ResourceQuota]#kubectl apply -f pod.yaml
25Error from server (Forbidden): error when creating "pod.yaml": pods "web" is forbidden: failed quota: compute-resources: must specify limits.cpu,limits.memory
26#注意:在部署pod时会看到报错,提示"pods "web" is forbidden: failed quota: compute-resources: must specify limits.cpu,limits.memory",因为test命名空间配置了ResourceQuota,pod里只配置requests会报错;
27
28#测试:如果不配置resource,看会否会报错?
29cat pod.yaml
30apiVersion: v1
31kind: Pod
32metadata:
33 labels:
34 run: web
35 name: web
36 namespace: test
37spec:
38 containers:
39 - image: nginx
40 name: web
41 #resources:
42 # requests:
43 # cpu: 0.5
44 # memory: 0.5Gi
45[root@k8s-master1 ResourceQuota]#kubectl apply -f pod.yaml
46Error from server (Forbidden): error when creating "pod.yaml": pods "web" is forbidden: failed quota: compute-resources: must specify limits.cpu,limits.memory,requests.cpu,requests.memory
47[root@k8s-master1 ResourceQuota]#kubectl get po -ntest
48No resources found in test namespace.
49#现象:也是会报错的!!!
50
51
52#结论:只要是配置了ResourceQuota的命名空间,pod里必须要配置上limits.cpu,limits.memory,requests.cpu,requests.memory,否则`会返回拒绝`,无法成功创建资源的。
53
54#重新配置pod:补加limits配置
55[root@k8s-master1 ResourceQuota]#vim pod.yaml
56apiVersion: v1
57kind: Pod
58metadata:
59 labels:
60 run: web
61 name: web
62 namespace: test
63spec:
64 containers:
65 - image: nginx
66 name: web
67 resources:
68 requests:
69 cpu: 0.5
70 memory: 0.5Gi
71 limits:
72 cpu: 1
73 memory: 1Gi
74
75#重新部署:
76[root@k8s-master1 ResourceQuota]#kubectl apply -f pod.yaml
77pod/web created1#查看:
2[root@k8s-master1 ResourceQuota]#kubectl get po -ntest
3NAME READY STATUS RESTARTS AGE
4web 1/1 Running 0 26s
5[root@k8s-master1 ResourceQuota]#kubectl get quota -ntest
6NAME AGE REQUEST LIMIT
7compute-resources 8h requests.cpu: 500m/1, requests.memory: 512Mi/1Gi limits.cpu: 1/2, limits.memory: 1Gi/2Gi
8#可以看到,此时ResourceQuota下可以清楚地看到requests.cpu,requests.memory,limits.cpu,limits.memory的当前使用量/总使用量。测试:如果pod里cpu或者memory的requests值之和超过ResourceQuota里定义的,预计会报错。
1[root@k8s-master1 ResourceQuota]#cp pod.yaml pod1.yaml
2[root@k8s-master1 ResourceQuota]#vim pod1.yaml
3apiVersion: v1
4kind: Pod
5metadata:
6 labels:
7 run: web
8 name: web2
9 namespace: test
10spec:
11 containers:
12 - image: nginx
13 name: web
14 resources:
15 requests:
16 cpu: 0.6
17 memory: 0.5Gi
18 limits:
19 cpu: 1
20 memory: 1Gi
21
22#部署,并观察现象:
23[root@k8s-master1 ResourceQuota]#kubectl apply -f pod1.yaml
24Error from server (Forbidden): error when creating "pod1.yaml": pods "web2" is forbidden: exceeded quota: compute-resources, requested: requests.cpu=600m, used: requests.cpu=500m, limited: requests.cpu=1
25[root@k8s-master1 ResourceQuota]#kubectl get quota -ntest
26NAME AGE REQUEST LIMIT
27compute-resources 8h requests.cpu: 500m/1, requests.memory: 512Mi/1Gi limits.cpu: 1/2, limits.memory: 1Gi/2Gi结论:如果pod里cpu或者memory的requests值之和超过ResourceQuota里定义的,创建新的pod会报错。
测试:如果pod里cpu或者memory的limits值之和超过ResourceQuota里定义的,预计会报错。
1[root@k8s-master1 ResourceQuota]#cp pod.yaml pod2.yaml
2[root@k8s-master1 ResourceQuota]#vim pod2.yaml
3apiVersion: v1
4kind: Pod
5metadata:
6 labels:
7 run: web
8 name: web3
9 namespace: test
10spec:
11 containers:
12 - image: nginx
13 name: web
14 resources:
15 requests:
16 cpu: 0.5
17 memory: 0.5Gi
18 limits:
19 cpu: 1.1
20 memory: 1Gi
21
22
23#部署,并观察现象:
24[root@k8s-master1 ResourceQuota]#kubectl apply -f pod2.yaml
25Error from server (Forbidden): error when creating "pod2.yaml": pods "web3" is forbidden: exceeded quota: compute-resources, requested: limits.cpu=1100m, used: limits.cpu=1, limited: limits.cpu=2
26[root@k8s-master1 ResourceQuota]#kubectl get quota -ntest
27NAME AGE REQUEST LIMIT
28compute-resources 8h requests.cpu: 500m/1, requests.memory: 512Mi/1Gi limits.cpu: 1/2, limits.memory: 1Gi/2Gi结论:如果pod里cpu或者memory的limits值之和超过ResourceQuota里定义的,创建新的pod会报错。
因此:
如果某个命名空间下配置了ResourceQuota,pod里必须要配置上limits.cpu,limits.memory,requests.cpu,requests.memory,否则
会返回拒绝,无法成功创建资源的。另外,如果pod里cpu或者memory的requests&limits值之和超过ResourceQuota里定义的requests&limits,则
会返回拒绝,无法成功创建资源的。
1[root@k8s-master1 ResourceQuota]#cp pod.yaml pod3.yaml
2[root@k8s-master1 ResourceQuota]#vim pod3.yaml
3apiVersion: v1
4kind: Pod
5metadata:
6 labels:
7 run: web
8 name: web3
9 namespace: test
10spec:
11 containers:
12 - image: nginx
13 name: web
14 resources:
15 requests:
16 cpu: 0.5
17 memory: 0.5Gi
18 limits:
19 cpu: 1
20 memory: 1Gi
21
22#部署:
23[root@k8s-master1 ResourceQuota]#kubectl apply -f pod3.yaml
24pod/web3 created
25
26#查看:
27[root@k8s-master1 ResourceQuota]#kubectl get po -ntest
28NAME READY STATUS RESTARTS AGE
29web 1/1 Running 0 16m
30web3 1/1 Running 0 27s
31[root@k8s-master1 ResourceQuota]#kubectl get quota -ntest
32NAME AGE REQUEST LIMIT
33compute-resources 8h requests.cpu: 1/1, requests.memory: 1Gi/1Gi limits.cpu: 2/2, limits.memory: 2Gi/2Gi测试结束。😘

1[root@k8s-master1 ResourceQuota]#vim storage-resources.yaml
2apiVersion: v1
3kind: ResourceQuota
4metadata:
5 name: storage-resources
6 namespace: test
7spec:
8 hard:
9 requests.storage: "10G"
10
11#部署:
12[root@k8s-master1 ResourceQuota]#kubectl apply -f storage-resources.yaml
13resourcequota/storage-resources created
14
15#查看
16[root@k8s-master1 ResourceQuota]#kubectl get quota -ntest
17NAME AGE REQUEST LIMIT
18compute-resources 8h requests.cpu: 1/1, requests.memory: 1Gi/1Gi limits.cpu: 2/2, limits.memory: 2Gi/2Gi
19storage-resources 8s requests.storage: 0/10G 1[root@k8s-master1 ResourceQuota]#vim pvc.yaml
2apiVersion: v1
3kind: PersistentVolumeClaim
4metadata:
5 name: pvc
6 namespace: test
7spec:
8 accessModes:
9 - ReadWriteOnce
10 resources:
11 requests:
12 storage: 8Gi
13
14#部署:
15[root@k8s-master1 ResourceQuota]#kubectl apply -f pvc.yaml
16persistentvolumeclaim/pvc created
17
18#查看:
19[root@k8s-master1 ResourceQuota]#kubectl get pvc -ntest
20NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
21pvc Pending #这个pending不影响实验,这里是没有pv才导致pvc处于Pending状态
22
23#部署成功后,可以看到ResourceQuota requests.storage这里已经发生了变化
24[root@k8s-master1 ResourceQuota]#kubectl get quota -ntest
25NAME AGE REQUEST LIMIT
26compute-resources 8h requests.cpu: 1/1, requests.memory: 1Gi/1Gi limits.cpu: 2/2, limits.memory: 2Gi/2Gi
27storage-resources 117s requests.storage: 8Gi/10G 1[root@k8s-master1 ResourceQuota]#cp pvc.yaml pvc1.yaml
2[root@k8s-master1 ResourceQuota]#vim pvc1.yaml
3apiVersion: v1
4kind: PersistentVolumeClaim
5metadata:
6 name: pvc1
7 namespace: test
8spec:
9 accessModes:
10 - ReadWriteOnce
11 resources:
12 requests:
13 storage: 2.1Gi
14
15#部署:
16[root@k8s-master1 ResourceQuota]#kubectl apply -f pvc1.yaml
17Error from server (Forbidden): error when creating "pvc1.yaml": persistentvolumeclaims "pvc1" is forbidden: exceeded quota: storage-resources, requested: requests.storage=2254857831, used: requests.storage=8Gi, limited: requests.storage=10G
18[root@k8s-master1 ResourceQuota]#kubectl get quota -ntest
19NAME AGE REQUEST LIMIT
20compute-resources 8h requests.cpu: 1/1, requests.memory: 1Gi/1Gi limits.cpu: 2/2, limits.memory: 2Gi/2Gi
21storage-resources 6m29s requests.storage: 8Gi/10G
22#可以看到,此时报错了,意料之中。
23
24#我们重新部署一个pvc看看:
25[root@k8s-master1 ResourceQuota]#cp pvc.yaml pvc2.yaml
26[root@k8s-master1 ResourceQuota]#vim pvc2.yaml
27apiVersion: v1
28kind: PersistentVolumeClaim
29metadata:
30 name: pvc2
31 namespace: test
32spec:
33 accessModes:
34 - ReadWriteOnce
35 resources:
36 requests:
37 storage: 2Gi
38
39#部署:注意,存储这里不能使用满!!!
40[root@k8s-master1 ResourceQuota]#kubectl apply -f pvc2.yaml
41Error from server (Forbidden): error when creating "pvc2.yaml": persistentvolumeclaims "pvc2" is forbidden: exceeded quota: storage-resources, requested: requests.storage=2Gi, used: requests.storage=8Gi, limited: requests.storage=10G
42[root@k8s-master1 ResourceQuota]#kubectl get quota -ntest
43NAME AGE REQUEST LIMIT
44compute-resources 8h requests.cpu: 1/1, requests.memory: 1Gi/1Gi limits.cpu: 2/2, limits.memory: 2Gi/2Gi
45storage-resources 6m29s requests.storage: 8Gi/10G
46
47#我们再次创建下pvc3.yaml看下
48[root@k8s-master1 ResourceQuota]#cp pvc.yaml pvc3.yaml
49[root@k8s-master1 ResourceQuota]#vim pvc3.yaml
50apiVersion: v1
51kind: PersistentVolumeClaim
52metadata:
53 name: pvc3
54 namespace: test
55spec:
56 accessModes:
57 - ReadWriteOnce
58 resources:
59 requests:
60 storage: 1Gi
61
62#部署:
63[root@k8s-master1 ResourceQuota]#kubectl apply -f pvc3.yaml
64persistentvolumeclaim/pvc3 created
65
66#查看:符合预期,可以正常部署pvc。
67[root@k8s-master1 ResourceQuota]#kubectl get pvc -ntest
68NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
69pvc Pending 9m27s
70pvc3 Pending 9s
71[root@k8s-master1 ResourceQuota]#kubectl get quota -ntest
72NAME AGE REQUEST LIMIT
73compute-resources 8h requests.cpu: 1/1, requests.memory: 1Gi/1Gi limits.cpu: 2/2, limits.memory: 2Gi/2Gi
74storage-resources 11m requests.storage: 9Gi/10G
75[root@k8s-master1 ResourceQuota]#测试结束。😘

1[root@k8s-master1 ResourceQuota]#kubectl get po -ntest
2NAME READY STATUS RESTARTS AGE
3web 1/1 Running 0 41m
4web3 1/1 Running 0 25m 1[root@k8s-master1 ResourceQuota]#vim object-counts.yaml
2apiVersion: v1
3kind: ResourceQuota
4metadata:
5 name: object-counts
6 namespace: test
7spec:
8 hard:
9 pods: "4"
10 count/deployments.apps: "3"
11 count/services: "3"
12
13#部署:
14[root@k8s-master1 ResourceQuota]#kubectl apply -f object-counts.yaml
15resourcequota/object-counts created
16
17#查看:
18[root@k8s-master1 ResourceQuota]#kubectl get quota -ntest
19NAME AGE REQUEST LIMIT
20compute-resources 9h requests.cpu: 1/1, requests.memory: 1Gi/1Gi limits.cpu: 2/2, limits.memory: 2Gi/2Gi
21object-counts 15s count/deployments.apps: 0/3, count/services: 0/3, pods: 2/4
22storage-resources 16m requests.storage: 9Gi/10G
23[root@k8s-master1 ResourceQuota]# 1#此时已经存在2个pod了,ResourceQuota里限制的pod最大数量为4,那我们接下来创建下测试pod看下
2#但此时为了测试方方便,我删除下前面的compute-resources.yaml,不然创建pod会报错的
3[root@k8s-master1 ResourceQuota]#kubectl delete -f compute-resources.yaml
4resourcequota "compute-resources" deleted
5[root@k8s-master1 ResourceQuota]#kubectl get quota -ntest
6NAME AGE REQUEST LIMIT
7object-counts 3m23s count/deployments.apps: 0/3, count/services: 0/3, pods: 2/4
8storage-resources 19m requests.storage: 9Gi/10G
9
10#创建3个测试pod看下
11[root@k8s-master1 ResourceQuota]#kubectl get po -ntest
12NAME READY STATUS RESTARTS AGE
13web 1/1 Running 0 47m
14web3 1/1 Running 0 32m
15[root@k8s-master1 ResourceQuota]#kubectl run web4 --image=nginx -ntest
16pod/web4 created
17[root@k8s-master1 ResourceQuota]#kubectl run web5 --image=nginx -ntest
18Error from server (Forbidden): pods "web5" is forbidden: exceeded quota: object-counts, requested: pods=1, used: pods=4, limited: pods=4
19#可以看到,这里报错了。测试结束。😘
- 如果某个命名空间下配置了ResourceQuota,pod里必须要配置上limits.cpu,limits.memory,requests.cpu,requests.memory,否则
会返回拒绝,无法成功创建资源的。- 如果pod里cpu或者memory的requests&limits值之和超过ResourceQuota里定义的requests&limits,则
会返回拒绝,无法成功创建资源的。(需要注意:实际创建的request和limits pod之和是可以等于这个ResourceQuota定义的数值的,但是存储资源配额:requests.storage、对象数量配额是不能超过(必须小于)ResourceQuota定义的数值,否则会报错的。)

==Namespace LimitRange==
资源限制 LimitRange:限制容器的最大最小。(使用LimitRange定义容器默认CPU和内存请求值或者最大上限/最小上限)
如果我们想限制一个命名空间可分配的资源总量,ResourceQuotas 很有用。但如果我们想给里面的元素提供默认值,会发生什么?
LimitRanges 是一种 Kubernetes 策略,它限制了命名空间中每个实体的资源设置。
1apiVersion: v1
2kind: LimitRange
3metadata:
4 name: cpu-resource-constraint
5spec:
6 limits:
7 - default:
8 cpu: 500m
9 defaultRequest:
10 cpu: 500m
11 min:
12 cpu: 100m
13 max:
14 cpu: "1"
15 type: Containerdefault。如果没有指定,创建的容器将有这个值。
min: 创建的容器不能有比这更小的限制或请求。
max: 创建的容器不能有大于此值的限制或请求。
以后,如果你创建一个没有设置请求或限制的新 Pod,LimitRange 会自动为其所有的容器设置这些值。
1 Limits:
2 cpu: 500m
3 Requests:
4 cpu: 100m现在,想象一下,你添加一个新的 Pod,以 1200M 为限。你会收到以下错误。
1Error from server (Forbidden): error when creating "pods/mypod.yaml": pods "mypod" is forbidden: maximum cpu usage per Container is 1, but limit is 1200m请注意,默认情况下,Pod 中的所有容器将有效地拥有 100m CPU 的请求,即使没有设置 LimitRanges。
默认情况下,K8s集群上的容器对计算资源没有任何限制,可能会导致个别容器资源过大导致影响其他容器正常工作,这时可以使用LimitRange定义容器默认CPU和内存请求值或者最大上限。 LimitRange限制维度: • 限制容器配置requests.cpu/memory,limits.cpu/memory的最小、最大值 • 限制容器配置requests.cpu/memory,limits.cpu/memory的默认值 • 限制PVC配置requests.storage的最小、最大值
==💘 实战:资源限制 LimitRange-2023.5.25(测试成功)==

1实验环境:
21、win10,vmwrokstation虚机;
32、k8s集群:3台centos7.6 1810虚机,1个master节点,2个node节点
4 k8s version:v1.20.0
5 docker://20.10.7链接:https://pan.baidu.com/s/1dTuFjqToJaCiHHvtYH9xiw?pwd=0820 提取码:0820
2023.5.25-LimitRange-cdoe

1[root@k8s-master1 ~]#cd ResourceQuota/
2[root@k8s-master1 ResourceQuota]#ls
3compute-resources.yaml object-counts.yaml pod1.yaml pod2.yaml pod3.yaml pod.yaml pvc1.yaml pvc2.yaml pvc3.yaml pvc.yaml storage-resources.yaml
4[root@k8s-master1 ResourceQuota]#kubectl delete -f .
5resourcequota "object-counts" deleted
6pod "web" deleted
7pod "web3" deleted
8pod "web3" deleted
9persistentvolumeclaim "pvc" deleted
10persistentvolumeclaim "pvc3" deleted
11resourcequota "storage-resources" deleted
12Error from server (NotFound): error when deleting "compute-resources.yaml": resourcequotas "compute-resources" not found
13Error from server (NotFound): error when deleting "pod1.yaml": pods "web2" not found
14Error from server (NotFound): error when deleting "pvc1.yaml": persistentvolumeclaims "pvc1" not found
15Error from server (NotFound): error when deleting "pvc2.yaml": persistentvolumeclaims "pvc2" not found
16[root@k8s-master1 ResourceQuota]#
如果命名空间里配置了LimitRange,那么后续创建的pod里容器的request值不能小于LimitRange里定义的min(request的最小值),limits值不能大于LimitRange里定义的max(limits的最大值),否则会报错的。
接下来,我们验证下上面的结论。
1[root@k8s-master1 ~]#mkdir LimitRange
2[root@k8s-master1 ~]#cd LimitRange/
3[root@k8s-master1 LimitRange]#vim cpu-memory-min-max.yaml
4apiVersion: v1
5kind: LimitRange
6metadata:
7 name: cpu-memory-min-max
8 namespace: test
9spec:
10 limits:
11 - max: # 容器能设置limit的最大值
12 cpu: 1
13 memory: 1Gi
14 min: # 容器能设置request的最小值
15 cpu: 200m
16 memory: 200Mi
17 type: Container
18
19#部署
20[root@k8s-master1 LimitRange]#kubectl apply -f cpu-memory-min-max.yaml
21limitrange/cpu-memory-min-max created
22
23#查看
24[root@k8s-master1 LimitRange]#kubectl get limits -n test
25NAME CREATED AT
26cpu-memory-min-max 2023-05-24T23:26:13Z
27[root@k8s-master1 LimitRange]#kubectl describe limits -ntest
28Name: cpu-memory-min-max
29Namespace: test
30Type Resource Min Max Default Request Default Limit Max Limit/Request Ratio
31---- -------- --- --- --------------- ------------- -----------------------
32Container memory 200Mi 1Gi 1Gi 1Gi -
33Container cpu 200m 1 1 1 -
34#注意:这里是有request和limit默认值的创建一个小于request最小值:
1[root@k8s-master1 LimitRange]#vim pod.yaml
2apiVersion: v1
3kind: Pod
4metadata:
5 labels:
6 run: web
7 name: web
8 namespace: test
9spec:
10 containers:
11 - image: nginx
12 name: web
13 resources:
14 requests:
15 cpu: 100m
16 memory: 200Mi
17 limits:
18 cpu: 1
19 memory: 1Gi
20
21#部署:(符合预期效果)
22[root@k8s-master1 LimitRange]#kubectl apply -f pod.yaml
23Error from server (Forbidden): error when creating "pod.yaml": pods "web" is forbidden: minimum cpu usage per Container is 200m, but request is 100m
24[root@k8s-master1 LimitRange]#kubectl describe limits -ntest
25Name: cpu-memory-min-max
26Namespace: test
27Type Resource Min Max Default Request Default Limit Max Limit/Request Ratio
28---- -------- --- --- --------------- ------------- -----------------------
29Container cpu 200m 1 1 1 -
30Container memory 200Mi 1Gi 1Gi 1Gi -创建一个大于limit最大值:
1[root@k8s-master1 LimitRange]#cp pod.yaml pod1.yaml
2[root@k8s-master1 LimitRange]#vim pod1.yaml
3apiVersion: v1
4kind: Pod
5metadata:
6 labels:
7 run: web
8 name: web
9 namespace: test
10spec:
11 containers:
12 - image: nginx
13 name: web
14 resources:
15 requests:
16 cpu: 200m
17 memory: 200Mi
18 limits:
19 cpu: 1.1
20 memory: 1Gi
21
22#部署:(符合预期效果)
23[root@k8s-master1 LimitRange]#kubectl apply -f pod1.yaml
24Error from server (Forbidden): error when creating "pod1.yaml": pods "web" is forbidden: maximum cpu usage per Container is 1, but limit is 1100m
25[root@k8s-master1 LimitRange]#kubectl describe limits -ntest
26Name: cpu-memory-min-max
27Namespace: test
28Type Resource Min Max Default Request Default Limit Max Limit/Request Ratio
29---- -------- --- --- --------------- ------------- -----------------------
30Container memory 200Mi 1Gi 1Gi 1Gi -
31Container cpu 200m 1 1 1 -创建一个合适的pod:
1[root@k8s-master1 LimitRange]#cp pod.yaml pod2.yaml
2[root@k8s-master1 LimitRange]#vim pod2.yaml
3apiVersion: v1
4kind: Pod
5metadata:
6 labels:
7 run: web
8 name: web
9 namespace: test
10spec:
11 containers:
12 - image: nginx
13 name: web
14 resources:
15 requests:
16 cpu: 250m
17 memory: 200Mi
18 limits:
19 cpu: 0.9
20 memory: 1Gi
21
22#部署:
23[root@k8s-master1 LimitRange]#kubectl apply -f pod2.yaml
24pod/web created
25
26#查看:
27[root@k8s-master1 LimitRange]#kubectl describe limits -ntest
28Name: cpu-memory-min-max
29Namespace: test
30Type Resource Min Max Default Request Default Limit Max Limit/Request Ratio
31---- -------- --- --- --------------- ------------- -----------------------
32Container cpu 200m 1 1 1 -
33Container memory 200Mi 1Gi 1Gi 1Gi -
34[root@k8s-master1 LimitRange]#kubectl get po -ntest
35NAME READY STATUS RESTARTS AGE
36web 1/1 Running 0 20s
37web4 1/1 Running 0 52m测试结束。😘

只要给某个命名空间配置了LimitRange,如果你的pod里不配置request、limit,也是可以创建成功pod的,则k8s会默认分配一个request、limit值的。
1root@k8s-master1 LimitRange]#kubectl get limits
2No resources found in default namespace.
3[root@k8s-master1 LimitRange]#kubectl get po
4NAME READY STATUS RESTARTS AGE
5busybox 1/1 Running 6 3d10h
6busybox2 1/1 Running 6 3d10h
7py-k8s 1/1 Running 1 24h
8[root@k8s-master1 LimitRange]#kubectl describe po busybox
9Name: busybox
10Namespace: default
11Priority: 0
12Node: k8s-node2/172.29.9.33
13Start Time: Sun, 21 May 2023 20:44:21 +0800
14Labels: run=busybox
15Annotations: cni.projectcalico.org/podIP: 10.244.169.162/32
16……
17
18#可以看到,default命名空间下的pod是没有默认request、limit值的,因此其pod可以用尽宿主机的资源 1[root@k8s-master1 LimitRange]#kubectl get limits -ntest
2NAME CREATED AT
3cpu-memory-min-max 2023-05-24T23:26:13Z
4[root@k8s-master1 LimitRange]#kubectl describe limits -ntest
5Name: cpu-memory-min-max
6Namespace: test
7Type Resource Min Max Default Request Default Limit Max Limit/Request Ratio
8---- -------- --- --- --------------- ------------- -----------------------
9Container cpu 200m 1 1 1 -
10Container memory 200Mi 1Gi 1Gi 1Gi -
11[root@k8s-master1 LimitRange]#kubectl run web520 --image=nginx -ntest
12pod/web520 created
13[root@k8s-master1 LimitRange]#kubectl describe pod web520 -ntest
14Name: web520
15Namespace: test
16Priority: 0
17Node: k8s-node2/172.29.9.33
18Start Time: Thu, 25 May 2023 07:44:16 +0800
19Labels: run=web520
20Annotations: cni.projectcalico.org/podIP: 10.244.169.167/32
21 cni.projectcalico.org/podIPs: 10.244.169.167/32
22 kubernetes.io/limit-ranger: LimitRanger plugin set: cpu, memory request for container web520; cpu, memory limit for container web520
23Status: Running
24IP: 10.244.169.167
25IPs:
26 IP: 10.244.169.167
27Containers:
28 web520:
29 Container ID: docker://6cf523d8b462fdfcb44348e4af5247f8c35e5cb0cf4c7e5b0dadaeea76aa8bec
30 Image: nginx
31 Image ID: docker-pullable://nginx@sha256:af296b188c7b7df99ba960ca614439c99cb7cf252ed7bbc23e90cfda59092305
32 Port: <none>
33 Host Port: <none>
34 State: Running
35 Started: Thu, 25 May 2023 07:44:26 +0800
36 Ready: True
37 Restart Count: 0
38 Limits:
39 cpu: 1
40 memory: 1Gi
41 Requests:
42 cpu: 1
43 memory: 1Gi
44 Environment: <none>
45
46#可以看到,是有默认值的 1[root@k8s-master1 LimitRange]#vim default-cpu-memory-min-max.yaml
2apiVersion: v1
3kind: LimitRange
4metadata:
5 name: default-cpu-memory-min-max
6 namespace: test
7spec:
8 limits:
9 - default:
10 cpu: 500m
11 memory: 500Mi
12 defaultRequest:
13 cpu: 300m
14 memory: 300Mi
15 type: Container
16
17#部署:
18[root@k8s-master1 LimitRange]#kubectl apply -f default-cpu-memory-min-max.yaml
19limitrange/default-cpu-memory-min-max created
20
21[root@k8s-master1 LimitRange]#kubectl describe limits -ntest
22Name: cpu-memory-min-max
23Namespace: test
24Type Resource Min Max Default Request Default Limit Max Limit/Request Ratio
25---- -------- --- --- --------------- ------------- -----------------------
26Container cpu 200m 1 1 1 -
27Container memory 200Mi 1Gi 1Gi 1Gi -
28
29
30Name: default-cpu-memory-min-max
31Namespace: test
32Type Resource Min Max Default Request Default Limit Max Limit/Request Ratio
33---- -------- --- --- --------------- ------------- -----------------------
34Container cpu - - 300m 500m -
35Container memory - - 300Mi 500Mi -
36#可以看到,此时的默认值改变了
37
38#我们再创建一个pod看下现象
39[root@k8s-master1 LimitRange]#kubectl run web1314 --image=nginx -ntest
40pod/web1314 created
41[root@k8s-master1 LimitRange]#kubectl describe pod web1314 -ntest
42Name: web1314
43Namespace: test
44Priority: 0
45Node: k8s-node1/172.29.9.32
46Start Time: Thu, 25 May 2023 07:51:28 +0800
47Labels: run=web1314
48Annotations: cni.projectcalico.org/podIP: 10.244.36.101/32
49 cni.projectcalico.org/podIPs: 10.244.36.101/32
50 kubernetes.io/limit-ranger: LimitRanger plugin set: cpu, memory request for container web1314; cpu, memory limit for container web1314
51Status: Running
52IP: 10.244.36.101
53IPs:
54 IP: 10.244.36.101
55Containers:
56 web1314:
57 Container ID: docker://9cd7d7d0ebf7adb9e71b33749c26dd9e9c0149c3c1e427b50dd733bd3989d75b
58 Image: nginx
59 Image ID: docker-pullable://nginx@sha256:af296b188c7b7df99ba960ca614439c99cb7cf252ed7bbc23e90cfda59092305
60 Port: <none>
61 Host Port: <none>
62 State: Running
63 Started: Thu, 25 May 2023 07:51:30 +0800
64 Ready: True
65 Restart Count: 0
66 Limits:
67 cpu: 1
68 memory: 1Gi
69 Requests:
70 cpu: 1
71 memory: 1Gi
72 Environment: <none>
73
74#注意:测试出现了问题
75#在test命名空间有2个LimitRange资源,其default配置有冲突,但根据测试现象看,默认匹配第一条规则。
76#此时,我们直接在第一条规则上直接更改默认值,我们看下现象:
77[root@k8s-master1 LimitRange]#vim cpu-memory-min-max.yaml
78apiVersion: v1
79kind: LimitRange
80metadata:
81 name: cpu-memory-min-max
82 namespace: test
83spec:
84 limits:
85 - max:
86 cpu: 1
87 memory: 1Gi
88 min:
89 cpu: 200m
90 memory: 200Mi
91 default:
92 cpu: 600m
93 memory: 600Mi
94 defaultRequest:
95 cpu: 400m
96 memory: 400Mi
97 type: Container
98
99#部署并查看:
100[root@k8s-master1 LimitRange]#kubectl apply -f cpu-memory-min-max.yaml
101limitrange/cpu-memory-min-max configured
102root@k8s-master1 LimitRange]#kubectl describe limits -ntest
103Name: cpu-memory-min-max
104Namespace: test
105Type Resource Min Max Default Request Default Limit Max Limit/Request Ratio
106---- -------- --- --- --------------- ------------- -----------------------
107Container cpu 200m 1 400m 600m -
108Container memory 200Mi 1Gi 400Mi 600Mi -
109
110
111Name: default-cpu-memory-min-max
112Namespace: test
113Type Resource Min Max Default Request Default Limit Max Limit/Request Ratio
114---- -------- --- --- --------------- ------------- -----------------------
115Container cpu - - 300m 500m -
116Container memory - - 300Mi 500Mi -
117
118#再次部署一个新pod
119[root@k8s-master1 LimitRange]#kubectl run web1315 --image=nginx -ntest
120pod/web1315 created
121[root@k8s-master1 LimitRange]#kubectl describe pod web1315 -ntest
122Name: web1315
123Namespace: test
124Priority: 0
125Node: k8s-node2/172.29.9.33
126Start Time: Thu, 25 May 2023 07:57:39 +0800
127Labels: run=web1315
128Annotations: cni.projectcalico.org/podIP: 10.244.169.168/32
129 cni.projectcalico.org/podIPs: 10.244.169.168/32
130 kubernetes.io/limit-ranger: LimitRanger plugin set: cpu, memory request for container web1315; cpu, memory limit for container web1315
131Status: Running
132IP: 10.244.169.168
133IPs:
134 IP: 10.244.169.168
135Containers:
136 web1315:
137 Container ID: docker://a9e19c184eaa9513078b46d8d2d8ce5dd0a09dca00772052345d59040c57346e
138 Image: nginx
139 Image ID: docker-pullable://nginx@sha256:af296b188c7b7df99ba960ca614439c99cb7cf252ed7bbc23e90cfda59092305
140 Port: <none>
141 Host Port: <none>
142 State: Running
143 Started: Thu, 25 May 2023 07:57:42 +0800
144 Ready: True
145 Restart Count: 0
146 Limits:
147 cpu: 600m
148 memory: 600Mi
149 Requests:
150 cpu: 400m
151 memory: 400Mi
152 Environment: <none>
153 Mounts:
154#此时,新建pod里的默认值被改变过来了,符合预期效果。 测试结束。😘

1[root@k8s-master1 LimitRange]#vim storage-min-max.yaml
2apiVersion: v1
3kind: LimitRange
4metadata:
5 name: storage-min-max
6 namespace: test
7spec:
8 limits:
9 - type: PersistentVolumeClaim
10 max:
11 storage: 10Gi
12 min:
13 storage: 1Gi
14
15#部署:
16[root@k8s-master1 LimitRange]#kubectl apply -f storage-min-max.yaml
17limitrange/storage-min-max created
18
19#创建一个pvc
20[root@k8s-master1 LimitRange]#vim pvc.yaml
21apiVersion: v1
22kind: PersistentVolumeClaim
23metadata:
24 name: pvc-storage-test
25 namespace: test
26spec:
27 accessModes:
28 - ReadWriteOnce
29 resources:
30 requests:
31 storage: 11Gi
32
33#部署pvc:
34[root@k8s-master1 LimitRange]#kubectl apply -f pvc.yaml
35Error from server (Forbidden): error when creating "pvc.yaml": persistentvolumeclaims "pvc-storage-test" is forbidden: maximum storage usage per PersistentVolumeClaim is 10Gi, but request is 11Gi
36[root@k8s-master1 LimitRange]#kubectl describe limits -ntest
37Name: cpu-memory-min-max
38Namespace: test
39Type Resource Min Max Default Request Default Limit Max Limit/Request Ratio
40---- -------- --- --- --------------- ------------- -----------------------
41Container cpu 200m 1 400m 600m -
42Container memory 200Mi 1Gi 400Mi 600Mi -
43
44
45Name: default-cpu-memory-min-max
46Namespace: test
47Type Resource Min Max Default Request Default Limit Max Limit/Request Ratio
48---- -------- --- --- --------------- ------------- -----------------------
49Container cpu - - 300m 500m -
50Container memory - - 300Mi 500Mi -
51
52
53Name: storage-min-max
54Namespace: test
55Type Resource Min Max Default Request Default Limit Max Limit/Request Ratio
56---- -------- --- --- --------------- ------------- -----------------------
57PersistentVolumeClaim storage 1Gi 10Gi - - -
58#可以看到,自己申请的存储为11Gi,但是LimitRange里max可申请存储为10Gi,因此报错。
59
60#重新创建一个pvc
61[root@k8s-master1 LimitRange]#cp pvc.yaml pvc1.yaml
62[root@k8s-master1 LimitRange]#vim pvc1.yaml
63apiVersion: v1
64kind: PersistentVolumeClaim
65metadata:
66 name: pvc-storage-test
67 namespace: test
68spec:
69 accessModes:
70 - ReadWriteOnce
71 resources:
72 requests:
73 storage: 8Gi
74
75#部署:
76[root@k8s-master1 LimitRange]#kubectl apply -f pvc1.yaml
77persistentvolumeclaim/pvc-storage-test created
78[root@k8s-master1 LimitRange]#kubectl get pvc -ntest
79NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
80pvc-storage-test Pending 30s
81[root@k8s-master1 LimitRange]#kubectl describe limits -ntest
82Name: cpu-memory-min-max
83Namespace: test
84Type Resource Min Max Default Request Default Limit Max Limit/Request Ratio
85---- -------- --- --- --------------- ------------- -----------------------
86Container memory 200Mi 1Gi 400Mi 600Mi -
87Container cpu 200m 1 400m 600m -
88
89
90Name: default-cpu-memory-min-max
91Namespace: test
92Type Resource Min Max Default Request Default Limit Max Limit/Request Ratio
93---- -------- --- --- --------------- ------------- -----------------------
94Container memory - - 300Mi 500Mi -
95Container cpu - - 300m 500m -
96
97
98Name: storage-min-max
99Namespace: test
100Type Resource Min Max Default Request Default Limit Max Limit/Request Ratio
101---- -------- --- --- --------------- ------------- -----------------------
102PersistentVolumeClaim storage 1Gi 10Gi - - -
103#部署成功,符合预期。测试结束。😘

这个理解起来稍微有些复杂; 这个可能用的不多,但如果你想进行一些细粒度的控制的话,用这个还是很不错的;
在 k8s 集群调度中,亲和性相关的概念本质上都是控制 Pod 如何被调度 – 堆叠或打散。
podAffinity 以及 podAntiAffinity 两个特性对 Pod 在不同拓扑域的分布进行了一些控制。
podAffinity 可以将无数个 Pod 调度到特定的某一个拓扑域,这是堆叠的体现;
podAntiAffinity 则可以控制一个拓扑域只存在一个 Pod,这是打散的体现;
但这两种情况都太极端了,在不少场景下都无法达到理想的效果,例如为了实现容灾和高可用,将业务 Pod 尽可能均匀的分布在不同可用区就很难实现。
PodTopologySpread(Pod 拓扑分布约束) 特性的提出正是为了对 Pod 的调度分布提供更精细的控制,以提高服务可用性以及资源利用率。PodTopologySpread 由 EvenPodsSpread 特性门所控制,在 v1.16 版本第一次发布,并在 v1.18 版本进入 beta 阶段默认启用。
在 Pod 的 Spec 规范中新增了一个 topologySpreadConstraints 字段即可配置拓扑分布约束,如下所示:
1spec:
2 topologySpreadConstraints:
3 - maxSkew: <integer> #这个属性理解起来不是那么地直接。。。;倾斜度;
4 topologyKey: <string>
5 whenUnsatisfiable: <string>
6 labelSelector: <object>由于这个新增的字段是在 Pod spec 层面添加,因此更高层级的控制 (Deployment、DaemonSet、StatefulSet) 也能使用 PodTopologySpread 功能。

让我们结合上图来理解 topologySpreadConstraints 中各个字段的含义和作用:
labelSelector: 用来查找匹配的 Pod,我们能够计算出每个拓扑域中匹配该 label selector 的 Pod 数量,在上图中,假如 label selector 是 app:foo,那么 zone1 的匹配个数为 2, zone2 的匹配个数为 0。topologyKey: 是 Node label 的 key,如果两个 Node 的 label 同时具有该 key 并且值相同,就说它们在同一个拓扑域。在上图中,指定 topologyKey 为 zone, 则具有 zone=zone1 标签的 Node 被分在一个拓扑域,具有 zone=zone2 标签的 Node 被分在另一个拓扑域。maxSkew: 这个属性理解起来不是很直接,它描述了 Pod在不同拓扑域中不均匀分布的最大程度(指定拓扑类型中任意两个拓扑域中匹配的 Pod 之间的最大允许差值),它必须大于零。每个拓扑域都有一个 skew 值,计算的公式是:skew[i] = 拓扑域[i]中匹配的 Pod 个数 - min{其他拓扑域中匹配的 Pod 个数}。在上图中,我们新建一个带有app=foo标签的 Pod:maxSkew: 1 的约束(差值为1)whenUnsatisfiable: 描述了如果 Pod 不满足分布约束条件该采取何种策略:假设你拥有一个 4 节点集群,其中标记为 foo:bar 的 3 个 Pod 分别位于 node1、node2 和 node3 中:

如果希望新来的 Pod 均匀分布在现有的可用区域,则可以按如下设置其约束:
1kind: Pod
2apiVersion: v1
3metadata:
4 name: mypod
5 labels:
6 foo: bar
7spec:
8 topologySpreadConstraints:
9 - maxSkew: 1
10 topologyKey: zone
11 whenUnsatisfiable: DoNotSchedule
12 labelSelector:
13 matchLabels:
14 foo: bar
15 containers:
16 - name: pause
17 image: k8s.gcr.io/pause:3.1topologyKey: zone 意味着均匀分布将只应用于存在标签键值对为 zone:<any value> 的节点。 whenUnsatisfiable: DoNotSchedule 告诉调度器如果新的 Pod 不满足约束,则不可调度。如果调度器将新的 Pod 放入 “zoneA”,Pods 分布将变为 [3, 1],因此实际的偏差为 2(3 - 1),这违反了 maxSkew: 1 的约定。此示例中,新 Pod 只能放置在 “zoneB” 上:

或者

你可以调整 Pod 约束以满足各种要求:
maxSkew 更改为更大的值,比如 “2”,这样新的 Pod 也可以放在 “zoneA” 上。topologyKey 更改为 “node”,以便将 Pod 均匀分布在节点上而不是区域中。 在上面的例子中,如果 maxSkew 保持为 “1”,那么传入的 Pod 只能放在 “node4” 上。whenUnsatisfiable: DoNotSchedule 更改为 whenUnsatisfiable: ScheduleAnyway, 以确保新的 Pod 可以被调度。上面是单个 Pod 拓扑分布约束的情况,下面的例子建立在前面例子的基础上来对多个 Pod 拓扑分布约束进行说明。假设你拥有一个 4 节点集群,其中 3 个标记为 foo:bar 的 Pod 分别位于 node1、node2 和 node3 上:

我们可以使用 2 个 TopologySpreadConstraint 来控制 Pod 在区域和节点两个维度上的分布:
1# two-constraints.yaml
2kind: Pod
3apiVersion: v1
4metadata:
5 name: mypod
6 labels:
7 foo: bar
8spec:
9 topologySpreadConstraints:
10 - maxSkew: 1
11 topologyKey: zone
12 whenUnsatisfiable: DoNotSchedule
13 labelSelector:
14 matchLabels:
15 foo: bar
16 - maxSkew: 1
17 topologyKey: node
18 whenUnsatisfiable: DoNotSchedule
19 labelSelector:
20 matchLabels:
21 foo: bar
22 containers:
23 - name: pause
24 image: k8s.gcr.io/pause:3.1在这种情况下,为了匹配第一个约束,新的 Pod 只能放置在 “zoneB” 中;而在第二个约束中, 新的 Pod 只能放置在 “node4” 上,最后两个约束的结果加在一起,唯一可行的选择是放置 在 “node4” 上。
🍂 多个约束之间是可能存在冲突的,假设有一个跨越 2 个区域的 3 节点集群:

如果对集群应用 two-constraints.yaml,会发现 “mypod” 处于 Pending 状态,这是因为为了满足第一个约束,“mypod” 只能放在 “zoneB” 中,而第二个约束要求 “mypod” 只能放在 “node2” 上,Pod 调度无法满足这两种约束,所以就冲突了。
为了克服这种情况,你可以增加 maxSkew 或修改其中一个约束,让其使用 whenUnsatisfiable: ScheduleAnyway。
仔细观察可能你会发现我们并没有类似于 **topologyValues** 的字段来限制 Pod 将被调度到哪些拓扑去,默认情况会搜索所有节点并按 topologyKey 对其进行分组。有时这可能不是理想的情况,比如假设有一个集群,其节点标记为 env=prod、env=staging和 env=qa,现在你想跨区域将 Pod 均匀地放置到 qa 环境中,是否可行?
答案是肯定的,我们可以结合 NodeSelector 或 NodeAffinity 一起使用,PodTopologySpread 会计算满足选择器的节点之间的传播约束。

如上图所示我们可以通过指定 spec.affinity.nodeAffinity 将搜索范围限制为 qa 环境,在该范围内 Pod 将被调度到一个满足 topologySpreadConstraints 的区域,这里就只能被调度到 zone=zone2 的节点上去了。
除了为单个 Pod 设置拓扑分布约束,也可以为集群设置默认的拓扑分布约束,默认拓扑分布约束在且仅在以下条件满足 时才会应用到 Pod 上:
.spec.topologySpreadConstraints 设置任何约束;你可以在 调度方案(Schedulingg Profile)中将默认约束作为 PodTopologySpread 插件参数的一部分来进行设置。 约束的设置采用和前面 Pod 中的规范一致,只是 labelSelector 必须为空。配置的示例可能看起来像下面这个样子:
1apiVersion: kubescheduler.config.k8s.io/v1beta1
2kind: KubeSchedulerConfiguration
3
4profiles:
5 - pluginConfig:
6 - name: PodTopologySpread
7 args:
8 defaultConstraints:
9 - maxSkew: 1
10 topologyKey: topology.kubernetes.io/zone
11 whenUnsatisfiable: ScheduleAnyway
12 defaultingType: List我们知道 DaemonSet 控制器的功能就是在每个节点上运行一个 Pod,如何要使用 Deployment 来实现,首先就要设置副本数量为节点数,比如我们这里加上 master 节点一共3个节点,则要设置3个副本,要在 master 节点上执行自然要添加容忍,那么要如何保证一个节点上只运行一个 Pod 呢?是不是前面的提到的 Pod 反亲和性就可以实现,以自己 Pod 的标签来进行过滤校验即可,新的 Pod 不能运行在一个已经具有该 Pod 的节点上,是不是就是一个节点只能运行一个?
1# 06-daemonset-deployment.yaml
2apiVersion: apps/v1
3kind: Deployment
4metadata:
5 name: mock-ds-demo
6spec:
7 replicas: 3
8 selector:
9 matchLabels:
10 app: mock-ds-demo
11 template:
12 metadata:
13 labels:
14 app: mock-ds-demo
15 spec:
16 tolerations: #加上容忍,这样才有机会运行在master节点上
17 - key: "node-role.kubernetes.io/master"
18 operator: "Exists"
19 effect: "NoSchedule"
20 containers:
21 - image: nginx
22 name: nginx
23 ports:
24 - containerPort: 80
25 name: ngpt
26 affinity:
27 podAntiAffinity: # pod反亲合性
28 requiredDuringSchedulingIgnoredDuringExecution: # 硬策略
29 - labelSelector:
30 matchExpressions:
31 - key: app # Pod的标签
32 operator: In
33 values: ["mock-ds-demo"]
34 topologyKey: kubernetes.io/hostname # 以hostname为拓扑域,相当于每一个节点都是一个拓扑域1$ kubectl apply -f 06-daemonset-deployment.yaml
2deployment.apps/mock-ds-demo created
3$ kubectl get po -owide
4NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
5mock-ds-demo-8694759c69-882fx 1/1 Running 0 18s 10.244.0.15 master1 <none> <none>
6mock-ds-demo-8694759c69-frfhx 1/1 Running 0 18s 10.244.1.108 node1 <none> <none>
7mock-ds-demo-8694759c69-p5cjw 1/1 Running 0 18s 10.244.2.235 node2 <none> <none>可以看到我们用 Deployment 部署的服务在每个节点上都运行了一个 Pod,实现的效果和 DaemonSet 是一致的。
🍂 同样的如果想在每个节点(或指定的一些节点)上运行2个(或多个)Pod 副本,如何实现?
DaemonSet 是在每个节点上运行1个 Pod 副本,显然我们去创建2个(或多个)DaemonSet 即可实现该目标,但是这不是一个好的接近方案,而 **PodAntiAffinity** 只能将一个 Pod 调度到某个拓扑域中去,所以都不能很好的来解决这个问题。
要实现这种更细粒度的控制,我们可以通过设置拓扑分布约束来进行调度,设置拓扑分布约束来将 Pod 分布到不同的拓扑域下,从而实现高可用性或节省成本,具体实现方式请看下文。
| 💘 实践:用拓扑分布约束来扩展daemonset功能(测试成功)-2022.5.17 |
|---|
现在我们再去解决上节课留下的一个问题 - 如果想在每个节点(或指定的一些节点)上运行2个(或多个)Pod 副本,如何实现?
1#01-daemonset2.yaml
2apiVersion: apps/v1
3kind: Deployment
4metadata:
5 name: topo-demo
6spec:
7 replicas: 6
8 selector:
9 matchLabels:
10 app: topo
11 template:
12 metadata:
13 labels:
14 app: topo
15 spec:
16 tolerations:
17 - key: "node-role.kubernetes.io/master"
18 operator: "Exists"
19 effect: "NoSchedule"
20 containers:
21 - image: nginx
22 name: nginx
23 ports:
24 - containerPort: 80
25 name: ngpt
26 topologySpreadConstraints:
27 - maxSkew: 1
28 topologyKey: kubernetes.io/hostname
29 whenUnsatisfiable: DoNotSchedule
30 labelSelector:
31 matchLabels:
32 app: topo这里我们重点需要关注的就是 topologySpreadConstraints 部分的配置,我们选择使用 kubernetes.io/hostname 为拓扑域,相当于就是3个节点都是独立的,maxSkew: 1 表示最大的分布不均匀度为1,所以只能出现的调度结果就是每个节点运行2个 Pod。
maxSkew=2这个相当于是软策略,不可取。

maxSkew=1这个满足需求。
maxSkew=1,这个是一轮轮往下调度的;(那么一般情况,maxSkew=1应该是比较常用的)

1$ kubectl apply -f 01-daemonset2.yaml
2deployment.apps/topo-demo created
3$ kubectl get po -l app=topo -owide
4NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
5topo-demo-6bbf65d967-ctf66 1/1 Running 0 43s 10.244.1.110 node1 <none> <none>
6topo-demo-6bbf65d967-gkqpx 1/1 Running 0 43s 10.244.0.16 master1 <none> <none>
7topo-demo-6bbf65d967-jsj4h 1/1 Running 0 43s 10.244.2.236 node2 <none> <none>
8topo-demo-6bbf65d967-kc9x8 1/1 Running 0 43s 10.244.2.237 node2 <none> <none>
9topo-demo-6bbf65d967-nr9b9 1/1 Running 0 43s 10.244.1.109 node1 <none> <none>
10topo-demo-6bbf65d967-wfzmf 1/1 Running 0 43s 10.244.0.17 master1 <none> <none>可以看到符合我们的预期,每个节点上运行了2个 Pod 副本。
1#02-daemonset.yaml
2apiVersion: apps/v1
3kind: Deployment
4metadata:
5 name: topo-demo2
6spec:
7 replicas: 9
8 selector:
9 matchLabels:
10 app: topo2
11 template:
12 metadata:
13 labels:
14 app: topo2
15 spec:
16 tolerations:
17 - key: "node-role.kubernetes.io/master"
18 operator: "Exists"
19 effect: "NoSchedule"
20 containers:
21 - image: nginx
22 name: nginx
23 ports:
24 - containerPort: 80
25 name: ngpt1
26 topologySpreadConstraints:
27 - maxSkew: 1
28 topologyKey: kubernetes.io/hostname
29 whenUnsatisfiable: DoNotSchedule
30 labelSelector:
31 matchLabels:
32 app: topo2查看现象:
1hg@LAPTOP-G8TUFE0T:/mnt/c/Users/hg/Desktop/yaml/2022.2.19-40.拓扑分布约束-实验代码$ kubectl apply -f 02-daemonset.yaml
2deployment.apps/topo-demo2 created
3hg@LAPTOP-G8TUFE0T:/mnt/c/Users/hg/Desktop/yaml/2022.2.19-40.拓扑分布约束-实验代码$ kubectl get po -l app=topo2 -owide
4NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
5topo-demo2-87d77dbbd-25wbw 1/1 Running 0 53s 10.244.2.240 node2 <none> <none>
6topo-demo2-87d77dbbd-77gm8 1/1 Running 0 53s 10.244.1.113 node1 <none> <none>
7topo-demo2-87d77dbbd-97npc 1/1 Running 0 53s 10.244.0.20 master1 <none> <none>
8topo-demo2-87d77dbbd-fspcq 1/1 Running 0 53s 10.244.1.111 node1 <none> <none>
9topo-demo2-87d77dbbd-k5llk 1/1 Running 0 53s 10.244.1.112 node1 <none> <none>
10topo-demo2-87d77dbbd-pc24x 1/1 Running 0 53s 10.244.2.238 node2 <none> <none>
11topo-demo2-87d77dbbd-rdmwn 1/1 Running 0 53s 10.244.2.239 node2 <none> <none>
12topo-demo2-87d77dbbd-s4rjp 1/1 Running 0 53s 10.244.0.18 master1 <none> <none>
13topo-demo2-87d77dbbd-xrwmk 1/1 Running 0 53s 10.244.0.19 master1 <none> <none>实验结束。😘

从 kube-scheduler 的角度来看,它是通过一系列算法计算出最佳节点运行 Pod,当出现新的 Pod 进行调度时,调度程序会根据其当时对 Kubernetes 集群的资源描述做出最佳调度决定,但是 Kubernetes 集群是非常动态的,由于整个集群范围内的变化,比如一个节点为了维护,我们先执行了驱逐操作,这个节点上的所有 Pod 会被驱逐到其他节点去,但是当我们维护完成后,之前的 Pod 并不会自动回到该节点上来,因为 Pod 一旦被绑定了节点是不会触发重新调度的,由于这些变化,Kubernetes 集群在一段时间内就可能会出现不均衡的状态,所以需要均衡器来重新平衡集群。
当然我们可以去手动做一些集群的平衡,比如手动去删掉某些 Pod,触发重新调度就可以了,但是显然这是一个繁琐的过程,也不是解决问题的方式。为了解决实际运行中集群资源无法充分利用或浪费的问题,可以使用 descheduler 组件对集群的 Pod 进行调度优化,descheduler 可以根据一些规则和配置策略来帮助我们重新平衡集群状态,其核心原理是根据其策略配置找到可以被移除的 Pod 并驱逐它们,其本身并不会进行调度被驱逐的 Pod,而是依靠默认的调度器来实现,目前支持的策略有:
这些策略都是可以启用或者禁用的,作为策略的一部分,也可以配置与策略相关的一些参数,默认情况下,所有策略都是启用的。另外,还有一些通用配置,如下:
nodeSelector:限制要处理的节点evictLocalStoragePods: 驱逐使用 LocalStorage 的 PodsignorePvcPods: 是否忽略配置 PVC 的 Pods,默认是 FalsemaxNoOfPodsToEvictPerNode:节点允许的最大驱逐 Pods 数我们可以通过如下所示的 DeschedulerPolicy 来配置:(这里是要用一个configmap来进行使用的。)
1apiVersion: "descheduler/v1alpha1"
2kind: "DeschedulerPolicy"
3nodeSelector: prod=dev
4evictLocalStoragePods: true
5maxNoOfPodsToEvictPerNode: 40
6ignorePvcPods: false
7strategies: # 配置策略
8 ...🍂 kubernetes-sigs/descheduler官方网站
https://github.com/kubernetes-sigs/descheduler
🍂 重调度器

腾讯内部自研,水位线,干扰率

增加一个模拟调度,先模拟调度一下pod,如果能调度成功的话,就驱逐pod,如果模拟调度失败,就先不驱逐pod。
默认的descheduler可能存在一些问题的!
| 💘 实践:descheduler安装(测试成功)-2022.5.17 |
|---|
descheduler 可以以 Job、CronJob 或者 Deployment 的形式运行在 k8s 集群内,同样我们可以使用 Helm Chart 来安装 descheduler:1[root@master1 ~]#helm repo add descheduler https://kubernetes-sigs.github.io/descheduler/
2"descheduler" has been added to your repositories
3
4[root@master1 ~]#helm fetch descheduler/descheduler
5[root@master1 ~]#tar xf descheduler-0.23.1.tgz通过 Helm Chart 我们可以配置 descheduler 以 CronJob 或者 Deployment 方式运行。默认情况下 descheduler 会以一个 critical pod 运行,以避免被自己或者 kubelet 驱逐了,需要确保集群中有 system-cluster-critical 这个 Priorityclass:
1[root@master1 ~]#kubectl get priorityclass system-cluster-critical
2NAME VALUE GLOBAL-DEFAULT AGE
3system-cluster-critical 2000000000 false 111d注意:descheduler和k8s版本问题
https://github.com/kubernetes-sigs/descheduler
什么样的k8s集群版本就是用什么样的descheduler;

CronJob 的形式运行,执行周期为 schedule: "*/2 * * * *",这样每隔两分钟会执行一次 descheduler 任务,默认的配置策略如下所示: 1apiVersion: v1
2kind: ConfigMap
3metadata:
4 name: descheduler
5data:
6 policy.yaml: |
7 apiVersion: "descheduler/v1alpha1"
8 kind: "DeschedulerPolicy"
9 strategies:
10 LowNodeUtilization:
11 enabled: true
12 params:
13 nodeResourceUtilizationThresholds:
14 targetThresholds:
15 cpu: 50
16 memory: 50
17 pods: 50
18 thresholds:
19 cpu: 20
20 memory: 20
21 pods: 20
22 RemoveDuplicates:
23 enabled: true
24 RemovePodsViolatingInterPodAntiAffinity:
25 enabled: true
26 RemovePodsViolatingNodeAffinity:
27 enabled: true
28 params:
29 nodeAffinityType:
30 - requiredDuringSchedulingIgnoredDuringExecution
31 RemovePodsViolatingNodeTaints:
32 enabled: trueDeschedulerPolicy 的 strategies,可以指定 descheduler 的执行策略,这些策略都是可以启用或禁用的。下面我们会详细介绍,这里我们使用默认策略即可,使用如下命令直接安装即可:1[root@master1 ~]#helm upgrade --install descheduler descheduler/descheduler --set image.repository=cnych/descheduler,podSecurityPolicy.create=false -n kube-system
2Release "descheduler" does not exist. Installing it now.
3NAME: descheduler
4LAST DEPLOYED: Sat Feb 19 17:05:31 2022
5NAMESPACE: kube-system
6STATUS: deployed
7REVISION: 1
8NOTES:
9Descheduler installed as a CronJob .CronJob 资源对象来平衡集群状态:1➜ kubectl get cronjob -n kube-system
2NAME SCHEDULE SUSPEND ACTIVE LAST SCHEDULE AGE
3descheduler */2 * * * * False 1 27s 31s
4➜ kubectl get job -n kube-system
5NAME COMPLETIONS DURATION AGE
6descheduler-27378876 1/1 72s 79s
7➜ kubectl get pods -n kube-system -l job-name=descheduler-27378876
8NAME READY STATUS RESTARTS AGE
9descheduler-27378876--1-btjmd 0/1 Completed 0 2m21sdescheduler 任务,我们可以通过查看日志可以了解做了哪些平衡操作: 1➜ kubectl logs -f descheduler-27378876--1-btjmd -n kube-system
2I0121 02:37:10.127266 1 named_certificates.go:53] "Loaded SNI cert" index=0 certName="self-signed loopback" certDetail="\"apiserver-loopback-client@1642732630\" [serving] validServingFor=[apiserver-loopback-client] issuer=\"apiserver-loopback-client-ca@1642732629\" (2022-01-21 01:37:09 +0000 UTC to 2023-01-21 01:37:09 +0000 UTC (now=2022-01-21 02:37:10.127237 +0000 UTC))"
3I0121 02:37:10.127324 1 secure_serving.go:195] Serving securely on [::]:10258
4I0121 02:37:10.127363 1 tlsconfig.go:240] "Starting DynamicServingCertificateController"
5I0121 02:37:10.138724 1 node.go:46] "Node lister returned empty list, now fetch directly"
6I0121 02:37:10.172264 1 nodeutilization.go:167] "Node is overutilized" node="master1" usage=map[cpu:1225m memory:565Mi pods:16] usagePercentage=map[cpu:61.25 memory:15.391786081415567 pods:14.545454545454545]
7I0121 02:37:10.172313 1 nodeutilization.go:164] "Node is underutilized" node="node1" usage=map[cpu:675m memory:735Mi pods:16] usagePercentage=map[cpu:16.875 memory:9.542007959787252 pods:14.545454545454545]
8I0121 02:37:10.172328 1 nodeutilization.go:170] "Node is appropriately utilized" node="node2" usage=map[cpu:975m memory:1515Mi pods:15] usagePercentage=map[cpu:24.375 memory:19.66820054018583 pods:13.636363636363637]
9I0121 02:37:10.172340 1 lownodeutilization.go:100] "Criteria for a node under utilization" CPU=20 Mem=20 Pods=20
10I0121 02:37:10.172346 1 lownodeutilization.go:101] "Number of underutilized nodes" totalNumber=1
11I0121 02:37:10.172355 1 lownodeutilization.go:114] "Criteria for a node above target utilization" CPU=50 Mem=50 Pods=50
12I0121 02:37:10.172360 1 lownodeutilization.go:115] "Number of overutilized nodes" totalNumber=1
13I0121 02:37:10.172374 1 nodeutilization.go:223] "Total capacity to be moved" CPU=1325 Mem=3267772416 Pods=39
14I0121 02:37:10.172399 1 nodeutilization.go:226] "Evicting pods from node" node="master1" usage=map[cpu:1225m memory:565Mi pods:16]
15I0121 02:37:10.172485 1 nodeutilization.go:229] "Pods on node" node="master1" allPods=16 nonRemovablePods=13 removablePods=3
16I0121 02:37:10.172495 1 nodeutilization.go:236] "Evicting pods based on priority, if they have same priority, they'll be evicted based on QoS tiers"
17I0121 02:37:10.180353 1 evictions.go:130] "Evicted pod" pod="default/topo-demo-6bbf65d967-lzlfh" reason="LowNodeUtilization"
18I0121 02:37:10.181506 1 nodeutilization.go:269] "Evicted pods" pod="default/topo-demo-6bbf65d967-lzlfh" err=<nil>
19I0121 02:37:10.181541 1 nodeutilization.go:294] "Updated node usage" node="master1" CPU=1225 Mem=592445440 Pods=15
20I0121 02:37:10.182496 1 event.go:291] "Event occurred" object="default/topo-demo-6bbf65d967-lzlfh" kind="Pod" apiVersion="v1" type="Normal" reason="Descheduled" message="pod evicted by sigs.k8s.io/deschedulerLowNodeUtilization"
21......从日志中我们就可以清晰的知道因为什么策略驱逐了哪些 Pods。
要注意自己helm chart版本和老师转存的镜像是否一致,否则可能拉取镜像失败:
values.yaml内容:

Chart.yaml文件内容:

老师这里已经转存好的镜像如下:

因此这里需要用如下命令来拉取:
1helm upgrade --install descheduler descheduler/descheduler --set image.repository=cnych/descheduler,image.tag=v0.22.1,podSecurityPolicy.create=false -n kube-system
2
3[root@master1 ~]#helm upgrade --install descheduler descheduler/descheduler --set image.repository=cnych/descheduler,image.tag=v0.22.1,podSecurityPolicy.create=false -n kube-system
4Release "descheduler" does not exist. Installing it now.
5NAME: descheduler
6LAST DEPLOYED: Sat Feb 19 19:04:52 2022
7NAMESPACE: kube-system
8STATUS: deployed
9REVISION: 1
10NOTES:
11Descheduler installed as a CronJob .完美。
由于使用 descheduler 会将 Pod 驱逐进行重调度,但是如果一个服务的所有副本都被驱逐的话,则可能导致该服务不可用。如果服务本身存在单点故障,驱逐的时候肯定就会造成服务不可用了,这种情况我们强烈建议使用反亲和性和多副本来避免单点故障。但是如果服务本身就被打散在多个节点上,这些 Pod 都被驱逐的话,这个时候也会造成服务不可用了,这种情况下我们可以通过配置 PDB(PodDisruptionBudget) 对象来避免所有副本同时被删除,比如我们可以设置在驱逐的时候某应用最多只有一个副本不可用,则创建如下所示的资源清单即可:
1apiVersion: policy/v1
2kind: PodDisruptionBudget
3metadata:
4 name: pdb-demo
5spec:
6 maxUnavailable: 1 # 设置最多不可用的副本数量,或者使用 minAvailable,可以使用整数或百分比
7 selector:
8 matchLabels: # 匹配Pod标签
9 app: demo关于 PDB 的更多详细信息可以查看官方文档:https://kubernetes.io/docs/tasks/run-application/configure-pdb/。
所以如果我们使用
descheduler来重新平衡集群状态,那么我们强烈建议给应用创建一个对应的PodDisruptionBudget对象进行保护。
该策略用于驱逐比 maxPodLifeTimeSeconds 更旧的 Pods;可以通过 podStatusPhases 来配置哪类状态的 Pods 会被驱逐。建议为每个应用程序创建一个 PDB,以确保应用程序的可用性。
比如我们可以配置如下所示的策略来驱逐运行超过7天的 Pod:
1apiVersion: "descheduler/v1alpha1"
2kind: "DeschedulerPolicy"
3strategies:
4 "PodLifeTime":
5 enabled: true
6 params:
7 maxPodLifeTimeSeconds: 604800 # Pods 运行最多7天**该策略确保只有一个和 Pod 关联的 RS、Deployment 或者 Job 资源对象运行在同一节点上。**如果还有更多的 Pod 则将这些重复的 Pod 进行驱逐,以便更好地在集群中分散 Pod。如果某些节点由于某些原因崩溃了,这些节点上的 Pod 漂移到了其他节点,导致多个与 RS 关联的 Pod 在同一个节点上运行,就有可能发生这种情况,一旦出现故障的节点再次准备就绪,就可以启用该策略来驱逐这些重复的 Pod。

配置策略的时候,可以指定参数 excludeOwnerKinds 用于排除类型,这些类型下的 Pod 不会被驱逐:
1apiVersion: "descheduler/v1alpha1"
2kind: "DeschedulerPolicy"
3strategies:
4 "RemoveDuplicates":
5 enabled: true
6 params:
7 removeDuplicates:
8 excludeOwnerKinds:
9 - "ReplicaSet"这个策略用的想对比较多一些。
该策略主要用于查找未充分利用的节点,并从其他节点驱逐 Pod,以便 kube-scheudler 重新将它们调度到未充分利用的节点上。该策略的参数可以通过字段 nodeResourceUtilizationThresholds 进行配置。
节点的利用率不足可以通过配置 thresholds 阈值参数来确定,可以通过 CPU、内存和 Pods 数量的百分比进行配置。如果节点的使用率均低于所有阈值,则认为该节点未充分利用。

targetThresholds,用于计算可能驱逐 Pods 的潜在节点,该参数也可以配置 CPU、内存以及 Pods 数量的百分比进行配置。thresholds 和 targetThresholds 可以根据你的集群需求进行动态调整,如下所示示例: 1apiVersion: "descheduler/v1alpha1"
2kind: "DeschedulerPolicy"
3strategies:
4 "LowNodeUtilization":
5 enabled: true
6 params:
7 nodeResourceUtilizationThresholds:
8 thresholds: #注意:这个是未充分利用节点
9 "cpu" : 20
10 "memory": 20
11 "pods": 20
12 targetThresholds: #注意:这个可能驱逐 Pods 的潜在节点
13 "cpu" : 50
14 "memory": 50
15 "pods": 50注意:这里可以考虑下用p8s来采集node的负载指标然后写在这里进行自定义;
需要注意的是:
thresholds 和 targetThresholds 必须配置相同的类型thresholds 的配置不能高于 targetThresholds 的配置如果未指定任何资源类型,则默认是100%,以避免节点从未充分利用变为过度利用。
和 LowNodeUtilization 策略关联的另一个参数是 numberOfNodes,只有当未充分利用的节点数大于该配置值的时候,才可以配置该参数来激活该策略,该参数对于大型集群非常有用,其中有一些节点可能会频繁使用或短期使用不足,默认情况下,numberOfNodes 为0。
该策略可以确保从节点中删除违反 Pod 反亲和性的 Pod,比如某个节点上有 podA 这个 Pod,并且 podB 和 podC(在同一个节点上运行)具有禁止它们在同一个节点上运行的反亲和性规则,则 podA 将被从该节点上驱逐,以便 podB 和 podC 运行正常运行。当 podB 和 podC 已经运行在节点上后,反亲和性规则被创建就会发送这样的问题。

要禁用该策略,直接配置成 false 即可:
1apiVersion: "descheduler/v1alpha1"
2kind: "DeschedulerPolicy"
3strategies:
4 "RemovePodsViolatingInterPodAntiAffinity":
5 enabled: false该策略可以确保从节点中删除违反 NoSchedule 污点的 Pod,比如有一个名为 podA 的 Pod,通过配置容忍 key=value:NoSchedule 允许被调度到有该污点配置的节点上,如果节点的污点随后被更新或者删除了,则污点将不再被 Pods 的容忍满足,然后将被驱逐:
1apiVersion: "descheduler/v1alpha1"
2kind: "DeschedulerPolicy"
3strategies:
4 "RemovePodsViolatingNodeTaints":
5 enabled: true该策略确保从节点中删除违反节点亲和性的 Pod。比如名为 podA 的 Pod 被调度到了节点 nodeA,podA 在调度的时候满足了节点亲和性规则 requiredDuringSchedulingIgnoredDuringExecution,但是随着时间的推移,节点 nodeA 不再满足该规则了,那么如果另一个满足节点亲和性规则的节点 nodeB 可用,则 podA 将被从节点 nodeA 驱逐,如下所示的策略配置示例:
1apiVersion: "descheduler/v1alpha1"
2kind: "DeschedulerPolicy"
3strategies:
4 "RemovePodsViolatingNodeAffinity":
5 enabled: true
6 params:
7 nodeAffinityType:
8 - "requiredDuringSchedulingIgnoredDuringExecution"该策略确保从节点驱逐违反拓扑分布约束的 Pods,具体来说,它试图驱逐将拓扑域平衡到每个约束的 maxSkew 内所需的最小 Pod 数,不过该策略需要 k8s 版本高于1.18才能使用。
默认情况下,此策略仅处理硬约束,如果将参数 includeSoftConstraints 设置为 True,也将支持软约束。
1apiVersion: "descheduler/v1alpha1"
2kind: "DeschedulerPolicy"
3strategies:
4 "RemovePodsViolatingTopologySpreadConstraint":
5 enabled: true
6 params:
7 includeSoftConstraints: false该策略确保从节点中删除重启次数过多的 Pods,它的参数包括 podRestartThreshold(这是应将 Pod 逐出的重新启动次数),以及包括InitContainers,它确定在计算中是否应考虑初始化容器的重新启动,策略配置如下所示:
1apiVersion: "descheduler/v1alpha1"
2kind: "DeschedulerPolicy"
3strategies:
4 "RemovePodsHavingTooManyRestarts":
5 enabled: true
6 params:
7 podsHavingTooManyRestarts:
8 podRestartThreshold: 100
9 includingInitContainers: true在驱逐 Pods 的时候,有时并不需要所有 Pods 都被驱逐,descheduler 提供了两种主要的方式进行过滤:命名空间过滤和优先级过滤。
该策略可以配置是包含还是排除某些名称空间。可以使用该策略的有:
比如只驱逐某些命令空间下的 Pods,则可以使用 include 参数进行配置,如下所示:
1apiVersion: "descheduler/v1alpha1"
2kind: "DeschedulerPolicy"
3strategies:
4 "PodLifeTime":
5 enabled: true
6 params:
7 podLifeTime:
8 maxPodLifeTimeSeconds: 86400
9 namespaces:
10 include:
11 - "namespace1"
12 - "namespace2"又或者要排除掉某些命令空间下的 Pods,则可以使用 exclude 参数配置,如下所示:
1apiVersion: "descheduler/v1alpha1"
2kind: "DeschedulerPolicy"
3strategies:
4 "PodLifeTime":
5 enabled: true
6 params:
7 podLifeTime:
8 maxPodLifeTimeSeconds: 86400
9 namespaces:
10 exclude:
11 - "namespace1"
12 - "namespace2"所有策略都可以配置优先级阈值,只有在该阈值以下的 Pod 才会被驱逐。我们可以通过设置 thresholdPriorityClassName(将阈值设置为指定优先级类别的值)或 thresholdPriority(直接设置阈值)参数来指定该阈值。默认情况下,该阈值设置为 system-cluster-critical 这个 PriorityClass 类的值。
比如使用 thresholdPriority:
1apiVersion: "descheduler/v1alpha1"
2kind: "DeschedulerPolicy"
3strategies:
4 "PodLifeTime":
5 enabled: true
6 params:
7 podLifeTime:
8 maxPodLifeTimeSeconds: 86400
9 thresholdPriority: 10000或者使用 thresholdPriorityClassName 进行过滤:
1apiVersion: "descheduler/v1alpha1"
2kind: "DeschedulerPolicy"
3strategies:
4 "PodLifeTime":
5 enabled: true
6 params:
7 podLifeTime:
8 maxPodLifeTimeSeconds: 86400
9 thresholdPriorityClassName: "priorityclass1"不过需要注意不能同时配置 thresholdPriority 和 thresholdPriorityClassName,如果指定的优先级类不存在,则 descheduler 不会创建它,并且会引发错误。
当使用descheduler驱除Pods的时候,需要注意以下几点:
priorityClassName 设置为 system-cluster-critical 或 system-node-critical 的 PodLocalStorage 的 Pod 不会被驱逐,除非设置 evictLocalStoragePods: trueignorePvcPods: trueLowNodeUtilization 和 RemovePodsViolatingInterPodAntiAffinity 策略下,Pods 按优先级从低到高进行驱逐,如果优先级相同,Besteffort 类型的 Pod 要先于 Burstable 和 Guaranteed 类型被驱逐annotations 中带有 descheduler.alpha.kubernetes.io/evict 字段的 Pod 都可以被驱逐,该注释用于覆盖阻止驱逐的检查,用户可以选择驱逐哪个Pods--v=4 从 descheduler 日志中查找原因,如果驱逐违反 PDB 约束,则不会驱逐这类 Pods


代码的实现而是很简单的。
🍂 docs.gorance.io
🍂
p8s基本是k8s的标配!
这个调度插件只一个锦上添花的能力,而不会影响主调度能力的。
🍂
webhook,节点超售–实际生产上有大规模使用的!
🍂 动态调度成效


1.19版本以后:Schedulling Framework,已经达到了stable状态



前不久,Kubernetes发布了1.24版本,虽然没有太多重磅的特性更新,却引入了很多非常实用的功能,这让Kubernetes变得愈发成熟。
比如调度这块的NonPreemptingPriority、PreferNominatedNode等功能,都升级到了稳定版本,能很好地优化调度流程,满足调度需求。

我的博客主旨:
🍀 微信二维码 x2675263825 (舍得), qq:2675263825。

🍀 微信公众号 《云原生架构师实战》

🍀 个人博客站点

🍀 语雀
https://www.yuque.com/xyy-onlyone

🍀 csdn https://blog.csdn.net/weixin_39246554?spm=1010.2135.3001.5421

🍀 知乎 https://www.zhihu.com/people/foryouone

好了,关于本次就到这里了,感谢大家阅读,最后祝大家生活快乐,每天都过的有意义哦,我们下期见!

日历 · 精选 · 友链 · 更多
如果内容对你有帮助,欢迎请我喝杯咖啡 ☕



One的公众号
爱折腾博客的小白