百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 热门文章 > 正文

Kubernetes自动化运维|Pod调度实践,节点亲和性与数据结构解析

bigegpt 2024-10-26 08:18 6 浏览


Kubernetes是生产级容器编排、自动化容器部署、扩展和管理的系统;这里会告诉你如何对约束一个 Pod 只能在特定的 Node上运行,或者优先运行在特定的节点上。

1 - 实验的环境

集群环境:

里面包含的IP地址均是内部网络使用的虚拟IP;

命名空间:

默认命名空间此时只存在默认的service资源,无任何Pod在运行;

Pod部署配置:

apiVersion: v1
kind: Pod
metadata:
  name: counter
  namespace: default
  labels:
    app: counter
    release: 0.1.0
spec:
  # 根据各实践案例,植入的调度策略配置
  # ${scheduler_policy}

  containers:
  - name: count
    image: alpine:3.8
    args: [/bin/sh, -c, 'i=0; while true; do echo "$i: $(date)"; i=$((i+1)); sleep 1; done']
    resources:
      requests:
        cpu: 100m
        memory: 100Mi

如果不设置任何调度策略,直接使用以上配置,Pod将会在任意节点运行;以下所有测试均基于k8s集群1.10版本,涉及内容均为核心功能,即使后期新版也不会轻易被废弃,所有Pod调度由kube-scheduler进程管理控制。

2 - NodeName方式调度

2.1 - 调度策略

${scheduler_policy}

spec:
  nodeName: 10.5.119.105

2.2 - 数据结构

type PodSpec struct {
    ......
    // Pod所在的Node节点
    // 如果是运行时候,表示Pod所在的节点
    // 如果是部署时候,设置为对应节点的名称,则默认调度器会忽略调度,直接将Pod部署至该节点
    NodeName string
    
    ......
}

2.3 - 运行示例

  • 查看默认命名空间下的Pod
  • 运行部署配置与查询结果

根据调度策略中的设置,Pod被调度至正确的主机上了

  • 删除该Pod资源,以免影响后续实验

3 - NodeSelector方式调度

3.1 - 调度策略

${scheduler_policy}

spec:
  nodeSelector:
    a-demo-web.site/disk-type=ssd

3.2 - 数据结构

type PodSpec struct {
    ......
    // 首先集群中各个Node均设置各自的标签
    // 然后这里设置与Node对应的标签名称与值,则该Pod将会调度至该Node上
    // 示例:a-demo-web.site/disk-type=ssd
    NodeSelector map[string]string
    ......
}

3.3 - 运行示例

  • 查看集群是否存在a-demo-web.site开头的标签,并添加调度策略部署Pod,观察状态

持续处于Pending状态,通过观察事件:

0/12 nodes are available: 12 node(s) didn't match node selector.

发现集群中没有存在符合调度的Node资源,所以Pod会一直处于Pending状态

  • 为主机10.5.119.137绑定标签:a-demo-web.site/disk-type=ssd
  • 通过添加标签后,另一个窗口实时监听Pod运行状态(Pod能否获得调度?)

根据调度策略中的设置,Pod被调度至正确的主机上了

  • 已经运行的Pod,此时撤销对应节点标签(标签名末尾添加中划线:-)

观察Pod的变化,能否导致Pod被驱逐、下线?

已完成调度的Pod,尽管此时节点标签变化不满足运行需求,是不会导致Pod被驱逐的

  • 删除该Pod资源,以免影响后续实验

3.4 - 内置标签

除了使用自定义的标签外,集群也内置了几个标签,可以设置它们进行相应调度

3.5 - 应用场景

如果我们指定了Pod的nodeSelector条件,而集群中不存在包含相关条件的节点,此时该Pod是无法被调度,即使集群其他节点是空闲状态。

所以实际上在生产环境中使用nodeSelector局限性还是比较大,可控性低,随着k8s集群版本的更新,在后续将会被废弃,由NodeAffinity代替。

4 - NodeAffinity:节点的亲和性

4.1 - 配置示例

spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: a-demo-web.site/host-type
            operator: Exists
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 100
        preference:
          matchExpressions:
          - key: a-demo-web.site/host-type
            operator: In
            values:
            - "physical"
          - key: a-demo-web.site/disk-type
            operator: In
            values:
            - "local"

