标签与标签选择器

实践中,随着同类型资源对象的数量越来越多,分类管理也变得越来越有必要,基于简单且直接的标准将资源划分为多个较小的小组,无论是对开发人员还是对管理员来说,都能提升管理效率,这也正是K8S标签的核心功能之一。对于附带标签的资源对象,可以使用标签选择器挑选出符合过滤条件的资源的以完成所需要的操作,如关联,删除等。

标签概述

标签是K8S机具特色的功能之一,它能够附加于K8S的任何资源对象之上。简单来说,标签就是键值类型的数据,它们可在资源创建时指定,也可以随时按需手动添加到活动对象中,而后即可由标签选择器进行匹配检查从而完成资源挑选,一个对象可以拥有多个标签,而同一个标签也可以被添加至多个资源之上,因为标签在K8S中并不是唯一的,并且实际上经常是很多对象(如Pod)都使用相同的标签来标记具体的应用 实践中,可以为资源附加多个不同维度的标签以实现灵活的资源分组管理功能,例如,版本标签,环境标签,分层架构标签等,用于交叉标识同一个资源所属的不同版本,环境及架构层级等,下面是较为常用的标签

  • 版本标签:”release”:”stable”、”release”:”canary”、”release”:”beta”
  • 环境标签: “environment”:”dev”、”environment”:”pre”、”environment”:”prod”
  • 应用标签: “app”:”ui”、”app”:”pc”、”app”:”sc”
  • 架构层级标签: “tier”:”frontend”、”tier”:”backend”、”tier”:”cache”
  • 分区标签: “partition”:”customerA”、”partition”:”customerB”
  • 品控级别标签: “track”:”daily”、”track”:”weekly”
    标签中的键名称通常由键前缀和键名组成,其中键前缀可选,其格式如KEY_PREFIX/KEY_NAME。键名最多能使用63个字符,可以使用字母、数字、连接好(-)、下划线()、点号(.)等字符,并且只能以字母或数字开头。键前缀必须为DNS子域名格式,且不能使用超过255个字符。省略键前缀时,键将被视为用户的私有数据,不过由K8S系统组件或第三方组件自动为用户资源添加的键必须使用键前缀,而kubernetes.io/前缀则预留给K8S的核心组件使用
    标签中的键值必须不能多于63个字符,它要么为空,要么是以字母或数字开头及结尾,且中间使用了字母、数字、连接号(-)、下划线(
    )或点号(.) 等字符的数据

管理资源标签

创建资源时,可以直接在metadata字段中嵌套使用label字段定义要附件的标签,例如下面的Pod资源清单文件demo.yaml使用了两个标签env=qa和tier=frotend 在metadata字段中嵌套labels字段

  1. [root@k8s-master01 nginx]# cat demo.yaml
  2. apiVersion: v1
  3. kind: Pod
  4. metadata:
  5. name: test
  6. labels:
  7. env: qa
  8. tier: frontend
  9. spec:
  10. containers:
  11. - name: busybox
  12. image: registry.cn-hangzhou.aliyuncs.com/jiangyida/busybox:0.1
  13. ports:
  14. - name: http
  15. containerPort: 80
  16. protocol: TCP
  17. [root@k8s-master01 nginx]#
  18. [root@k8s-master01 nginx]# kubectl get pods --show-labels
  19. NAME READY STATUS RESTARTS AGE LABELS
  20. client 0/1 Completed 0 4d run=client
  21. my-nginx-854bbd7557-xnbn9 1/1 Running 0 3d4h app=my-nginx,pod-template-hash=854bbd7557
  22. test 1/1 Running 0 15s env=qa,tier=frontend
  23. [root@k8s-master01 nginx]#

标签较多时,在kubectl get pods命令上使用-L key1,key2,...选项可以指定显示有着特定标签的信息

  1. [root@k8s-master01 nginx]# kubectl get pods -L env,tier
  2. NAME READY STATUS RESTARTS AGE ENV TIER
  3. client 0/1 Completed 0 4d
  4. my-nginx-854bbd7557-xnbn9 1/1 Running 0 3d4h
  5. test 1/1 Running 0 2m17s qa frontend
  6. [root@k8s-master01 nginx]#

kubectl label命令可以直接管理活动对象的标签,以按需进行添加或修改等操作

  1. [root@k8s-master01 nginx]# kubectl label pods my-nginx-854bbd7557-xnbn9 env=prod
  2. pod/my-nginx-854bbd7557-xnbn9 labeled
  3. [root@k8s-master01 nginx]#
  4. [root@k8s-master01 nginx]# kubectl get pods -L env,tier
  5. NAME READY STATUS RESTARTS AGE ENV TIER
  6. client 0/1 Completed 0 4d
  7. my-nginx-854bbd7557-xnbn9 1/1 Running 0 3d4h prod
  8. test 1/1 Running 0 4m32s qa frontend
  9. [root@k8s-master01 nginx]#

