浅入Kubernetes(10):控制节点的部署,选择器、亲和性、污点

简介: 在前面的学习中,我们学到了 Deployment 部署,以及副本数(ReplicaSet),但是 Pod 部署到哪个 Worker 节点是随机,即使有 3个 Woker 和设定 3个 副本,不一定每个 Node 刚刚好运行一个 Pod,也可能其中 Node 运行着三个副本。在本篇我们将探究 Kubernetes 中的 DaemonSet、容忍度、亲和性、Label、选择器等概念,以便控制 pod 的部署。

标签和nodeSelector


标签(Label)是附加到 Kubernetes 对象上的键值对,如果用 json 表示附加到 metadata 的 label:


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


yaml:

metadata:
  labels:
    key1: "value1"
    key2: "value2"


标签主要是用于表示对用户有意义的对象的属性标识。

可以给节点设定一些 Label,例如在 kube-system 命名空间中,运行着 Kubernetes 的核心组件,我们可以查看此命名空间中所有组件的 Label。


kubectl get nodes --namespace=kube-system --show-labels


beta.kubernetes.io/arch=amd64,
beta.kubernetes.io/os=linux,
kubernetes.io/arch=amd64,
... ...


我们也可以手动给一个 Node 添加标签。

kubectl label nodes <node-name> <label-key>=<label-value>


例如我们给节点设置一个 disksize,表示节点的硬盘是否够大。

kubectl label nginx disksize=big


然后我们在编写 yaml 文件时,希望这个 pod 在容量大的 Node 上运行,可以这样写:

nodeSelector:
    disksize=big


顺便聊一下官方的一个例子,设置 Node 的 Label,表示硬盘是 ssd。

kubectl label nodes kubernetes-foo-node-1.c.a-robinson.internal disktype=ssd


在 yaml 文件的节点选择器中,添加选择。

spec:
  containers:
  - name: nginx
    image: nginx
    imagePullPolicy: IfNotPresent
  nodeSelector:
    disktype: ssd


Label 可以在多个地方使用,例如在 Node 上添加 Label,标识此 Node;而在 NodeSelector 里使用,可以选择合适的 Node 运行 Pod;在 metadata 中使用,可以对元数据加以描述。


在 metadata 中添加的 Label,可以在命令查询时做筛选。


查询 pod 的 Label:

kubectl get pods --show-labels


查找符合条件的 pod(参考 LABELS 字段,可以根据里面的标签选择):

kubectl get pods -l app=nginx


标签选择


在前面,我们学习了 nodeSelector ,可以帮助我们选择合适的 Node 运行 Pod,实际上 Kubernets 的标签选择是丰富多样的,例如:

nodeSelector:
    disktype: ssd
    disksize: big


则表示节点选择器是等值选择,表达式是 disktype=ssd && disksize=big

标签选择有等值和集合两种,其中等值选择有 ===!= 三种,=== 无区别。


在多个需求(多个label)的情况下,相对于使用 && 运算符,但是选择器不存在 || 这种逻辑或运算符。


yaml 只支持 {key}:{value} 这种形式,而我们使用命令形式时,则可使用以上三种运算符。

kubectl get nodes -l disktype=ssd,disksize!=big
# 多个条件使用 逗号","" 隔开,而不是 "&&"。


对于集合选择方式,支持三种操作符:innotinexists。不过别理解成是从集合中选择,下面举个例子。


假如有三个 Node,其 disksize 有 big、medium、small,我们要部署一个 pod,在 big、medium 中都可以运行,则:

... -l disksize in (big,medium)


... -l disksize notin (small)
# 不在 small 中运行


而 exists 则跟 != 类似,但是 exists 表示只要存在这个 label 即可,而不论其设置了是什么值。

-l disksize
# 等同 -l disksize in (big,medium,small)


我们也可以使用 '' 把选择表达式包起来。

kubectl get pods -l 'app=nginx'