自定义标签说明:

4.2 - 配置说明

存在两种表达式:

  • requiredDuringSchedulingIgnoredDuringExecution
  • preferredDuringSchedulingIgnoredDuringExecution

注意:

  1. 如果同时定义了nodeSelector和nodeAffinity,那么必须两个条件都要得到满足,Pod才能运行到指定节点
  2. 如果nodeAffinity指定了多个nodeSelectorTerms,那么只要其中一个匹配即可
  3. 如果nodeSelectorTerms中有多个matchExpressions,则一个节点必须满足所有matchExpressions才能运行该Pod

对于每个符合所有调度要求的节点,调度器将遍历该字段的元素来计算总和,并且如果节点匹配对应的MatchExpressions,则添加权重到总和,然后对各个节点评分,总分最高的节点是最优选的。

4.3 - 数据结构

Affinity

// 一组亲和性的调度规则
type Affinity struct {
    // 节点亲和性调度策略
    // json:"nodeAffinity,omitempty"
    NodeAffinity *NodeAffinity 

    // Pod的亲和性调度策略
    // 用于控制相同的Pod出现在同一个主机、可用区等
    // json:"podAffinity,omitempty"
    PodAffinity *PodAffinity

    // Pod的互斥性调度策略
    // 用于控制相同的Pod避免在同一个主机、可用区等
    // json:"podAntiAffinity,omitempty"
    PodAntiAffinity *PodAntiAffinity
}

NodeAffinity

// 节点亲和性的数据结构
type NodeAffinity struct {
    // 硬限制;必须满足指定的规则才可以调度Pod到节点上(与nodeSelector功能一样,但语法不同)
    // json:"requiredDuringSchedulingIgnoredDuringExecution,omitempty"
    RequiredDuringSchedulingIgnoredDuringExecution *NodeSelector 

    // 软限制;根据节点权重总分最高为最优,调度器调度Pod到该节点上
    // 同时必须满足nodeSelector、requiredDuringScheduling的规则(AND关系)
    // json:"preferredDuringSchedulingIgnoredDuringExecution,omitempty"
    PreferredDuringSchedulingIgnoredDuringExecution []PreferredSchedulingTerm
}

XXXXXXXIgnoredDuringExecution的含义:如果一个Pod已经运行,且它所在Node标签发生了变更,不在符合调度策略,则系统忽略,该Pod可以继续运行,也就是亲和性实际只有在调度期间才会使用(类型nodeSelector方式调度,调度成功后移除Node标签,并不会导致Pod被驱逐)

NodeSelector

// 节点选择器的数据结构
// 表示一组节点上多个标签查询结果的并集
type NodeSelector struct {
    // 在RequiredDuringScheduling调度策略下如果设置多个,则它们之间是OR的关系
    // 也就是只要满足其中之一即可
    // json:"nodeSelectorTerms"
    NodeSelectorTerms []NodeSelectorTerm
}

PreferredSchedulingTerm

// 首选调度项的数据结构
type PreferredSchedulingTerm struct {
    // 节点选择项的权重,取值范围:1至100
    // json:"weight"
    Weight int32 

    // 具体节点选择项设置,关联上面的权重
    // json:"preference"
    Preference NodeSelectorTerm
}

NodeSelectorTerm

// 节点选择项的数据结构
// 初始化一个策略,但不设置任何规则,则不匹配任何对象
// 如果设置了多个Match规则,它们之间是AND关系,也就是说必须全匹配
type NodeSelectorTerm struct {
    // 根据自定义的标签名称、标签值,编写表达式去匹配
    // json:"matchExpressions,omitempty"
    MatchExpressions []NodeSelectorRequirement

    // 早于kubernetes1.11版本不存在
    // 根据内置固定的字段调度,如:metadata.name
    // TODO: 待后续的案例分析详解释
    // json:"matchFields,omitempty"
    MatchFields []NodeSelectorRequirement
}

NodeSelectorRequirement

