Kubernetes:标签选择器注解容忍度亲和性

Posted dotNET跨平台

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Kubernetes:标签选择器注解容忍度亲和性相关的知识,希望对你有一定的参考价值。

在前面的学习中,我们学到了 Deployment 部署,以及副本数(ReplicaSet),但是 Pod 部署到哪个 Worker 节点是随机的,即使有 3个 Woker 和 3个 Pod 副本,不一定每个 Node 刚刚好运行一个 Pod,也可能其中一个 Node 运行着三个副本。当然使用 Daemont 可以保证每个 Node 平均运行这种一个 Pod 副本。但是有的机器内存大,我想尽量将 Redis Pod 调度到此节点;而有的服务器磁盘容量大,我想尽可能将日志服务等调度到这些节点,那么该如何处理呢?为什么 master 不会部署我们创建的 Pod 呢?

在本篇我们将探究 Kubernetes 中的 DaemonSet、容忍度、亲和性、Label、选择器等概念,以便控制 Pod 的调度。

本文为作者的 Kubernetes 系列电子书的一部分,电子书已经开源,欢迎关注,电子书浏览地址:

https://k8s.whuanle.cn【适合国内访问】

https://ek8s.whuanle.cn 【gitbook】

标签和选择器

标签

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

标签(Label)是附加到 Kubernetes 对象上的键值对,在对象描述信息中以 Labels 字段保存,例如 Pod 可通过 kubectl describe pods 查询,可以看到每个 Pod 都带有 Labels: app=...

... ...
Labels:       app=nginx
              pod-template-hash=85b45874d9
... ...

[Info] 提示

每个 Pod 都有 app 标签,表示 Pod 模板的名称,app 标签的值跟 Pod 模板名称一致。

当我们创建 Deployment 时,如果没有指定 Pod 的 app 标签值,那么一般跟 Deployment 名称一致。如 kubectl create deployment nginx --image=nginx:latest 创建的 Pod,其 app 标签值就是 nginx。

Deployment 、ReplicaSet 等都是通过 Pod 的 app 标签识别 Pod 是不是属于1自己管控的。

在 kubectl get 后面加上 --show-labels 可以快速筛选获得对象的 Label :

kubectl get pods --show-labels
NAME                     READY   STATUS    RESTARTS   AGE     LABELS
nginx-55495b586f-4hlzm   1/1     Running   0          4h16m   app=nginx,pod-template-hash=55495b586f
nginx-55495b586f-5m985   1/1     Running   0          4h16m   app=nginx,pod-template-hash=55495b586f
nginx-55495b586f-cs4zh   1/1     Running   0          4h16m   app=nginx,pod-template-hash=55495b586f
... ...

在对象的 YAML 文件中,metadata.label 存储了这个对象的标签元数据。

如果用 JSON 表示附加到 metadata 的 label:

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

如果用 YAML 表示附加到 metadata 的 label

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

使用 kubectl get pod 名称 -o yaml 查询,或者使用 kubectl edit pod 名称 查看修改 Pod 的 标签。

在 Deployment 中,selector 字段设置了如何查找属于此对象的 Pod。

selector:
    matchLabels:
      app: nginx

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

设置 Label

我们也可以手动给一个 Node 或者别的对象 添加标签。

kubectl label nodes <node-name> <label-key>=<label-value>
kubectl label pod pod名称 <node-name> <label-key>=<label-value>

例如我们给一个 Pod 设置一个 disksize,表示 Pod 需要很大的硬盘。

kubectl label pod nginx-55495b586f-4hlzm disksize=big

如果要删除一个标签,只需要在标签名字后面加上 - 。

kubectl label pod nginx-55495b586f-4hlzm disksize-

如果标签已经存在,需要覆盖:

kubectl label pod nginx-55495b586f-4hlzm disksize=big --overwrite

在 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,
... ...

节点选择器

假如有个 Pod 需要很大的存储空间,而集群中的不同机器的硬盘容量都不同,我们希望 Pod 在硬盘很大的服务器上部署,我们可以通过给节点设置标签,然后在 Pod 的 YAML 文件中加上选择器,让 Pod 自动查找这类容量大的节点,并部署。

获取集群中的所有节点的 Label:

kubectl get nodes --show-labels
root@master:~# kubectl get nodes --show-labels
NAME     STATUS   ROLES                  AGE   VERSION   LABELS
master   Ready    control-plane,master   47h   v1.22.2   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=master,kubernetes.io/os=linux,node-role.kubernetes.io/control-plane=,node-role.kubernetes.io/master=,node.kubernetes.io/exclude-from-external-load-balancers=
slave1   Ready    <none>                 47h   v1.22.2   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=slave1,kubernetes.io/os=linux

我们的节点名称不一样,读者按照实际名称修改后面的命令。

给节点加上标签:

kubectl label node instance-2 disksize=big

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

nodeSelector:
    disksize=big

下面我们来练习一下,先删除以前的 Deployment:kubectl delete deployment nginx

然后我们使用 YAML 创建一个 Deployment,保存下面 YAML 到 nginx.yaml 文件:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
spec:
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - image: nginx:latest
        name: nginx
      nodeSelector:
        disksize: "big"

执行 kubectl apply -f nginx.yaml ,部署 Pod。

上面的例子由于是 Deployment 的,selector 和别的字段是必填的,所以看起来内容比较多,但是这里核心是 nodeSelector

我们需要使用节点选择器时,在 spec 下面增加 nodeSelector,其位置如下 YAML 所示:

spec:
      containers:
      - image: nginx:latest
        name: nginx
      nodeSelector:
        disksize: "big"

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

kubectl label nodes node-1 disktype=ssd

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

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

使用 Pod 选择节点:

apiVersion: v1
kind: Pod
metadata:
  name: nginx
  labels:
    env: test
spec:
  containers:
  - name: nginx
    image: nginx
    imagePullPolicy: IfNotPresent
  nodeSelector:
    disktype: ssd

命令式标签选择

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

nodeSelector:
    disktype: ssd
    disksize: big

上面的例子表示节点选择器是等值选择,表达式是 disktype=ssd && disksize=big,节点需要具备这两个 Label (key和value都要符合)。

我们也可以使用类似方式筛选符合条件的 Pod。

查询 Pod 所有的 Label:

kubectl get pods --show-labels

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

kubectl get pods -l app=nginx
kubectl get pods -l app!=nginx

列出不包含某个标签的 Pod:

kubectl get pods -l '!env'
kubectl get pods -l '!app'

获取同时包含两个标签的 Pod:

kubectl get pods -l app,env

标签选择有等值和集合两种,其中等值选择有 ===!= 三种符号,= 和 == 无任何区别,所以实际只有 等于 、不等于 两种选择情况。

在多个需求(多个label)的情况下,可以使用 && 运算符,表示需要同时符合多个条件,但是选择器不存在 || 这种逻辑或运算符。

查看符合两个条件的 节点:

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

注意,多条件使用 , 隔开,而不是 &&,不能写成 disktype=ssd && disksize!=big

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

假如有三个 Node,其 disksize 有 big、medium、small 三种类型。

NAME        LABELS
node1      disksize=big
node2      disksize=medium
node3      disksize=small

我们要部署一个 pod,在 big、medium 中都可以运行,则可以写成

kubectl get nodes -l disksize in (big,medium)
# ... -l disksize in (big,medium)
kubectl get nodes -l disksize notin (small)
# ... -l disksize notin (small)
# 不在 small 中运行

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

kubectl get nodes -l exists disksize
# -l exists disksize
# 等同 -l disksize in (big,medium,small)

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

kubectl get pods -l 'app=nginx'

目前提到的标签选择,主要是命令式选择,比较容易使用,而且支持的选择方式比较多;但是写进 YAML 或 JSON 中的选择器,则编写比较复杂,而且不支持所有的运算符。

selector

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

在 Deployment 的 YAML 中,有这样的内容:

spec:
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx

由 Deployment 部署的每个 Pod 的名称都不同(随机生成),所以不可能以 Pod 名称作为表示,何况 Pod 只是临时性的,所以会为每个 Pod 加上一个相同的 label,表示它们都是同一个 Deployment 创建的。

YAML 文件配置示例:

template:
 metadata:
   labels:
     app: nginx

其中 spec.template 是定义所有 Pod 的模板,template.metadata 可以为所有 Pod 设置一些元数据,例如 labels,对于 Deployment 等控制器,设置标签是必不可少的。

接下来是 spec.selectorselector 字段定义 Deployment 如何查找要管理的 Pods。spec.selector 有个 matchLabels 字段,每个 Deployment 都要配置 matchLabelsmatchLabels 里面的标签是集合运算的 in,Pod 只需要包含这些标签,就会被此 Deployment 管理。

