域安全 | K8s调度策略

VSole2023-02-08 16:01:30

调度

    在K8s中,调度是指将Pod放置到合适的节点上。调度器通过 K8s 的监测机制来发现集群中新创建且尚未被调度到节点上的Pod。调度器会将所发现的每一个未调度的Pod调度到一个合适的节点上来运行。

kube-scheduler调度器

   kube-scheduler组件是K8s集群的默认调度器,并且是集群控制面的一部分。对每一个新创建的Pod或者是未被调度的 Pod,kube-scheduler 会选择一个最优的节点去运行这个Pod。然而,Pod内的每一个容器对资源都有不同的需求,而且Pod本身也有不同的需求。因此,Pod 在被调度到节点上之前,根据这些特定的调度需求,需要对集群中的节点进行一次过滤。在一个集群中满足一个 Pod调度请求的所有节点称之为可调度节点。如果没有任何一个节点能满足 Pod 的资源请求,那么这个 Pod 将一直停留在未调度状态直到调度器能够找到合适的Node节点。kube-scheduler调度器先在集群中找到一个Pod的所有可调度节点,然后根据一系列函数对这些可调度节点打分,选出其中得分最高的节点来运行 Pod。之后,调度器将这个调度决定通知给 kube-apiserver,这个过程叫做绑定。

    在做调度决定时需要考虑的因素包括:单独和整体的资源请求、硬件/软件/策略限制、 亲和以及反亲和要求、数据局部性、负载间的干扰等等。

kube-scheduler调度流程

   kube-scheduler 给一个 Pod 做调度选择时包含两个步骤:

  • 过滤
  • 打分

    过滤阶段会将所有满足Pod调度需求的节点选出来。PodFitsResources 过滤函数会检查候选节点的可用资源能否满足 Pod 的资源请求。在过滤之后,得出一个节点列表,里面包含了所有可调度节点;通常情况下, 这个节点列表包含不止一个节点。如果这个列表是空的,代表这个 Pod 不可调度。

    在打分阶段,调度器会为 Pod 从所有可调度节点中选取一个最合适的节点。根据当前启用的打分规则,调度器会给每一个可调度节点进行打分。最后,kube-scheduler 会将 Pod 调度到得分最高的节点上。如果存在多个得分最高的节点,kube-scheduler 会从中随机选取一个。

    支持以下两种方式配置调度器的过滤和打分行为:

  • 调度策略:允许你配置过滤所用的 断言(Predicates) 和打分所用的 优先级(Priorities)。
  • 调度配置:允许你配置实现不同调度阶段的插件, 包括:QueueSort、Filter、Score、Bind、Reserve、Permit 等等。你也可以配置 kube-scheduler 运行不同的配置文件。

将Pod分配给指定节点

    可以通过一些手段约束一个Pod以便限制其只能在特定的节点上运行,或优先在特定的节点上运行。有几种方法可以实现这点:

  • 节点标签
  • 亲和性与反亲和性
  • nodeName字段
  • Pod拓扑分布约束
  • 污点和容忍度

节点标签

    标签(Labels)是附加到 K8s 对象(比如 Pod)上的键值对。标签旨在用于指定对用户有意义且相关的对象的标识属性,但不直接对核心系统有语义含义。标签可以用于组织和选择对象的子集。标签可以在创建时附加到对象,随后可以随时添加和修改。每个对象都可以定义一组键/值标签。每个键对于给定对象必须是唯一的,如下:

"metadata": {  "labels": {    "key1" : "value1",    "key2" : "value2"  }}

    与很多其他 K8s 对象类似,节点也有标签(Labels),也可以手动给节点添加标签。K8s 也会为集群中所有节点添加一些标准的标签。

给节点添加标签

    执行如下命令给指定的k8s-node1节点添加标签 key1=value1。

#给指定的节点打标签kubectl label nodes k8s-node1 key1=value1#移除标签kubectl label nodes k8s-node1 key1-

创建一个将被调度到你选择的节点的 Pod

    如下Pod 配置文件描述了一个拥有节点选择器 key1: value1 的 Pod。这表明该 Pod 将被调度到有 key1: value1 标签的节点上。


apiVersion: v1kind: Podmetadata:  name: nginx  labels:    env: testspec:  containers:  - name: nginx    image: nginx    imagePullPolicy: IfNotPresent  nodeSelector:    key1: value1

    可以看到该pod确实创建在具有该标签的node节点上。

亲和性和反亲和性

   nodeSelector 提供了一种最简单的方法来将 Pod 约束到具有特定标签的节点上,而亲和性和反亲和性扩展了你可以定义的约束类型。使用亲和性与反亲和性的一些好处有如下:

  • 亲和性、反亲和性语言的表达能力更强。nodeSelector 只能选择拥有所有指定标签的节点,而亲和性、反亲和性为你提供对选择逻辑的更强控制能力。
  • 你可以标明某规则是“软需求”或者“偏好”,这样调度器在无法找到匹配节点时仍然调度该 Pod。
  • 你可以使用节点上(或其他拓扑域中)运行的其他 Pod 的标签来实施调度约束, 而不是只能使用节点本身的标签。这个能力让你能够定义规则允许哪些 Pod 可以被放置在一起。