// 节点选择项规则的数据结构
// 它由key、operator、values组成,不同的operator会有不一样的values结构
type NodeSelectorRequirement struct {
    // 这个是标签名称,对应CMDB中规定的值,非顺便乱写
    // json:"key"
    Key string

    // 节点选择操作符,表示对Key对应Value的操作
    // 可取六个值:In、NotIn、Exists、DoesNotExist、Gt、Lt
    // json:"operator"
    Operator NodeSelectorOperator

    // 这里编写value均是以字符串表示
    Values []string `json:"values,omitempty" protobuf:"bytes,3,rep,name=values"`
}

这里Values是数组对应标签值,根据不同的Operator,可以设置0、1、多个标签值。

这里要区分注意每个主机节点设置的标签键值是1对1的,如:a-demo-web.site/disk-type=ssd,而不是可以设置数组形式,如:a-demo-web.site/disk-type=[ssd, local](没有这种写法)。

当Operator=In 或 NotIn 时,Values可以设置为数组(多个标签值),这些标签值是来自集群中各个节点的并集。

NodeSelectorOperator

// 节点选择操作符
type NodeSelectorOperator string

const (
    NodeSelectorOpIn           NodeSelectorOperator = "In"
    NodeSelectorOpNotIn        NodeSelectorOperator = "NotIn"
    NodeSelectorOpExists       NodeSelectorOperator = "Exists"
    NodeSelectorOpDoesNotExist NodeSelectorOperator = "DoesNotExist"
    NodeSelectorOpGt           NodeSelectorOperator = "Gt"
    NodeSelectorOpLt           NodeSelectorOperator = "Lt"
)

所以规则中只支持这六种操作符运算

4.4 - 节点选择操作符逻辑代码

switch op {
    // 如果 Operator=In 或 NotIn,则 Values 必须设置至少一个值
    case selection.In, selection.NotIn:
        if len(vals) == 0 {
            return nil, fmt.Errorf("for 'in', 'notin' operators, values set can't be empty")
        }
    // NodeSelectorRequirement 不支持这三种操作符
    case selection.Equals, selection.DoubleEquals, selection.NotEquals:
        if len(vals) != 1 {
            return nil, fmt.Errorf("exact-match compatibility requires one single value")
        }
    // 如果 Operator=Exists 或 DoesNotExist,则 Values 必须为空
    case selection.Exists, selection.DoesNotExist:
        if len(vals) != 0 {
            return nil, fmt.Errorf("values set must be empty for exists and does not exist")
        }
    // 如果 Operator=Gt 或 Lt,则 Values 必须只能设置一个值,且必须为整数(不能包含小数点)
    case selection.GreaterThan, selection.LessThan:
        if len(vals) != 1 {
            return nil, fmt.Errorf("for 'Gt', 'Lt' operators, exactly one value is required")
        }
        for i := range vals {
            if _, err := strconv.ParseInt(vals[i], 10, 64); err != nil {
                return nil, fmt.Errorf("for 'Gt', 'Lt' operators, the value must be an integer")
            }
        }
    default:
        return nil, fmt.Errorf("operator '%v' is not recognized", op)
}

5 - NodeAffinity "硬限制" 调度

5.1 - 调度策略

${scheduler_policy}

spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: a-demo-web.site/disk-type
            operator: In
            values:
            - local_ssd
           - key: a-demo-web.site/host-type
             operator: In
             values:
             - kvm
        - matchExpressions:
          - key: a-demo-web.site/host-type
            operator: In
            values:
            - vmware
            - physical

策略说明部署Pod至包含标签:a-demo-web.site/disk-type=local_ssd 且 a-demo-web.site/host-type=vmware 或 physciall 其中之一。

5.2 - 应用示例:多个 nodeSelectorTerms 的关系

  • 确认当前集群不包含任何 a-demo-web.site 开头标签的节点
  • 打开另外一个终端,开启实时监听默认命名空间下Pod的状态
  • 添加调度策略,部署应用,此时观察到Pod持续处于Pending状态


  • 为节点225添加标签 a-demo-web.site/host-type=vmware

此时等待一会...

可以看到Pod被调度至225节点上了,也验证了多个nodeSelectorTerms之间为OR的关系

  • 移除225的标签 a-demo-web.site/host-type=vmware,已调度的Pod不会影响,仍然继续运行
  • 删除该Pod资源,以免影响后续实验