不过,对于已经拥有了指定键名的标签,使用kubectl label为其设定新的键值时需要在命令后面加上”—overwrite”选项命令以强制覆盖原有的键值,例如,将test的env的值改为dev

  1. [root@k8s-master01 nginx]# kubectl label pods test env=dev
  2. error: 'env' already has a value (qa), and --overwrite is false
  3. [root@k8s-master01 nginx]#
  4. 如果不加--overwrite则会提示env的键值已经存在
  5. [root@k8s-master01 nginx]# kubectl label pods test env=dev --overwrite
  6. pod/test labeled
  7. [root@k8s-master01 nginx]#
  8. 使用--overwrite就可以覆盖掉原来的值
  9. [root@k8s-master01 nginx]# kubectl get pods -L env,tier
  10. NAME READY STATUS RESTARTS AGE ENV TIER
  11. client 0/1 Completed 0 4d
  12. my-nginx-854bbd7557-xnbn9 1/1 Running 0 3d4h prod
  13. test 1/1 Running 0 8m40s dev frontend
  14. [root@k8s-master01 nginx]#

用户若期望对某标签下的资源集合进行某类操作,例如,查看或删除等,则需要先使用标签选择器挑选出满足条件的资源对象

标签选择器

标签选择器用于表达标签的查询条件或选择条件,K8S API目前支持两个选择器:基于等值关系以及基于集合关系。例如,env=productionenv!=qa是基于等值关系的选择器,而tier in(frontend,backend)则是基于集合关系的选择器。另外,使用标签选择器时还将遵循以下逻辑

  • 同时指定的的多个选择器直接的逻辑关系为“与”操作
  • 使用空值的标签选择器意味着每个资源对象都将被选中
  • 空的标签选择器将无法选出任何资源
    基于等值关系的标签选择器的可用操作符有“=”“==”和”!=”三种,其中前两个意义相同,都表示“等值”关系,最后一个表示“不等”关系。kubectl get命令的-l选项能够指定使用标签选择器,例如下面显示键名为env的值不等于prod的所有对象
  1. [root@k8s-master01 ~]# kubectl get pods --show-labels
  2. NAME READY STATUS RESTARTS AGE LABELS
  3. client 0/1 Completed 0 5d21h run=client
  4. my-nginx-854bbd7557-xnbn9 1/1 Running 0 5d1h app=my-nginx,env=prod,pod-template-hash=854bbd7557
  5. test 1/1 Running 0 45h env=dev,tier=frontend
  6. [root@k8s-master01 ~]#
  7. 可以看到,键名为env,键值为prodpod对象只有一个my-nginx-854bbd7557-xnbn9,下面进行筛选
  8. [root@k8s-master01 ~]# kubectl get pods -l "env!=prod" -L env
  9. NAME READY STATUS RESTARTS AGE ENV
  10. client 0/1 Completed 0 5d21h
  11. test 1/1 Running 0 45h dev
  12. [root@k8s-master01 ~]#

再比如查询一个键名为env键值不为prod,键名为tier键值为frontend的pod对象

  1. [root@k8s-master01 ~]# kubectl get pods -l "env!=prod,tier=frontend" -L env,tier
  2. NAME READY STATUS RESTARTS AGE ENV TIER
  3. test 1/1 Running 0 45h dev frontend
  4. [root@k8s-master01 ~]#
  5. 可以看到能够满足查询条件的只有一个名称为testPod

基于集合关系的标签选择支持in、notin和exists三种操作符,它们的使用格式及意义具体如下

  • KEY IN (VALUE1,VALUE2,…):指定的键名的值存在于给定的列表中即满足条件。
  • KEY NOTIN (VALUE1,VALUE2,…): 指定的键名的值不存在于给定的列表中即满足条件。
  • KEY:所有存在此键名标签的资源
  • !KEY:所有不存在此键名标签的资源
    例如,显示键名env的值为prod或dev的所有Pod对象
  1. [root@k8s-master01 ~]# kubectl get pods --show-labels
  2. NAME READY STATUS RESTARTS AGE LABELS
  3. client 0/1 Completed 0 5d22h run=client
  4. my-nginx-854bbd7557-xnbn9 1/1 Running 0 5d2h app=my-nginx,env=prod,pod-template-hash=854bbd7557
  5. test 1/1 Running 0 45h env=dev,tier=frontend
  6. [root@k8s-master01 ~]# kubectl get pods -l "env in (prod,dev)" -L env
  7. NAME READY STATUS RESTARTS AGE ENV
  8. my-nginx-854bbd7557-xnbn9 1/1 Running 0 5d2h prod
  9. test 1/1 Running 0 45h dev
  10. [root@k8s-master01 ~]#
  11. 可以看到键名env的值为prod的和dev的都被显示出来

再比如,列出键名env的值为prod或dev,且不存在键名为tier的标签的所有对象

  1. 为了避免shell教师器解析感叹号,必须要为此类表达式使用单引号
  2. [root@k8s-master01 ~]# kubectl get pods -l 'env in (prod,dev),!tier' -L env,tier
  3. NAME READY STATUS RESTARTS AGE ENV TIER
  4. my-nginx-854bbd7557-xnbn9 1/1 Running 0 5d2h prod
  5. [root@k8s-master01 ~]#

