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
注意:
- 如果同时定义了nodeSelector和nodeAffinity,那么必须两个条件都要得到满足,Pod才能运行到指定节点
- 如果nodeAffinity指定了多个nodeSelectorTerms,那么只要其中一个匹配即可
- 如果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 - 最佳实践的总结
- 所有涉及的标签应该包含一个前缀,例如:a-demo-web.site/host-type;
- 所有涉及的标签应该来自一个权威数据中心,而非各集群管理员私下随意定义;
- 一般情况下不使用nodeName、nodeSelector进行Pod调度,一律通过nodeAffinity代替;
- 保障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删除...
- 一周热门
- 最近发表
-
- Docker篇(二):Docker实战,命令解析
- docker学习笔记——安装和基本操作
- 不可错过的Docker完整笔记(dockerhib)
- 扔掉运营商的 IPTV 机顶盒,全屋全设备畅看 IPTV!
- 第五节 Docker 入门实践:从 Hello World 到容器操作
- 替代Docker build的Buildah简单介绍
- Docker 命令大全(docker命令大全记录表)
- docker常用指令及安装rabbitMQ(docker安装rabbitmq配置环境)
- 使用Docker快速部署Storm环境(docker部署confluence)
- Docker Desktop安装使用指南:零基础教程
- 标签列表
-
- mybatiscollection (79)
- mqtt服务器 (88)
- keyerror (78)
- c#map (65)
- resize函数 (64)
- xftp6 (83)
- bt搜索 (75)
- c#var (76)
- mybatis大于等于 (64)
- xcode-select (66)
- mysql授权 (74)
- 下载测试 (70)
- linuxlink (65)
- pythonwget (67)
- androidinclude (65)
- logstashinput (65)
- hadoop端口 (65)
- vue阻止冒泡 (67)
- oracle时间戳转换日期 (64)
- jquery跨域 (68)
- php写入文件 (73)
- kafkatools (66)
- mysql导出数据库 (66)
- jquery鼠标移入移出 (71)
- 取小数点后两位的函数 (73)