5.3 - 应用示例:多个 matchExpressions 的关系

  • 为避免干扰同样验证集群中需不存在 a-demo-web.site 开头的标签,然后部署应用
  • 为节点224添加标签:a-demo-web.site/disk-type=local_ssd

由于并不满足所有条件,此时Pod还是维持Pending状态

  • 为节点224添加标签:a-demo-web.site/host-type=kvm
  • 观察原先开启的实时监听窗口

由于策略寻找到合适的节点,成功调度Pod,同时验证多个matchExpressions之间是AND的关系

  • 同样,移除节点的标签,并不会导致原先已调度的Pod
  • 删除该Pod资源,以免影响后续实验

6 - NodeAffinity "软限制" 调度

6.1 - 调度策略

${scheduler_policy}

spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: a-demo-web.site/host-type
            operator: Exists
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 100
        preference:
          matchExpressions:
          - key: a-demo-web.site/host-type
            operator: In
            values:
            - "physical"
          - key: a-demo-web.site/disk-type
            operator: In
            values:
            - "local"
      - weight: 80
        preference:
          matchExpressions:
          - key: a-demo-web.site/host-type
            operator: In
            values:
            - "kvm"
            - "vmware"
          - key: a-demo-web.site/disk-type
            operator: In
            values:
            - "local"
      - weight: 30
        preference:
          matchExpressions:
          - key: a-demo-web.site/cloud-provider
            operator: In
            values:
            - "aws"

由于preferredDuringSchedulingIgnoredDuringExecution是"软限制",也就是说为匹配最优节点,也会被调度并部署至其他节点,所以这里添加了requiredDuringSchedulingIgnoredDuringExecution策略,以配合实验的成功演示。

匹配 a-demo-web.site/host-type=physical 且 a-demo-web.site/disk-type=local(物理服务器本地普通磁盘)则节点权重为100

匹配 a-demo-web.site/host-type=kvm 或 vmware 且 a-demo-web.site/disk-type=local(基于kvm或vmware的虚拟机)则节点权重为80

匹配 a-demo-web.site/cloud-provider=aws(主机云服务提供商为亚马逊)则节点权重为30

6.2 - 请求示例

  • 检查当前集群不包含a-demo-web.site开头的标签,并创建Pod
  • 查看创建的Pod并实时监听状态的变化

不管等待多久,会持续处于Pending状态,查询事件,没有匹配的节点。

0/12 nodes are available: 12 node(s) didn't match node selector.
  • 为节点135/136/137添加标签:a-demo-web.site/disk-type=local

此时只匹配到标签 a-demo-web.site/disk-type=local,对Pod并没有什么影响,还是处于Pending状态。

  • 为节点135添加标签:a-demo-web.site/cloud-provider=aws


此时节点135相比其他,拥有30的权重,但还是不会吧Pod调度过来,因为前面设置了 requiredDuringSchedulingIgnoredDuringExecution 策略,如果没有责135就是最优的节点。

  • 为节点136添加标签:a-demo-web.site/host-type=physical


此时节点136相比其他,拥有100的权重,同时又满足requiredDuringSchedulingIgnoredDuringExecution设置的策略,最符合调度,所以Pod被部署至该节点上。

  • 删除该Pod,并为节点137添加标签:a-demo-web.site/host-type=kvm
  • 重新部署nodeaffinity-preferred.yaml,观察此时会被调度至哪个节点


此时节点137拥有权重80,而136拥有权重100,仍然是136是最佳被调度节点。

  • 删除该Pod,为节点135添加标签:a-demo-web.site/host-type=kvm
  • 重新部署nodeaffinity-preferred.yaml,观察此时会被调度至哪个节点


此时135的权重为80+30=110,136节点的权重为100,137节点的权重为80,所以被调度部署至135节点。

7 - 最佳实践的总结

  1. 所有涉及的标签应该包含一个前缀,例如:a-demo-web.site/host-type;
  2. 所有涉及的标签应该来自一个权威数据中心,而非各集群管理员私下随意定义;
  3. 一般情况下不使用nodeName、nodeSelector进行Pod调度,一律通过nodeAffinity代替;
  4. 保障nodeAffinity中规则的简单,尽量使用“软限制"方式;