前面已经提到了 yaml 的 nodeSelector 和 命令式的选择,这里我们介绍 yaml 的 selector。


前面我们提到在 Deployment 的 metadata 中加上 Label,即 pod 加上 Label,我们也在 kubectl get pods 中使用 Label 选择过滤 pod。同样,当我们创建 Service 或者使用 ReplicationController 时,也可以使用标签选择合适的 pod。


假如我们已经部署了 nginx,那么查询 kubectl get pods --show-labels 时,其 pod 的 LABELS 会有 app=nginx,那么我们可以这样选择:

selector:
    app: nginx


完整版本:

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  type: LoadBalancer
  selector:
    app: nginx
  ports:
    - protocol: TCP
      port: 80
      targetPort: 6666
status:
  loadBalancer:
    ingress:
      - ip: 192.0.2.127


selector 还支持以下选择方式 matchLabelsmatchExpressions

matchLabels 是由 {key,value} 对组成的映射。 matchLabels 映射中的单个 {key,value } 等同于 matchExpressions 的元素, 其 key 字段为 "key",operator 为 "In",而 values 数组仅包含 "value"。


matchExpressions 是 Pod 选择算符需求的列表。 有效的运算符包括 InNotInExistsDoesNotExist。 在 InNotIn 的情况下,设置的值必须是非空的。 来自 matchLabelsmatchExpressions 的所有要求都按逻辑与的关系组合到一起 -- 它们必须都满足才能匹配。


示例如下:

selector:
  matchLabels:
    component: redis
  matchExpressions:
    - {key: tier, operator: In, values: [cache]}
    - {key: environment, operator: NotIn, values: [dev]}


这里就不在详细说这些选择规则了,前面提到的已经够用了,读者可以查阅官方文档学习更多复杂的操作:https://kubernetes.io/zh/docs/concepts/overview/working-with-objects/labels/


亲和性和反亲和性


前面我们学习了 nodeSelector ,使用 nodeSelector 选择合适的 Label,可以表达我们约束的类型。


亲和性则类似于 nodeSelector,可以根据节点上的标签约束 pod 可以调度到哪些节点。

pod 亲和性有两种别为:

  • requiredDuringSchedulingIgnoredDuringExecution
    硬需求,将 pod 调度到一个节点必须满足的规则。
  • preferredDuringSchedulingIgnoredDuringExecution
    尝试执行但是不能保证偏好。


这是官方的一个例子:

apiVersion: v1
kind: Pod
metadata:
  name: with-node-affinity
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: kubernetes.io/e2e-az-name
            operator: In
            values:
            - e2e-az1
            - e2e-az2
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 1
        preference:
          matchExpressions:
          - key: another-node-label-key
            operator: In
            values:
            - another-node-label-value
  containers:
  - name: with-node-affinity
    image: k8s.gcr.io/pause:2.0


亲和性的约束相对于:

... ... -l kubernetes.io/e2e-az-name in (e2e-az1,e2e-az2)


affinity 设置亲密关系,nodeAffinity 设置节点亲密关系,最后才到 亲和性,它们表示必须满足和尽量满足。


如果我们设置了多个 nodeSelectorTerms :

requiredDuringSchedulingIgnoredDuringExecution:
  nodeSelectorTerms:
  ...
  nodeSelectorTerms:


则只需要满足其中一种即可调度 pod 到 node 上。


如果你同时指定了 nodeSelectornodeAffinity两者必须都要满足, 才能将 Pod 调度到候选节点上。


节点亲和性语法支持下面的操作符: InNotInExistsDoesNotExistGtLt


Pod 亲和性与反亲和性的合法操作符有 InNotInExistsDoesNotExist

通过 -Affinity 可以设置亲和性,例如节点亲和性 nodeAffinity,而且设置反亲和性使用 -AntiAffinity,例如 nodeAntiAffinity