在之前的创建的 Deployment(nginx) ,其 Pod 都会带上 app=nginx 的标签,我们可以在创建 Pod 之前,设置 Pod 的标签。Deployment 的 YAML 文件如下:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
spec:
  selector:
    matchLabels:
      app: nginx666
  template:
    metadata:
      labels:
        app: nginx666
        type: test
    spec:
      containers:
      - image: nginx:latest
        name: nginx

查询创建的 Pod:

NAME                     READY   STATUS    RESTARTS   AGE   LABELS
nginx-64b465ddc9-8t9c8   1/1     Running   0          23s   app=nginx666,pod-template-hash=64b465ddc9,type=test

同样,当我们创建 Service 或者使用 ReplicationController 时,也可以使用标签选择合适的 Pod。

回忆一下,我们之前已经学习过怎么创建 Service,示例命令:

kubectl expose deployment xxx ...

命令式的选择很简单,加上 deployment xxx 即可。对于 YAML ,我们可以使用 seletcor 选择器,表示该 Service 在什么对象上起效。

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

selector:
    app: nginx

使用 Service 时,要绑定具体的 Pod,完整的 YAML 示例如下:

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

选择器的复合运算

nodeSelector、selector 还支持以下选择方式 matchLabelsmatchExpressions,在前面我们已经了解 Deployment 有 matchLabels ,接下来我们继续深入了解。

matchExpressions 是 Pod 选择算符需求的列表。有效的运算符包括 InNotInExists 和 DoesNotExist。来自 matchLabels 和 matchExpressions 的所有要求都按逻辑与的关系组合到一起 -- 它们必须都满足才能匹配。

在 In 和 NotIn 标签中,设置的值必须是非空的。

Exists、DoesNotExist 表示某个标签是否存在;

In、NotIn 表示 A 集合中的标签是否都在 B 集合中。

matchExpressions 的表达比较复杂,下面是一个示例:

matchExpressions:
    - key: tier, operator: In, values: [cache1,cache2]
    - key: environment, operator: NotIn, values: [dev]
    - Key: disksize, operator: Exists

Pod 必须有 tier 这个标签,并且值必须在 [cache1,cach2] 中找到。

Pod 必须有 environment 这个标签,并且值不能为 dev,或者说不能在 [dev] 中出现。

Pod 必须存在 disksize 这个标签。

还可以使用 YAML 格式表示:

- matchExpressions:
          - key: kubernetes.io/e2e-az-name
            operator: In
            values:
            - e2e-az1
            - e2e-az2

读者可以根据自己喜好使用其中一种编写格式。

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

举个例子,matchLabels 示例为:

matchLabels:
   component: redis

相对于 matchExpressions :

matchExpressions:
   - key: component, operator: In, values: [redis]

两种选择器可以结合使用,示例如下:

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

[Info] 提示

复合选择比较复杂,只需了解即可。

注解

我们可以使用 Kubernetes 注解为对象附加任意的非标识的元数据,注解使用 annotations 标识。客户端程序(例如工具和库)能够获取这些元数据信息。

我们查看 dashboard 的相关 annotations :

kubectl describe services -n kubernetes-dashboard
... ...
Labels:                   k8s-app=kubernetes-dashboard
Annotations:              <none>
... ...

annotations 由 key/value 组成,类似 label,但是 annotations 支持一些特殊字符,可以用作构建发布镜像时的信息、日志记录等。

kubectl annotate service kubernetes-dashboard -n kubernetes-dashboard description='my test'
key=description

value=my test

重新查看 describe,可以看到:

Annotations:              description: my test

如果要覆盖 key 的值,需要加上 --overwrite 。

如果要删除一个 key:

kubectl annotate service kubernetes-dashboard description-

以上是关于Kubernetes:标签选择器注解容忍度亲和性的主要内容,如果未能解决你的问题,请参考以下文章

Kubernetes 的亲和性污点与容忍

k8s 亲和反亲和污点容忍

Kubernetes污点(taints)与容忍(tolerations)

Kubernetes 调度使用介绍(亲和反亲和污点容忍)

Kubernetes 调度使用介绍(亲和反亲和污点容忍)

Kubernetes 调度使用介绍(亲和反亲和污点容忍)