相关推荐

Docker篇(二):Docker实战,命令解析

大家好,我是杰哥上周我们通过几个问题,让大家对于Docker有了一个全局的认识。然而,说跟练往往是两个概念。从学习的角度来说,理论知识的学习,往往只是第一步,只有经过实战,才能真正掌握一门技术所以,本...

docker学习笔记——安装和基本操作

今天学习了docker的基本知识,记录一下docker的安装步骤和基本命令(以CentOS7.x为例)一、安装docker的步骤:1.yuminstall-yyum-utils2.yum-con...

不可错过的Docker完整笔记(dockerhib)

简介一、Docker简介Docker是一个开源的应用容器引擎,基于Go语言并遵从Apache2.0协议开源。Docker可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中,...

扔掉运营商的 IPTV 机顶盒,全屋全设备畅看 IPTV!

其实现在看电视节目的需求确实大大降低了,折腾也只是为了单纯的让它实现,享受这个过程带来的快乐而已,哈哈!预期构想家里所有设备直接接入网络随时接收并播放IPTV直播(电信点播的节目不是太多,但好在非常稳...

第五节 Docker 入门实践:从 Hello World 到容器操作

一、Docker容器基础运行(一)单次命令执行通过dockerrun命令可以直接在容器中执行指定命令,这是体验Docker最快捷的方式:#在ubuntu:15.10容器中执行ech...

替代Docker build的Buildah简单介绍

Buildah是用于通过较低级别的coreutils接口构建OCI兼容镜像的工具。与Podman相似,Buildah不依赖于Docker或CRI-O之类的守护程序,并且不需要root特权。Builda...

Docker 命令大全(docker命令大全记录表)

容器生命周期管理run-创建并启动一个新的容器。start/stop/restart-这些命令主要用于启动、停止和重启容器。kill-立即终止一个或多个正在运行的容器rm-于删除一个或...

docker常用指令及安装rabbitMQ(docker安装rabbitmq配置环境)

一、docker常用指令启动docker:systemctlstartdocker停止docker:systemctlstopdocker重启docker:systemctlrestart...

使用Docker快速部署Storm环境(docker部署confluence)

Storm的部署虽然不是特别麻烦,但是在生产环境中,为了提高部署效率,方便管理维护,使用Docker来统一管理部署是一个不错的选择。下面是我开源的一个新的项目,一个配置好了storm与mono环境的D...

Docker Desktop安装使用指南:零基础教程

在之前的文章中,我多次提到使用Docker来安装各类软件,尤其是开源软件应用。鉴于不少读者对此有需求,我决定专门制作一期关于Docker安装与使用的详细教程。我主要以Macbook(Mac平台)为例进...

Linux如何成功地离线安装docker(linux离线安装httpd)

系统环境:Redhat7.2和Centos7.4实测成功近期因项目需要用docker,所以记录一些相关知识,由于生产环境是不能直接连接互联网,尝试在linux中离线安装docker。步骤1.下载...

Docker 类面试题(常见问题)(docker面试题目)

Docker常见问题汇总镜像相关1、如何批量清理临时镜像文件?可以使用sudodockerrmi$(sudodockerimages-q-fdanging=true)命令2、如何查看...

面试官:你知道Dubbo怎么优雅上下线的吗?你:优雅上下线是啥?

最近无论是校招还是社招,都进行的如火如荼,我也承担了很多的面试工作,在一次面试过程中,和候选人聊了一些关于Dubbo的知识。Dubbo是一个比较著名的RPC框架,很多人对于他的一些网络通信、通信协议、...

【Docker 新手入门指南】第五章:Hello Word

适合人群:完全零基础新手|学习目标:30分钟掌握Docker核心操作一、准备工作:先确认是否安装成功打开终端(Windows用户用PowerShell或GitBash),输入:docker--...

松勤软件测试:详解Docker,如何用portainer管理Docker容器

镜像管理搜索镜像dockersearch镜像名称拉取镜像dockerpullname[:tag]列出镜像dockerimages删除镜像dockerrmiimage名称或id删除...