反亲和性跟亲和性一样,都有 requiredDuringSchedulingIgnoredDuringExecution 硬限制和 preferredDuringSchedulingIgnoredDuringExecution 软限制,只是反亲和性是相反的表示,如果符合条件则不能调度。


关于亲和性和反亲和性的说明就到这里,着两者的配置比较多和复杂,读者可以参考官方文档,这里不在赘述。


污点和容忍度


前面提到亲和性和反亲和性,我们加以通过 pod 选择合适的 node,或者 service 选择合适的 pod,这些拥有 Label 的对象都是被选择的。


这里,我们介绍污点和容忍度,它们可以排斥 “被选择” 的命运。

节点污点(taint) 可以排斥一类特定的 pod,而 容忍度(Tolerations)则表示能够容忍这个对象的污点。


当节点添加一个污点后,除非 pod 声明能够容忍这个污点,否则 pod 不会被调度到这个 节点上。


系统会 尽量 避免将 Pod 调度到存在其不能容忍污点的节点上, 但这不是强制的。Kubernetes 处理多个污点和容忍度的过程就像一个过滤器:从一个节点的所有污点开始遍历, 过滤掉那些 Pod 中存在与之相匹配的容忍度的污点。


但是如果你只有一个 worker,那么设置了污点,那 pod 也只能选择在这个节点上运行。

添加污点格式:

kubectl taint node [node] key=value:[effect]


更新污点或覆盖:

kubectl taint node [node] key=value:[effect] --overwrite=true


使用 kubectl taint 给节点增加一个污点。

kubectl taint nodes node1 key1=value1:NoSchedule


移除污点:

kubectl taint nodes node1 key1=value1:NoSchedule-


其中,污点需要设置 label ,并设置这个 label 的效果为 NoSchedule。


污点的效果称为 effect ,节点的污点可以设置为以下三种效果:

  • NoSchedule:不能容忍此污点的 Pod 不会被调度到节点上;不会影响已存在的 pod。
  • PreferNoSchedule:Kubernetes 会避免将不能容忍此污点的 Pod 安排到节点上。
  • NoExecute:如果 Pod 已在节点上运行,则会将该 Pod 从节点中逐出;如果尚未在节点上运行,则不会将其安排到节点上。


但是某些系统创建的 Pod 可以容忍所有 NoExecuteNoSchedule 污点,因此不会被逐出,例如 master 节点是不能被部署 pod 的,但是 kube-system 命名空间却有很多系统 pod。当然通过修改污点,可以让 户 pod 部署到 master 节点中。


查询节点的污点:

kubectl describe nodes | grep Taints


Taints:             node-role.kubernetes.io/master:NoSchedule
Taints:             key1=value1:NoSchedule


系统默认污点


我们去除 master 的污点:

kubectl taint node instance-1 node-role.kubernetes.io/master:NoSchedule-


然后部署 nginx pod。

kubectl create deployment nginxtaint --image=nginx:latest --replicas=3


查看 pod:

kubectl get pods -o wide


结果笔者查到三个副本都在 master 节点上。

为了保证集群安全,我们需要恢复 master 的污点。

kubectl taint node instance-1 node-role.kubernetes.io/master:NoSchedule


当某种条件为真时,节点控制器会自动给节点添加一个污点。当前内置的污点包括:

  • node.kubernetes.io/not-ready:节点未准备好。这相当于节点状态 Ready 的值为 "False"。
  • node.kubernetes.io/unreachable:节点控制器访问不到节点. 这相当于节点状态 Ready 的值为 "Unknown"。
  • node.kubernetes.io/out-of-disk:节点磁盘耗尽。
  • node.kubernetes.io/memory-pressure:节点存在内存压力。
  • node.kubernetes.io/disk-pressure:节点存在磁盘压力。
  • node.kubernetes.io/network-unavailable:节点网络不可用。
  • node.kubernetes.io/unschedulable: 节点不可调度。
  • node.cloudprovider.kubernetes.io/uninitialized:如果 kubelet 启动时指定了一个 "外部" 云平台驱动, 它将给当前节点添加一个污点将其标志为不可用。在 cloud-controller-manager 的一个控制器初始化这个节点后,kubelet 将删除这个污点。