亲和性功能由两种类型的亲和性组成:

  • 节点亲和性功能类似于 nodeSelector 字段,但它的表达能力更强,并且允许你指定软规则。
  • Pod 间亲和性、反亲和性允许你根据其他 Pod 的标签来约束 Pod。


节点亲和性

    节点亲和性概念上类似于 nodeSelector, 它使你可以根据节点上的标签来约束 Pod 可以调度到哪些节点上。节点亲和性有两种:

  • requiredDuringSchedulingIgnoredDuringExecution:调度器只有在规则被满足的时候才能执行调度。此功能类似于 nodeSelector, 但其语法表达能力更强。
  • preferredDuringSchedulingIgnoredDuringExecution:调度器会尝试寻找满足对应规则的节点。如果找不到匹配的节点,调度器仍然会调度该 Pod。

    注:在上述类型中,IgnoredDuringExecution 意味着如果节点标签在 Kubernetes 调度 Pod 后发生了变更,Pod 仍将继续运行。

Pod 间亲和性与反亲和性

   Pod间亲和性与反亲和性使你可以基于已经在节点上运行的Pod的标签来约束Pod可以调度到的节点,而不是基于节点上的标签。

   Pod间亲和性与反亲和性的规则格式为“如果 X 上已经运行了一个或多个满足规则 Y 的 Pod, 则这个 Pod 应该(或者在反亲和性的情况下不应该)运行在 X 上”。这里的 X 可以是节点、机架、云提供商可用区或地理区域或类似的拓扑域, Y 则是 K8s 尝试满足的规则。

    你可以通过标签选择算符的形式来表达规则(Y),并可根据需要指定关联的名字空间列表。Pod 在 K8s 中是名字空间作用域的对象,因此 Pod 的标签也隐式地具有名字空间属性。针对 Pod 标签的所有标签选择算符都要指定名字空间,K8s 会在指定的名字空间内寻找标签。

    你会通过 topologyKey 来表达拓扑域(X)的概念,其取值是系统用来标示域的节点标签键。

    注:Pod 间亲和性和反亲和性都需要相当的计算量,因此会在大规模集群中显著降低调度速度。我们不建议在包含数百个节点的集群中使用这类设置。Pod 反亲和性需要节点上存在一致性的标签。换言之, 集群中每个节点都必须拥有与 topologyKey 匹配的标签。如果某些或者所有节点上不存在所指定的 topologyKey 标签,调度行为可能与预期的不同。

用节点亲和性把Pod分配到节点

    执行如下命令给指定的k8s-node1节点添加标签 key1=value1。


#给指定的节点打标签kubectl label nodes k8s-node1 key1=value1

    下面清单描述了一个 Pod,它有一个节点亲和性配置 requiredDuringSchedulingIgnoredDuringExecution,key1=value1。这意味着 pod 只会被调度到具有 key1=value1 标签的节点上。


apiVersion: v1kind: Podmetadata:  name: nginx4spec:  affinity:    nodeAffinity:      requiredDuringSchedulingIgnoredDuringExecution:        nodeSelectorTerms:        - matchExpressions:          - key: key1            operator: In            values:            - value1              containers:  - name: nginx    image: nginx    imagePullPolicy: IfNotPresent

  本清单描述了一个Pod,它有一个节点亲和性设置 preferredDuringSchedulingIgnoredDuringExecution,key1=value1。这意味着 pod 将首选具有 key1=value1 标签的节点。如果没有找到key1=value1 标签的节点,则会随机调度到可用的Node节点上。


apiVersion: v1kind: Podmetadata:  name: nginx5spec:  affinity:    nodeAffinity:      preferredDuringSchedulingIgnoredDuringExecution:      - weight: 1        preference:          matchExpressions:          - key: key1            operator: In            values:            - value1           containers:  - name: nginx    image: nginx    imagePullPolicy: IfNotPresent

nodeName字段

    可以通过设置 nodeName 将某个 Pod 调度到特定的节点,且该 Pod 将只能被调度到特定节点。


apiVersion: v1kind: Podmetadata:  name: nginx2spec:  nodeName: k8s-node2  containers:  - name: nginx    image: nginx    imagePullPolicy: IfNotPresent

Pod 拓扑分布约束

    可以使用拓扑分布约束(Topology Spread Constraints)来控制Pod在集群内故障域之间的分布,故障域的示例有区域(Region)、可用区(Zone)、节点和其他用户自定义的拓扑域。这样做有助于提升性能、实现高可用或提升资源利用率。详细:Pod 拓扑分布约束: https://kubernetes.io/zh-cn/docs/concepts/scheduling-eviction/topology-spread-constraints/