此外,K8S的诸多资源对象必须以标签选择器的方式关联到Pod资源对象,例如Service,Deployment和replicaset类型的资源等,它们在spec字段中嵌套使用嵌套的selector字段,通过matchLabels来指定标签选择器,有的甚至还支持使用machExpressions构造复杂的标签选择机制

  • matchLabels: 通过直接给定键值对来指定标签选择器
  • matchExpressions:基于表达式指定标签选择器列表,每个选择器都刑辱”{key:KEY_NAME,operator:OPERATOR,value:[VALUE1,VALUE2,…]}”,选择器列表间为“逻辑与”关系,使用In或NotIn操作符时,其values不强制要求为非空的字符串列表,而使用Exists或DostNotExist时,其values必须为空
  1. selector :
  2. matchLabels:
  3. component: redis
  4. matchExpressions:
  5. - {key: tier, operator: In values: [cache]}
  6. - {key: environment, operator: Exists, values: }

标签赋予了K8S灵活操作资源对象的能力,它也是service和deployment等核心资源得以实现的基本前提

Pod节点选择器nodeSelector

Pod节点选择器是标签及标签选择器的一种应用,它能够让Pod资源对象基于集群中目标工作节点的标签来挑选倾向运行的目标节点 K8S的kube-scheduler守护进程负责在各工作节点中基于系统资源的可用性等标签挑选一个来运行待创建的Pod对象,默认的调度器是default-scheduler。K8S可将所有工作节点上的各系统资源抽象成资源池进行统一分配使用,因此用户无需关心POd对象的具体位置也能良好工作。不过,事情总有例外,比如仅有部分节点拥有被Pod对象依赖到的特殊硬件设备的情况,如GPU和SSD等。即便如此,用户也不应该静态指定Pod对象运行的位置,而是让scheduler基于标签和标签选择器为Pod挑选匹配的工作节点 Pod对象的spec.nodeSelector可用于定义节点标签选择器,用户事先为特定部分的Node资源对象设定好标签,然后配置Pod对象通过节点标签选择器进行匹配检测,从而完成节点亲和性调度 为Node资源对象附加标签的方法同Pod资源,例如kubectl label node/Node 命令即可,例如,我们为node01节点配置一个标签”disktype=ssd”以标识其拥有ssd设备

  1. [root@k8s-master01 ~]# kubectl label nodes 172.18.15.113 disktype=ssd
  2. node/172.18.15.113 labeled
  3. [root@k8s-master01 ~]#
  4. [root@k8s-master01 ~]# kubectl get nodes -l "disktype=ssd" -L disktype
  5. NAME STATUS ROLES AGE VERSION DISKTYPE
  6. 172.18.15.113 Ready node 13d v1.16.9 ssd
  7. [root@k8s-master01 ~]#

如果某Pod资源需要调度至这些具有SSD设备的节点之上,name只需要为其使用spec.nodeSelector标签选择器即可,下面是示例 查看一下nodeSelector的用法

  1. [root@k8s-master01 ~]# kubectl explain pod.spec.nodeSelector
  2. KIND: Pod
  3. VERSION: v1
  4. FIELD: nodeSelector <map[string]string>
  5. DESCRIPTION:
  6. NodeSelector is a selector which must be true for the pod to fit on a node.
  7. Selector which must match a node's labels for the pod to be scheduled on
  8. that node. More info:
  9. https://kubernetes.io/docs/concepts/configuration/assign-pod-node/
  10. [root@k8s-master01 ~]#

配置nodeSelector

  1. [root@k8s-master01 nginx]# cat nodeSelector.yaml
  2. apiVersion: v1
  3. kind: Pod
  4. metadata:
  5. name: test-nodeselector
  6. labels:
  7. env: pre
  8. spec:
  9. containers:
  10. - name: busybox
  11. image: registry.cn-hangzhou.aliyuncs.com/jiangyida/busybox:0.1
  12. ports:
  13. - name: http
  14. containerPort: 80
  15. protocol: TCP
  16. nodeSelector:
  17. disktype: ssd
  18. [root@k8s-master01 nginx]#
  19. [root@k8s-master01 nginx]# kubectl get pods -l "env=pre" -L env -o wide
  20. NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES ENV
  21. test-nodeselector 1/1 Running 0 38s 10.244.59.3 172.18.15.113 <none> <none> pre
  22. [root@k8s-master01 nginx]#
  23. 可以看到它被调度到了172.18.15.113这个节点上,这个时候你把它删除然后再创建发现还是会被调度到这个节点

集群中的每个节点默认就附带了多个标签,这些标签也可以直接由nodeSelector使用,尤其是希望将Pod调度至某特定节点时,可以使用kubernetes.io/hostname直接绑定相应的主机即可。不过,这种绑定至特定主机的需求还有一种更为简单的实现方式,就是使用spec.nodeName字段直接指定目标节点