容忍度


一个 node 可以设置污点,排斥 pod,但是 pod 也可以设置 容忍度,容忍 node 的污点。

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


也可以设置 value。

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


operator 的默认值是 Equal

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

  • 如果 operatorExists
    此时容忍度不能指定 value,如果存在 key 为 key1 的 label,且污点效果为 NoSchedule,则容忍。
  • 如果 operatorEqual ,则它们的 value 应该相等


如果 effect 留空,则表示只要是 label 为 key1 的节点,都可以容忍。


如果:

tolerations:
  operator: "Exists"


则表示此 pod 能够容忍任意的污点,无论 node 怎么设置 keyvalueeffect ,此 pod 都不会介意。


如果要在 master 上也能部署 pod,则可以修改 pod 的容忍度:

spec:
      tolerations:
      # this toleration is to have the daemonset runnable on master nodes
      # remove it if your masters can't run pods
      - key: node-role.kubernetes.io/master
        effect: NoSchedule


DaemonSet


在 Kubernetes 中,有三个 -Set ,分别是 ReplicaSet、DaemonSet、StatefulSets。而 负载类型有 Deployments、ReplicaSet、DaemonSet、StatefulSets等(或者说有这几个控制器)。


前面已经介绍过 Deployments ,而 kind: ReplicaSet 一般是没必要的,可以在 kind: Deployment 加上 replicas:


kind: DaemonSet 需要使用一个 yaml 来描述,但是整体跟 Deployment 一样。

DaemonSet 可以确保一个节点只运行一个 Pod 副本,假如有个 nginx 的 pod,当新的 Node 加入集群时,会自动在这个 Node 上部署一个 pod;当节点从集群中移开时,这个 Node 上的 Pod 会被回收;如果 DaemontSet 配置被删除,则也会删除所有由它创建的 Pod。


DaemonSet 的一些典型用法:

  • 在每个节点上运行集群守护进程
  • 在每个节点上运行日志收集守护进程
  • 在每个节点上运行监控守护进程


在 yaml 中,要配置 Daemont,可以使用 tolerations,配置示例:

kind: DaemontSet
... ...


其它地方跟 Deployment 一致。

