Skip to content

Commit 01df401

Browse files
committed
[add] 添加调度器
1 parent 484e68c commit 01df401

11 files changed

+419
-10
lines changed

Diff for: README.md

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
# 从Docker到Kubernetes进阶
22
从 Docker 入门一步步迁移到 Kubernetes 的进阶课程
33

4-
在线浏览:[https://k8s.qikqiak.com/](https://k8s.qikqiak.com/)
4+
在线浏览:[https://qikqiak.com/k8s-book](https://qikqiak.com/k8s-book/)
55

66
GitHub地址:[https://github.com/cnych/kubernetes-learning/](https://github.com/cnych/kubernetes-learning/)
77

8-
视频课程在线地址:[https://www.haimaxy.com/course/6n8xd6/](https://www.haimaxy.com/course/6n8xd6/)
8+
视频课程在线地址:[https://youdianzhishi.com/course/6n8xd6/](https://youdianzhishi.com/course/6n8xd6/)
99

1010

1111
## 介绍
@@ -30,15 +30,15 @@ GitHub地址:[https://github.com/cnych/kubernetes-learning/](https://github.co
3030

3131
## 社区&读者交流
3232

33-
* 博客:[阳明的博客](https://blog.qikqiak.com/)
34-
* 微信群:`k8s`技术圈,扫描我的微信二维码,[阳明](https://blog.qikqiak.com/page/about/),或直接搜索微信号**iEverything**后拉您入群,请增加备注(k8s或kubernetes)
33+
* 博客:[阳明的博客](https://qikqiak.com/)
34+
* 微信群:`k8s`技术圈,扫描我的微信二维码,[阳明](https://qikqiak.com/page/about/),或直接搜索微信号**iEverything**后拉您入群,请增加备注(k8s或kubernetes)
3535
* 知乎专栏:[k8s技术圈](https://zhuanlan.zhihu.com/kube100)
3636
* 开发者头条:[k8s技术圈](https://toutiao.io/subjects/268333)
3737
* 微信公众号:扫描下面的二维码关注微信公众号`k8s技术圈`
3838

3939
![k8s公众帐号](./docs/images/k8s-qrcode.png)
4040

41-
* 优点知识:[优点知识](https://www.haimaxy.com/)是一个综合的技术学习平台,本书配套的视频教程将会发布在该平台上面,感兴趣的朋友可以扫描下发的二维码关注自己感兴趣的课程。
41+
* 优点知识:[优点知识](https://youdianzhishi.com/)是一个综合的技术学习平台,本书配套的视频教程将会发布在该平台上面,感兴趣的朋友可以扫描下发的二维码关注自己感兴趣的课程。
4242

4343
![优点知识服务号](./docs/images/ydzs-qrcode.png)
4444
![优点知识小程序](./docs/images/ydzs-xcx.png)

Diff for: SUMMARY.md

+5
Original file line numberDiff line numberDiff line change
@@ -73,3 +73,8 @@
7373
* [Helm 模板之命名模板](docs/47.Helm模板之命名模板.md)
7474
* [Helm 模板之其他注意事项](docs/48.Helm模板之其他注意事项.md)
7575
* [Helm Hooks](docs/49.Helm Hooks.md)
76+
77+
78+
### 调度器
79+
* [Kubernetes 调度器介绍](docs/50.Kubernetes调度策略.md)
80+
* [Kubernetes 亲和性调度](docs/51.Kubernetes亲和性调度.md)

Diff for: book.json

+6-4
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
{
2-
"title": "从 Docker 到 Kubernetes 进阶",
2+
"title": "从 Docker 到 Kubernetes 进阶手册",
33
"description": "从 Docker 入门一步步迁移到 Kubernetes 的进阶课程学习指南",
44
"language": "zh-hans",
55
"author": "阳明",
66
"links": {
77
"sidebar": {
8-
"阳明的博客": "https://blog.qikqiak.com",
9-
"海马学院": "https://www.haimaxy.com"
8+
"阳明的博客": "https://qikqiak.com",
9+
"优点知识": "https://youdianzhishi.com",
10+
"我们一起学istio技术": "https://qikqiak.com/istio-book/",
11+
"python微服务实战": "https://qikqiak.com/tdd-book/"
1012
}
1113
},
1214
"plugins": [
@@ -52,7 +54,7 @@
5254
"size": "small"
5355
},
5456
"sitemap-general": {
55-
"prefix": "https://k8s.qikqiak.com"
57+
"prefix": "https://qikqiak.com/k8s-book/"
5658
},
5759
"tbfed-pagefooter": {
5860
"copyright": "Copyright © qikqiak.com 2018",

Diff for: docs/36.Jenkins Slave.md

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ spec:
1717
app: jenkins2
1818
spec:
1919
terminationGracePeriodSeconds: 10
20+
serviceAccount: jenkins2
2021
containers:
2122
- name: jenkins
2223
image: jenkins/jenkins:lts

Diff for: docs/42.Helm安装.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# 42.Helm安装使用
1+
# 42. Helm安装使用
22
`Helm`这个东西其实早有耳闻,但是一直没有用在生产环境,而且现在对这货的评价也是褒贬不一。正好最近需要再次部署一套测试环境,对于单体服务,部署一套测试环境我相信还是非常快的,但是对于微服务架构的应用,要部署一套新的环境,就有点折磨人了,微服务越多、你就会越绝望的。虽然我们线上和测试环境已经都迁移到了`kubernetes`环境,但是每个微服务也得维护一套`yaml`文件,而且每个环境下的配置文件也不太一样,部署一套新的环境成本是真的很高。如果我们能使用类似于`yum`的工具来安装我们的应用的话是不是就很爽歪歪了啊?`Helm`就相当于`kubernetes`环境下的`yum`包管理工具。
33

44

Diff for: docs/50.Kubernetes调度策略.md

+180
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
# 50. Kubernetes 调度器介绍
2+
`kube-scheduler`是 kubernetes 系统的核心组件之一,主要负责整个集群资源的调度功能,根据特定的调度算法和策略,将 Pod 调度到最优的工作节点上面去,从而更加合理、更加充分的利用集群的资源,这也是我们选择使用 kubernetes 一个非常重要的理由。如果一门新的技术不能帮助企业节约成本、提供效率,我相信是很难推进的。
3+
4+
## 调度流程
5+
默认情况下,kube-scheduler 提供的默认调度器能够满足我们绝大多数的要求,我们前面和大家接触的示例也基本上用的默认的策略,都可以保证我们的 Pod 可以被分配到资源充足的节点上运行。但是在实际的线上项目中,可能我们自己会比 kubernetes 更加了解我们自己的应用,比如我们希望一个 Pod 只能运行在特定的几个节点上,或者这几个节点只能用来运行特定类型的应用,这就需要我们的调度器能够可控。
6+
7+
`kube-scheduler` 是 kubernetes 的调度器,它的主要作用就是根据特定的调度算法和调度策略将 Pod 调度到合适的 Node 节点上去,是一个独立的二进制程序,启动之后会一直监听 API Server,获取到 PodSpec.NodeName 为空的 Pod,对每个 Pod 都会创建一个 binding。
8+
9+
![kube-scheduler structrue](./images/kube-scheduler-structrue.jpg)
10+
11+
这个过程在我们看来好像比较简单,但在实际的生产环境中,需要考虑的问题就有很多了:
12+
13+
* 如何保证全部的节点调度的公平性?要知道并不是说有节点资源配置都是一样的
14+
* 如何保证每个节点都能被分配资源?
15+
* 集群资源如何能够被高效利用?
16+
* 集群资源如何才能被最大化使用?
17+
* 如何保证 Pod 调度的性能和效率?
18+
* 用户是否可以根据自己的实际需求定制自己的调度策略?
19+
20+
考虑到实际环境中的各种复杂情况,kubernetes 的调度器采用插件化的形式实现,可以方便用户进行定制或者二次开发,我们可以自定义一个调度器并以插件形式和 kubernetes 进行集成。
21+
22+
kubernetes 调度器的源码位于 kubernetes/pkg/scheduler 中,大体的代码目录结构如下所示:(不同的版本目录结构可能不太一样)
23+
```shell
24+
kubernetes/pkg/scheduler
25+
-- scheduler.go //调度相关的具体实现
26+
|-- algorithm
27+
| |-- predicates //节点筛选策略
28+
| |-- priorities //节点打分策略
29+
|-- algorithmprovider
30+
| |-- defaults //定义默认的调度器
31+
```
32+
33+
其中 Scheduler 创建和运行的核心程序,对应的代码在 pkg/scheduler/scheduler.go,如果要查看`kube-scheduler`的入口程序,对应的代码在 cmd/kube-scheduler/scheduler.go。
34+
35+
调度主要分为以下几个部分:
36+
37+
* 首先是**预选过程**,过滤掉不满足条件的节点,这个过程称为`Predicates`
38+
* 然后是**优选过程**,对通过的节点按照优先级排序,称之为`Priorities`
39+
* 最后从中选择优先级最高的节点,如果中间任何一步骤有错误,就直接返回错误
40+
41+
`Predicates`阶段首先遍历全部节点,过滤掉不满足条件的节点,属于强制性规则,这一阶段输出的所有满足要求的 Node 将被记录并作为第二阶段的输入,如果所有的节点都不满足条件,那么 Pod 将会一直处于 Pending 状态,直到有节点满足条件,在这期间调度器会不断的重试。
42+
43+
> 所以我们在部署应用的时候,如果发现有 Pod 一直处于 Pending 状态,那么就是没有满足调度条件的节点,这个时候可以去检查下节点资源是否可用。
44+
45+
`Priorities`阶段即再次对节点进行筛选,如果有多个节点都满足条件的话,那么系统会按照节点的优先级(priorites)大小对节点进行排序,最后选择优先级最高的节点来部署 Pod 应用。
46+
47+
下面是调度过程的简单示意图:
48+
![kube-scheduler filter](./images/kube-scheduler-filter.jpg)
49+
50+
51+
更详细的流程是这样的:
52+
53+
* 首先,客户端通过 API Server 的 REST API 或者 kubectl 工具创建 Pod 资源
54+
* API Server 收到用户请求后,存储相关数据到 etcd 数据库中
55+
* 调度器监听 API Server 查看为调度(bind)的 Pod 列表,循环遍历地为每个 Pod 尝试分配节点,这个分配过程就是我们上面提到的两个阶段:
56+
* 预选阶段(Predicates),过滤节点,调度器用一组规则过滤掉不符合要求的 Node 节点,比如 Pod 设置了资源的 request,那么可用资源比 Pod 需要的资源少的主机显然就会被过滤掉
57+
* 优选阶段(Priorities),为节点的优先级打分,将上一阶段过滤出来的 Node 列表进行打分,调度器会考虑一些整体的优化策略,比如把 Deployment 控制的多个 Pod 副本分布到不同的主机上,使用最低负载的主机等等策略
58+
* 经过上面的阶段过滤后选择打分最高的 Node 节点和 Pod 进行 binding 操作,然后将结果存储到 etcd 中
59+
* 最后被选择出来的 Node 节点对应的 kubelet 去执行创建 Pod 的相关操作
60+
61+
62+
其中`Predicates`过滤有一系列的算法可以使用,我们这里简单列举几个:
63+
64+
* PodFitsResources:节点上剩余的资源是否大于 Pod 请求的资源
65+
* PodFitsHost:如果 Pod 指定了 NodeName,检查节点名称是否和 NodeName 匹配
66+
* PodFitsHostPorts:节点上已经使用的 port 是否和 Pod 申请的 port 冲突
67+
* PodSelectorMatches:过滤掉和 Pod 指定的 label 不匹配的节点
68+
* NoDiskConflict:已经 mount 的 volume 和 Pod 指定的 volume 不冲突,除非它们都是只读的
69+
* CheckNodeDiskPressure:检查节点磁盘空间是否符合要求
70+
* CheckNodeMemoryPressure:检查节点内存是否够用
71+
72+
除了这些过滤算法之外,还有一些其他的算法,更多更详细的我们可以查看源码文件:k8s.io/kubernetes/pkg/scheduler/algorithm/predicates/predicates.go。
73+
74+
`Priorities`优先级是由一系列键值对组成的,键是该优先级的名称,值是它的权重值,同样,我们这里给大家列举几个具有代表性的选项:
75+
76+
* LeastRequestedPriority:通过计算 CPU 和内存的使用率来决定权重,使用率越低权重越高,当然正常肯定也是资源是使用率越低权重越高,能给别的 Pod 运行的可能性就越大
77+
* SelectorSpreadPriority:为了更好的高可用,对同属于一个 Deployment 或者 RC 下面的多个 Pod 副本,尽量调度到多个不同的节点上,当一个 Pod 被调度的时候,会先去查找该 Pod 对应的 controller,然后查看该 controller 下面的已存在的 Pod,运行 Pod 越少的节点权重越高
78+
* ImageLocalityPriority:就是如果在某个节点上已经有要使用的镜像节点了,镜像总大小值越大,权重就越高
79+
* NodeAffinityPriority:这个就是根据节点的亲和性来计算一个权重值,后面我们会详细讲解亲和性的使用方法
80+
81+
除了这些策略之外,还有很多其他的策略,同样我们可以查看源码文件:k8s.io/kubernetes/pkg/scheduler/algorithm/priorities/ 了解更多信息。每一个优先级函数会返回一个0-10的分数,分数越高表示节点越优,同时每一个函数也会对应一个表示权重的值。最终主机的得分用以下公式计算得出:
82+
```shell
83+
finalScoreNode = (weight1 * priorityFunc1) + (weight2 * priorityFunc2) + … + (weightn * priorityFuncn)
84+
```
85+
86+
87+
## 自定义调度
88+
上面就是 kube-scheduler 默认调度的基本流程,除了使用默认的调度器之外,我们也可以自定义调度策略。
89+
90+
### 调度器扩展
91+
`kube-scheduler`在启动的时候可以通过 `--policy-config-file`参数来指定调度策略文件,我们可以根据我们自己的需要来组装`Predicates``Priority`函数。选择不同的过滤函数和优先级函数、控制优先级函数的权重、调整过滤函数的顺序都会影响调度过程。
92+
93+
下面是官方的 Policy 文件示例:
94+
```json
95+
{
96+
"kind" : "Policy",
97+
"apiVersion" : "v1",
98+
"predicates" : [
99+
{"name" : "PodFitsHostPorts"},
100+
{"name" : "PodFitsResources"},
101+
{"name" : "NoDiskConflict"},
102+
{"name" : "NoVolumeZoneConflict"},
103+
{"name" : "MatchNodeSelector"},
104+
{"name" : "HostName"}
105+
],
106+
"priorities" : [
107+
{"name" : "LeastRequestedPriority", "weight" : 1},
108+
{"name" : "BalancedResourceAllocation", "weight" : 1},
109+
{"name" : "ServiceSpreadingPriority", "weight" : 1},
110+
{"name" : "EqualPriority", "weight" : 1}
111+
]
112+
}
113+
```
114+
115+
### 多调度器
116+
如果默认的调度器不满足要求,还可以部署自定义的调度器。并且,在整个集群中还可以同时运行多个调度器实例,通过 podSpec.schedulerName 来选择使用哪一个调度器(默认使用内置的调度器)。
117+
```yaml
118+
apiVersion: v1
119+
kind: Pod
120+
metadata:
121+
name: nginx
122+
labels:
123+
app: nginx
124+
spec:
125+
schedulerName: my-scheduler # 选择使用自定义调度器 my-scheduler
126+
containers:
127+
- name: nginx
128+
image: nginx:1.10
129+
```
130+
131+
要开发我们自己的调度器也是比较容易的,比如我们这里的 my-scheduler:
132+
133+
* 首先需要通过指定的 API 获取节点和 Pod
134+
* 然后选择`phase=Pending`和`schedulerName=my-scheduler`的pod
135+
* 计算每个 Pod 需要放置的位置之后,调度程序将创建一个`Binding`对象
136+
* 然后根据我们自定义的调度器的算法计算出最适合的目标节点
137+
138+
### 优先级调度
139+
与前面所讲的调度优选策略中的优先级(Priorities)不同,前面所讲的优先级指的是节点优先级,而我们这里所说的优先级 pod priority 指的是 Pod 的优先级,高优先级的 Pod 会优先被调度,或者在资源不足低情况牺牲低优先级的 Pod,以便于重要的 Pod 能够得到资源部署。
140+
141+
要定义 Pod 优先级,就需要先定义`PriorityClass`对象,该对象没有 Namespace 的限制:
142+
```yaml
143+
apiVersion: v1
144+
kind: PriorityClass
145+
metadata:
146+
name: high-priority
147+
value: 1000000
148+
globalDefault: false
149+
description: "This priority class should be used for XYZ service pods only."
150+
```
151+
152+
其中:
153+
154+
* `value`为 32 位整数的优先级,该值越大,优先级越高
155+
* `globalDefault`用于未配置 PriorityClassName 的 Pod,整个集群中应该只有一个`PriorityClass`将其设置为 true
156+
157+
158+
然后通过在 Pod 的`spec.priorityClassName`中指定已定义的`PriorityClass`名称即可:
159+
```yaml
160+
apiVersion: v1
161+
kind: Pod
162+
metadata:
163+
name: nginx
164+
labels:
165+
app: nginx
166+
spec:
167+
containers:
168+
- name: nginx
169+
image: nginx
170+
imagePullPolicy: IfNotPresent
171+
priorityClassName: high-priority
172+
```
173+
174+
另外一个值得注意的是当节点没有足够的资源供调度器调度 Pod,导致 Pod 处于 pending 时,抢占(preemption)逻辑就会被触发。`Preemption`会尝试从一个节点删除低优先级的 Pod,从而释放资源使高优先级的 Pod 得到节点资源进行部署。
175+
176+
现在我们通过下面的图再去回顾下 kubernetes 的调度过程是不是就清晰很多了:
177+
![kube-scheduler](./images/kube-scheduler-detail.png)
178+
179+
下节课我们再和大家讲解关于 Pod 调度的一些具体使用方法。
180+

0 commit comments

Comments
 (0)