运维实战 容器部分 Kubernetes调度
- 需求简介
- 具体调度方法
-
- nodeName
- nodeSelector
- 亲和与反亲和
-
- 节点亲和
- Pod亲和
- Taints
-
- Taints设置的取值
- Tolerations相关设置
需求简介
- 生产环境有对
调度规划的真实需求, 比如端口冲突的业务不能部署在同一物理机如何实现, 比如某些业务尽可能部署在同一物理机如何实现Pod
- 调度器通过
的K8S
机制来发现集群中新创建且尚未被调度到watch
上的Node
. 调度器会将发现的每一个未调度的Pod
调度到一个合适的Pod
上来运行.Node
- 集群默认使用
作为调度器, 可以自行编写调度组件替换原有的kube-scheduler
kube-scheduler
- 也可以在资源清单中指定使用的调度策略
- 做调度分配时需要考虑许多要素, 如: 单独和整体的资源请求, 硬件/软件/策略限制, 亲和以及反亲和要求, 数据局限性, 负载间的干扰等等
具体调度方法
nodeName
-
是最简单的节点约束方法, 通常来说不推荐使用nodeName
- 通过在资源清单中指定
,Node
会被自动部署到该Pod
上, 但如果Node
不存在, 集群也会尝试这么做, 因而导致Node
Pending
局限性
- 指定的节点不存在时不会只能解决
- 节点存在, 但不满足物理需求(如资源/空间不足), 调度也会失败
- 在生产环境中
的名称并不总是稳定/可预测的Node
-
文件内容nodeName.yaml
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- name: nginx
image: nginx
nodeName: server3
- 测试结果
[[email protected] Schedule]# kubectl apply -f nodeName.yaml
Pod/nginx created
[[email protected] Schedule]# kubectl get Pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx 1/1 Running 0 10s 10.244.141.226 server3 <none> <none>
- 如果集群中没有符合条件的
, 比如上面的Node
改成server3
server10
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- name: nginx
image: nginx
nodeName: server10
- 则会出现调度失败, 并不会只能解决
- 因为集群中不存在
而你又要求调度到server10
上, 就会一直server10
Pending
[[email protected] Schedule]# kubectl delete Pod nginx
Pod "nginx" deleted
[[email protected] Schedule]# kubectl get Pod -o wide
No resources found in default namespace.
[[email protected] Schedule]# kubectl apply -f nodeName.yaml
Pod/nginx created
[[email protected] Schedule]# kubectl get Pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx 0/1 Pending 0 1s <none> server10 <none> <none>
nodeSelector
想必你也注意到了,
nodeName
并不能指定一类机器, 而只能指定固定机器
如果我们想定义一类具有同样特点的机器, 如这些机器都使用固态硬盘, 可以通过对节点打
tag
的方式来实现
与之对应,
nodeSelector
是通过
tag
进行调度筛选的机制, 也是节点选择约束的最简单推荐形式
-
文件内容nodeSelector.yaml
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
env: test
spec:
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent
nodeSelector:
disktype: ssd
- 测试流程
[[email protected] Schedule]# kubectl apply -f nodeSelector.yaml
Pod/nginx created
[[email protected] Schedule]# kubectl get Pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx 0/1 Pending 0 9s <none> <none> <none> <none>
- 此时, 所有结点上都没有
标签所以disktype: ssd
Pending
- 对
附加server3
的标签后, 调度正确实现disktype=ssd
[[email protected] Schedule]# kubectl label nodes server3 disktype=ssd
node/server3 labeled
[[email protected] Schedule]# kubectl get Pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx 1/1 Running 0 67s 10.244.141.227 server3 <none> <none>
亲和与反亲和
- 实现了 “硬要求” 和 “软需求” 并存, 如"必须满足A, 尽量满足B(B不满足也可以)"这类需求, 这极大扩展了表达约束的类型
- 亲和和反亲和不止支持节点标签, 也支持
标签, 如Pod
只能放在B服务
不存在的A服务
上这种需求Node
节点亲和
参数 | 含义 |
---|---|
requiredDuringSchedulingIgnoredDuringExecution | 必须满足 |
preferredDuringSchedulingIgnoredDuringExecution | 倾向满足 |
虽然这个参数很长, 但实际上是由两部分构成的, 前半部分表示
软/硬
后半部分的
IgnoreDuringExecution
表示如果在
Pod
运行期间
Node
的标签发生变化, 导致亲和性策略不能满足, 则继续运行当前的
Pod
, 而不直接驱离
nodeaffinity
还支持多种规则匹配条件的配置
参数 | 含义 |
---|---|
In | 的值在列表内 |
NotIn | 的值不在列表内 |
Gt | 的值大于设置的值, 不支持 亲和性 |
Lt | 的值小于设置的值, 不支持 亲和性 |
Exists | 设置的 存在 |
DoesNotExist | 设置的 不存在 |
- 测试用
文件内容node-affinity.yaml
apiVersion: v1
kind: Pod
metadata:
name: node-affinity
spec:
containers:
- name: nginx
image: nginx
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: disktype
operator: In
values:
- ssd
- 这里的需求是 满足
的节点,disktype=ssd
+key
+operator
共同完成了这一指名values
- 测试流程, 可以看到测试用的
被调度到了node-affinity
上server3
[[email protected] Schedule]# kubectl apply -f nodeAffinity_required.yaml
Pod/node-affinity created
[[email protected] Schedule]# kubectl get Pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
node-affinity 1/1 Running 0 5s 10.244.141.228 server3 <none> <none>
倾向满足
的案例
apiVersion: v1
kind: Pod
metadata:
name: node-affinity
spec:
containers:
- name: nginx
image: nginx
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: NotIn
values:
- server3
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1
preference:
matchExpressions:
- key: disktype
operator: In
values:
- ssd
在这个资源清单中存在两条要求:
- 必须满足 不能调度到
上server3
- 倾向满足
包含Node
的disktype=ssd
tag
这样, 即使
server4
上并没有这个
tag
, 也可以调度到
server4
上了
Pod亲和
-
主要解决PodAffinity
可以和哪些Pod
部署在同一个拓扑域中的问题(拓扑域用主机标签实现, 可以是单个主机, 也可以是多个主机组成的Pod
,cluster
等.) 与之效能相反的zone
主要解决PodAntiAffinity
不能和哪些Pod
部署在同一个拓扑域中的问题. 它们处理的是Pod
集群内部Kubernetes
和Pod
之间的关系.Pod
-
间亲和与反亲和在与更高级别的集合(例如Pod
,ReplicaSets
,StatefulSets
等)一起使用时, 它们可能更加有用. 可以轻松配置一组应位于相同定义拓扑(例如, 节点)中的工作负载.Deployments
-
间亲和与反亲和需要大量的处理, 这可能会显著减慢大规模集群中的调度. \Pod
测试样例
- 创建一个自主的包含
服务的nginx
Pod
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
- 创建一个包含
亲和需求的资源清单Pod
apiVersion: v1
kind: Pod
metadata:
name: mysql
labels:
app: mysql
spec:
containers:
- name: mysql
image: mysql
env:
- name: "MYSQL_ROOT_PASSWORD"
value: "westos"
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- nginx
topologyKey: kubernetes.io/hostname
[[email protected] Schedule]# kubectl apply -f nodeName.yaml
Pod/nginx created
[[email protected] Schedule]# kubectl get Pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATESnginx 1/1 Running 0 11s 10.244.141.230 server3 <none> <none>
[[email protected] Schedule]# kubectl apply -f PodAffinity.yaml
Pod/demo created
[[email protected] Schedule]# kubectl get Pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATESdemo 1/1 Running 0 6s 10.244.22.25 server4 <none> <none> nginx
1/1 Running 0 54s 10.244.141.230 server3 <none> <none>
可以看到, 两个
Pod
分别被调度到了2个不同的
Node
Taints
NodeAffinity
节点亲和性的目的是让
Pod
可以按照我们的需求调度到一个或一类
Node
上;
Taints
与之相反, 它能让
Node
拒绝运行
Pod
, 甚至驱离已经在该
Node
上运行的
Pod
Taints
(污点)是
Node
的一个属性, 设置了
Taints
后,
K8S
集群就不会把
Pod
调度到这个
Node
上了
如果想让
Pod
调度到有
Taints
的节点, 就需要给
Pod
设置
Tolerations
(容忍)属性
主节点上天生具有
Taints
, 因此才有了默认不会调度到主节点上的说法
- 相关命令
##为node1增加一个NoSchedule(不允许调度)的污点
kubectl taint nodes node1 key=value:NoSchedule
##查询server1上的污点情况
kubectl describe nodes server1 |grep Taints
##删除node1上的NoSchedule(不允许调度)污点属性
$ kubectl taint nodes node1 key:NoSchedule-
- 一个标准的
控制器部署Deployment
的清单nginx
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-server
spec:
selector:
matchLabels:
app: nginx
replicas: 3
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
- 主节点默认不允许调度
[[email protected] Schedule]# kubectl describe nodes server2 |grep TaintsTaints:
node-role.kubernetes.io/master:NoSchedule
- 为Server4增加驱离属性
[[email protected] Schedule]# kubectl taint node server4 key1=v1:NoExecute
node/server4 tainted
[[email protected] Schedule]# kubectl get Pod -o wide
- 如果设置正确, 你将只会在
上看到server3
, 因为Pod
和server2
都不能用于部署server4
- 增加容忍设置的版本
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-server
spec:
selector:
matchLabels:
app: nginx
replicas: 3
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
tolerations:
- key: "key1"
operator: "Equal"
value: "v1"
effect: "NoExecute"
Taints设置的取值
参数 | 含义 |
---|---|
NoSchedule | 不会被调度到标记节点 |
PreferNoSchedule | NoSchedule 的软策略版本 |
NoExecute | 不会调度到标记节点, 并且该节点内的 如无对应的 设置还会被驱逐 |
Tolerations相关设置
Tolerations
中定义的
key
,
value
,
effect
, 要与
node
上设置的
taint
保持一致, 但并不是都要填写
- 当
为operator
时, 可以省略Exists
value
- 当
为operator
时,Equal
与key
之间的关系必须相等value
- 如果不指定
属性, 则默认值为operator
Equal
- 当不指定
, 再配合key
就能匹配所有的Exists
与key
, 可以容忍所有污点value
- 当不指定
, 则匹配所有的effect
effect
tolerations:
- key: "key"
operator: "Equal"
value: "value"
effect: "NoSchedule"
对满足key=value 且污点策略为NoSchedule的节点进行容忍
tolerations:
- operator: "Exists"
effect: "NoSchedule"
容忍所有污点并不驱离