相关实践学习
深入解析Docker容器化技术
Docker是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的Linux机器上,也可以实现虚拟化,容器是完全使用沙箱机制,相互之间不会有任何接口。Docker是世界领先的软件容器平台。开发人员利用Docker可以消除协作编码时“在我的机器上可正常工作”的问题。运维人员利用Docker可以在隔离容器中并行运行和管理应用,获得更好的计算密度。企业利用Docker可以构建敏捷的软件交付管道,以更快的速度、更高的安全性和可靠的信誉为Linux和Windows Server应用发布新功能。 在本套课程中,我们将全面的讲解Docker技术栈,从环境安装到容器、镜像操作以及生产环境如何部署开发的微服务应用。本课程由黑马程序员提供。 &nbsp; &nbsp; 相关的阿里云产品:容器服务 ACK 容器服务 Kubernetes 版(简称 ACK)提供高性能可伸缩的容器应用管理能力,支持企业级容器化应用的全生命周期管理。整合阿里云虚拟化、存储、网络和安全能力,打造云端最佳容器化应用运行环境。 了解产品详情: https://www.aliyun.com/product/kubernetes
相关文章
|
7月前
|
存储 Kubernetes 对象存储
部署DeepSeek但GPU不足,ACK One注册集群助力解决IDC GPU资源不足
借助阿里云ACK One注册集群,充分利用阿里云强大ACS GPU算力,实现DeepSeek推理模型高效部署。
|
6月前
|
存储 Kubernetes 开发工具
使用ArgoCD管理Kubernetes部署指南
ArgoCD 是一款基于 Kubernetes 的声明式 GitOps 持续交付工具,通过自动同步 Git 存储库中的配置与 Kubernetes 集群状态,确保一致性与可靠性。它支持实时同步、声明式设置、自动修复和丰富的用户界面,极大简化了复杂应用的部署管理。结合 Helm Charts,ArgoCD 提供模块化、可重用的部署流程,显著减少人工开销和配置错误。对于云原生企业,ArgoCD 能优化部署策略,提升效率与安全性,是实现自动化与一致性的理想选择。
293 0
|
5月前
|
存储 Kubernetes 异构计算
Qwen3 大模型在阿里云容器服务上的极简部署教程
通义千问 Qwen3 是 Qwen 系列最新推出的首个混合推理模型,其在代码、数学、通用能力等基准测试中,与 DeepSeek-R1、o1、o3-mini、Grok-3 和 Gemini-2.5-Pro 等顶级模型相比,表现出极具竞争力的结果。
|
6月前
|
存储 Kubernetes 监控
K8s集群实战:使用kubeadm和kuboard部署Kubernetes集群
总之,使用kubeadm和kuboard部署K8s集群就像回归童年一样,简单又有趣。不要忘记,技术是为人服务的,用K8s集群操控云端资源,我们不过是想在复杂的世界找寻简单。尽管部署过程可能遇到困难,但朝着简化复杂的目标,我们就能找到意义和乐趣。希望你也能利用这些工具,找到你的乐趣,满足你的需求。
590 33
|
6月前
|
Kubernetes 开发者 Docker
集群部署:使用Rancher部署Kubernetes集群。
以上就是使用 Rancher 部署 Kubernetes 集群的流程。使用 Rancher 和 Kubernetes,开发者可以受益于灵活性和可扩展性,允许他们在多种环境中运行多种应用,同时利用自动化工具使工作负载更加高效。
337 19
|
6月前
|
Kubernetes API 网络安全
当node节点kubectl 命令无法连接到 Kubernetes API 服务器
当Node节点上的 `kubectl`无法连接到Kubernetes API服务器时,可以通过以上步骤逐步排查和解决问题。首先确保网络连接正常,验证 `kubeconfig`文件配置正确,检查API服务器和Node节点的状态,最后排除防火墙或网络策略的干扰,并通过重启服务恢复正常连接。通过这些措施,可以有效解决与Kubernetes API服务器通信的常见问题,从而保障集群的正常运行。
398 17
|
6月前
|
存储 测试技术 对象存储
使用容器服务ACK快速部署QwQ-32B模型并实现推理智能路由
阿里云最新发布的QwQ-32B模型,通过强化学习大幅度提升了模型推理能力。QwQ-32B模型拥有320亿参数,其性能可以与DeepSeek-R1 671B媲美。
|
6月前
|
Kubernetes Shell Windows
【Azure K8S | AKS】在AKS的节点中抓取目标POD的网络包方法分享
在AKS中遇到复杂网络问题时,可通过以下步骤进入特定POD抓取网络包进行分析:1. 使用`kubectl get pods`确认Pod所在Node;2. 通过`kubectl node-shell`登录Node;3. 使用`crictl ps`找到Pod的Container ID;4. 获取PID并使用`nsenter`进入Pod的网络空间;5. 在`/var/tmp`目录下使用`tcpdump`抓包。完成后按Ctrl+C停止抓包。
212 12
|
7月前
|
存储 Kubernetes 测试技术
企业级LLM推理部署新范式:基于ACK的DeepSeek蒸馏模型生产环境落地指南
企业级LLM推理部署新范式:基于ACK的DeepSeek蒸馏模型生产环境落地指南
286 12
|
7月前
|
人工智能 Kubernetes 异构计算
大道至简-基于ACK的Deepseek满血版分布式推理部署实战
大道至简-基于ACK的Deepseek满血版分布式推理部署实战
372 5

推荐镜像

更多