master节点重置后添加node报错_Kubernetes自动化运维|Pod调度实践,节点亲和性与数据结构解析

65e2adafb792d04da043d02c4a6ba858.png

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

1 - 实验的环境

集群环境:

fdea142f61150357cfaa1fe853cf2b5b.png

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

命名空间:

a496f54b256220d4af9bd330fa8be145.png

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

Pod部署配置:

apiVersion: v1kind: Podmetadata:  name: counter  namespace: default  labels:    app: counter    release: 0.1.0spec:  # 根据各实践案例,植入的调度策略配置  # ${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
fd01c09b283482f0fc85717a3de5f0ee.png
  • 运行部署配置与查询结果
9f036902b665f41a5e8dcba5c051ce2c.png

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

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

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,观察状态
1b807d258bd4557030201275560cb181.png

持续处于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
b8cb01747a81bb40bbcf1224ce775bfa.png
  • 通过添加标签后,另一个窗口实时监听Pod运行状态(Pod能否获得调度?)
2435b9a58e32baff2fe26750e9879c61.png

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

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

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

3375947ee0ae1dbc6e847c88b9b051bb.png

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

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

3.4 - 内置标签

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

c1623f36299910a1c1cf297cf7e9cf07.png

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"

自定义标签说明:

73970930290684308699de4ff240a3f5.png

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 stringconst (    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 开头标签的节点
5430200304c074f9ad8e1a45fd023606.png
  • 打开另外一个终端,开启实时监听默认命名空间下Pod的状态
3480c0aebbf2ce269255eccafbf2370c.png
  • 添加调度策略,部署应用,此时观察到Pod持续处于Pending状态
d00c48ca4cc3276cf2c3f1196f81dc1e.png
370b776212abc5b68556d84791fd05fc.png
  • 为节点225添加标签 a-demo-web.site/host-type=vmware
6d232b1bd06dc924d0af4a904a91c34f.png

此时等待一会...

41a69d3843d7d4809f3e24a0125d5c06.png

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

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

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

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

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

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

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

  • 同样,移除节点的标签,并不会导致原先已调度的Pod
45b2068e68815f5e453d5f0e6af1dca9.png
  • 删除该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
e5e73c9af13ba1fcc1e984d9f6f4b689.png
  • 查看创建的Pod并实时监听状态的变化
d9bb6f102cec31b3d68c20a3dd78e68b.png

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

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

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

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

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

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

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

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

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

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

此时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中规则的简单,尽量使用“软限制"方式;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值