运维实战 容器部分 Kubernetes调度

Posted 洛冰音

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了运维实战 容器部分 Kubernetes调度相关的知识,希望对你有一定的参考价值。

需求简介

  • 生产环境有对Pod调度规划的真实需求, 比如端口冲突的业务不能部署在同一物理机如何实现, 比如某些业务尽可能部署在同一物理机如何实现
  • 调度器通过K8Swatch机制来发现集群中新创建且尚未被调度到 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还支持多种规则匹配条件的配置

参数含义
Inlabel的值在列表内
NotInlabel的值不在列表内
Gtlabel的值大于设置的值, 不支持Pod亲和性
Ltlabel的值小于设置的值, 不支持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=ssdtag

这样, 即使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集群内部PodPod之间的关系.
  • 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, 因为server2server4都不能用于部署

  • 增加容忍设置的版本

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不会被调度到标记节点
PreferNoScheduleNoSchedule 的软策略版本
NoExecute不会调度到标记节点, 并且该节点内的Pod如无对应的Tolerate设置还会被驱逐

Tolerations相关设置

Tolerations中定义的key, value, effect, 要与node上设置的taint保持一致, 但并不是都要填写

  • operatorExists时, 可以省略value
  • operatorEqual时, keyvalue之间的关系必须相等
  • 如果不指定operator属性, 则默认值为Equal
  • 当不指定key, 再配合Exists就能匹配所有的keyvalue, 可以容忍所有污点
  • 当不指定effect , 则匹配所有的effect
tolerations:
  - key: "key"
    operator: "Equal"
    value: "value"
    effect: "NoSchedule"
对满足key=value 且污点策略为NoSchedule的节点进行容忍

tolerations:
  - operator: "Exists"
    effect: "NoSchedule"
容忍所有污点并不驱离

以上是关于运维实战 容器部分 Kubernetes调度的主要内容,如果未能解决你的问题,请参考以下文章

运维实战 容器部分 Kubernetes调度

运维实战 容器部分 Kubernetes存储

运维实战 容器部分 Kubernetes集群部署

运维实战 容器部分 Kubernetes集群部署

运维实战 容器部分 Kubernetes集群部署

运维实战 容器部分 Kubernetes存储