污点(Taint)和容忍度(Toleration)

    节点亲和性是Pod的一种属性,它使Pod被吸引到一类特定的节点(这可能是出于一种偏好,也可能是硬性要求)。而污点(Taint)则相反,污点(Taint)是应用在Node节点之上的它使节点能够排斥一类特定的Pod,具有污点Taint的node和pod是互斥关系;而容忍度(Toleration)是应用于 Pod 上的,容忍度允许(但不要求)调度器调度带有对容忍度的Pod到Node节点上,污点和容忍度的目的是优化pod在集群间的调度。作为其功能的一部分, 调度器也会评估其他参数。

    污点和容忍度(Toleration)相互配合,可以用来避免 Pod 被分配到不合适的节点上。每个节点上都可以应用一个或多个污点,这表示对于那些不能容忍这些污点的 Pod, 是不会被该节点接受的。

给Node节点打污点

    给节点k8s-master增加一个污点,它的键名是key1,键值是value1,效果是NoSchedule。这表示只有拥有和这个污点相匹配的容忍度的 Pod 才有可能够被分配到k8s-node1这个节点上。


#查看节点信息kubectl describe node k8s-node1#添加污点kubectl taint nodes k8s-node1 key1=value1:NoSchedule#移除污点kubectl taint nodes k8s-node1 key1=value1:NoSchedule-

在pod中定义容忍度

    可以在 PodSpec 中定义 Pod 的容忍度。下面两个容忍度均与上面例子中使用kubectl taint命令创建的污点相匹配, 因此如果一个 Pod 拥有其中的任何一个容忍度都可能能够被分配到node1:


tolerations:- key: "key1"  operator: "Equal"  value: "value1"  effect: "NoSchedule"


tolerations:- key: "key1"  operator: "Exists"  effect: "NoSchedule"

实例


apiVersion: v1kind: Podmetadata:  name: myapp  labels:    env: testspec:  containers:  - name: nginx    image: nginx    imagePullPolicy: IfNotPresent  tolerations:  - key: "key1"    operator: "Exists"    effect: "NoSchedule"

    一个容忍度和一个污点相“匹配”是指它们有一样的键名和效果。

operator 的参数含义:

  • 如果 operator 是 Exists (此时容忍度不能指定 value)
  • 如果 operator 是 Equal ,则它们的 value 应该相等。
  • 如果 operator 不指定,则默认为Equal。

    注:如果一个容忍度的 key 为空且 operator 为 Exists, 表示这个容忍度与任意的 key、value 和 effect 都匹配,即这个容忍度能容忍任何污点。

effect 的参数含义:

  • 如果effect是NoSchedule,则新的不能容忍的pod不能再调度过来,但是之前运行在node节点中的Pod不受影响。
  • 如果effect是NoExecute,则新的不能容忍的pod不能调度过来,老的pod也会被驱逐。
  • 如果effect是PreferNoScheduler,则表示尽量不调度到污点节点中去。

注:如果 effect 为空,则可以与所有键名 key1 的效果相匹配。

多个污点的匹配规则

    可以给一个节点添加多个污点,也可以给一个Pod添加多个容忍度。K8s处理多个污点和容忍度的过程就像一个过滤器:从一个节点的所有污点开始遍历, 过滤掉那些 Pod 中存在与之相匹配的容忍度的污点。余下未被过滤的污点的effect 值决定了 Pod 是否会被分配到该节点,特别是以下情况:

  • 如果未被过滤的污点中存在至少一个effect值为NoSchedule的污点, 则 Kubernetes 不会将Pod分配到该节点。
  • 如果未被过滤的污点中不存在 effect 值为NoSchedule的污点, 但是存在 effect 值为PreferNoSchedule的污点, 则 Kubernetes 会尝试不将 Pod 分配到该节点。
  • 如果未被过滤的污点中存在至少一个 effect 值为NoExecute 的污点, 则 Kubernetes 不会将 Pod 分配到该节点(如果 Pod 还未在节点上运行), 或者将 Pod 从该节点驱逐(如果 Pod 已经在节点上运行)。

    例如,假设您给一个节点添加了如下污点:


kubectl taint nodes node1 key1=value1:NoSchedulekubectl taint nodes node1 key1=value1:NoExecutekubectl taint nodes node1 key2=value2:NoSchedule

假定有一个 Pod,它有两个容忍度:


tolerations:- key: "key1"  operator: "Equal"  value: "value1"  effect: "NoSchedule"- key: "key1"  operator: "Equal"  value: "value1"  effect: "NoExecute"

    上述 Pod 不会被分配到 node1 节点,因为其没有容忍度和第三个污点相匹配。 

    但是如果在给节点添加上述污点之前,该 Pod 已经在上述节点运行, 那么它还可以继续运行在该节点上,因为第三个污点是三个污点中唯一不能被这个 Pod 容忍的。

    通常情况下,如果给一个节点添加了一个 effect 值为 NoExecute 的污点, 则任何不能忍受这个污点的 Pod 都会马上被驱逐,任何可以忍受这个污点的 Pod 都不会被驱逐。但是,如果 Pod 存在一个 effect 值为 NoExecute 的容忍度指定了可选属性tolerationSeconds 的值,则表示在给节点添加了上述污点之后, Pod 还能继续在节点上运行的时间。例如:


tolerations:- key: "key1"  operator: "Equal"  value: "value1"  effect: "NoExecute"  tolerationSeconds: 3600

    这表示如果这个 Pod 正在运行,同时一个匹配的污点被添加到其所在的节点, 那么 Pod 还将继续在节点上运行 3600 秒,然后被驱逐。如果在此之前上述污点被删除了,则 Pod 不会被驱逐。

说明

  • 默认情况下,只有master节点会有污点(role.kubernetes.io/master:NoSchedule),Node节点默认没有污点。这也是为什么默认情况下Pod不会被分配到master节点的原因。
  • 当只有一个节点没污点,其他节点都有污点时,创建pod的yml文件的容忍度没匹配上所有的污点时,创建的pod节点在没有污点的节点上。
  • 当只有一个节点没污点,其他节点都有污点时,创建pod的yml文件的容忍度匹配上污点时,创建的pod节点也在没有污点的节点上。
  • 当所有node的节点都有污点,创建pod的yml文件的容忍度能匹配上污点时,创建的pod节点在匹配的污点的节点上。
  • 当所有node节点都有污点,但是创建pod的yml文件的容忍度没匹配上所有的污点时,创建的pod节点的NODE这里为空。

3

给master节点分配pod

    默认情况下创建的pod是不会被分配到master节点上的,因为master节点默认被打上了node-role.kubernetes.io/master:NoSchedule的污点。


#查询k8s-master节点的Taintskubectl describe node k8s-master | grep Taints#取消污点kubectl taint nodes k8s-master role.kubernetes.io/master:NoSchedule-

注:其实也可以省略取消master节点污点这一步。

然后使用nodeName字段指定k8s-master


apiVersion: v1kind: Podmetadata:  name: nginxspec:  nodeName: k8s-master  containers:  - name: nginx    image: nginx    imagePullPolicy: IfNotPresent

k8spod
本作品采用《CC 协议》,转载必须注明作者和本文链接
K8s 的API Server未授权命令执行
本文将引入一个思路:“在 Kubernetes 集群发生网络异常时如何排查”。文章将引入 Kubernetes 集群中网络排查的思路,包含网络异常模型,常用工具,并且提出一些案例以供学习。其可能原因为Pod 的 DNS 配置不正确DNS 服务异常pod 与 DNS 服务通讯异常大数据包丢包:主要现象为基础网络和端口均可以连通,小数据包收发无异常,大数据包丢包。
攻击者进入Pod后,通过未授权访问或漏洞攻击第三方组件,并利用这些组件的高权限账户操纵K8s集群。反入侵对于底层资产的监控趋于复杂的同时,UEBA技术将在业务层发挥更重要的作用。事实上身份认证、鉴权、UEBA以及数据安全是一个整体设计,这一点零信任架构的已经给出了实践。
恰巧近期有一枚和容器逃逸相关的漏洞:CVE-2022-0492 Linux内核权限提升漏洞可导致容器意外逃逸。
最近这log4j热度很高。好久没写文章了,而且目前市面有些文章里面的内容信息已经有些过时缺少最新信息迭代,借此机会我剑指系列基于国内外的关于此漏洞的研究我进行了总结和归纳,并且将我自己目前发现的小众的技巧方法分享给各位,希望能给各位带来帮助不会让各位失望。
K8s组件和架构
2022-12-29 16:51:34
K8s常见组件和架构
K8s 安全策略最佳实践
2022-04-02 16:42:33
随着K8s在生产和测试环境中用的越来越多,对安全性的关注也会越来越多~
云环境网络流量采集和分析解决方案
从云的虚拟化管理平台和云网络构架的一般性知识入手,以 Clos 云网络架构和 Kubernetes管理平台为例,俯瞰了当前云计算环境的全貌和细节,宏观上总览了云网络架构和 Kubernetes 管理平台,微观上深入连接 fabrics 和容器的细节。
随着数字经济时代到来,云计算、大数据、物联网等新兴技术在关键信息基础设施领域深度应用,数字技术已经成为企业转型和发展的关键要素,而云是企业数字化转型的基础支柱,也是企业的首要技术重点
VSole
网络安全专家