运维实战 容器部分 Kubernetes调度
Posted 洛冰音
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了运维实战 容器部分 Kubernetes调度相关的知识,希望对你有一定的参考价值。
运维实战 容器部分 Kubernetes调度
需求简介
- 生产环境有对
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
- 测试结果
[root@Server2 Schedule]# kubectl apply -f nodeName.yaml
Pod/nginx created
[root@Server2 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
[root@Server2 Schedule]# kubectl delete Pod nginx
Pod "nginx" deleted
[root@Server2 Schedule]# kubectl get Pod -o wide
No resources found in default namespace.
[root@Server2 Schedule]# kubectl apply -f nodeName.yaml
Pod/nginx created
[root@Server2 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
- 测试流程
[root@Server2 Schedule]# kubectl apply -f nodeSelector.yaml
Pod/nginx created
[root@Server2 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
的标签后, 调度正确实现
[root@Server2 Schedule]# kubectl label nodes server3 disktype=ssd
node/server3 labeled
[root@Server2 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 | label 的值在列表内 |
NotIn | label 的值不在列表内 |
Gt | label 的值大于设置的值, 不支持Pod 亲和性 |
Lt | label 的值小于设置的值, 不支持Pod 亲和性 |
Exists | 设置的label 存在 |
DoesNotExist | 设置的label 不存在 |
- 测试用
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
上
[root@Server2 Schedule]# kubectl apply -f nodeAffinity_required.yaml
Pod/node-affinity created
[root@Server2 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
上了
[root@Server2 Schedule]# kubectl apply -f nodeAffinity_preferred.yaml Pod/node-affinity created[root@Server2 Schedule]# kubectl get Pod -o wideNAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATESnode-affinity 1/1 Running 0 27s 10.244.22.24 server4 <none> <none>
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
[root@Server2 Schedule]# kubectl apply -f nodeName.yaml
Pod/nginx created
[root@Server2 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>
[root@Server2 Schedule]# kubectl apply -f PodAffinity.yaml
Pod/demo created
[root@Server2 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
- 主节点默认不允许调度
[root@Server2 Schedule]# kubectl describe nodes server2 |grep TaintsTaints:
node-role.kubernetes.io/master:NoSchedule
- 为Server4增加驱离属性
[root@Server2 Schedule]# kubectl taint node server4 key1=v1:NoExecute
node/server4 tainted
[root@Server2 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 | 不会调度到标记节点, 并且该节点内的Pod 如无对应的Tolerate 设置还会被驱逐 |
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"
容忍所有污点并不驱离
以上是关于运维实战 容器部分 Kubernetes调度的主要内容,如果未能解决你的问题,请参考以下文章