(八)标签
在前面,我们学习了 Deployment 部署,以及 Replicaset,但是 Pod 部署到哪个 slave 节点是随机的,即使有 3 个 slave 和 3 个 Pod 副本,也不一定每个 Node 刚刚好运行一个 Pod,也可能其中一个 Node 运行着三个副本,当然使用 Daemont 可以保证每个 Node 平均运行这种一个 Pod 副本,但是有的机器内存大,我想尽量将 Redis Pod 调度到此节点;而有的服务器磁盘容量大,我想尽可能将日志服务等调度到这些节点,那么该如何处理呢?为什么 master 不会部署我们创建的 Pod 呢?
本章我们就会讲讲 Kubernetes 中的 DaemonSet、容忍度、亲和性、Label、选择器等概念,以便控制 Pod 的调度
标签和选择器
Labels
查看 Labels
标签 主要用于表示对用户有意义的对象的属性标识,而在 Kubernetes 中是附加到对象上的键值对,在对象描述信息中以 Labels 字段保存
kubectl describe pods
Pod 由上篇创建,可以去看看是不是每个 Pod 都有 Labels

kubectl create deployment nginx-test --image=nginx:latest
kubectl describe pod nginx-test
可以看到,创建的 Pod,其 app 标签 就是 nginx-test;

除了通过 describe 的方式,还可以使用 --show-labels 参数快速获得对象标签
# 先缩容,不然太多了
kubectl scale deployment nginx --replicas=3
kubectl get pods --show-labels

在对象的 yaml 文件中,metadata.labels 存储了这个对象的标签元数据
# 以 nginx-test 为例
kubectl get pod nginx-test-d677b4b5b-8f4m4 -o yaml
# 可以简化为
metadata:
labels:
key1: value1
key2: value2
# eg
metadata:
labels:
app: nginx
env: production
version: v1
tier: frontend
表示 nginx 应用、production 环境、v1 版本、前端层级
这里就是 nginx-test 应用、pod-template-hash 一般由 Deployment 创建 ReplicaSet 时自动生成的标签,用来区分不同版本的 Pod 模板
在 Deployment 中,selector 字段设置了如何查找属于此对象的 Pod
- 可以试试 kubectl get deployment nginx -o wide,能否找到 nginx-test

Label 可以在多个地方使用,例如在 Node 上添加 Label,标识此 Node;而在 NodeSelector 里使用,则可以选择合适的 Node 运行 Pod;在 metadata 中使用,可以对元数据加以描述,在 metadata 中添加的 Label,可以在命令查询时做筛选
设置 Labels
我们可以手动给一个 Node 或别的对象添加标签,例如给 Pod 设置一个 mem,表示需要很大内存
# kubectl label nodes <node-name> <label-key>=<label-value>
# kubectl label pod {pod名称} <node-name> <label-key>=<label-value>
# 添加
kubectl label pod nginx-test-d677b4b5b-8f4m4 mem=more
# 删除
kubectl label pod nginx-test-d677b4b5b-8f4m4 mem-
# 覆盖
kubectl label pod nginx-test-d677b4b5b-8f4m4 disk=big
kubectl label pod nginx-test-d677b4b5b-8f4m4 disk=small --overwrite

在 kube-system 命名空间中,运行着 Kubernetes 的核心组件,我们可以查看此命名空间中所有组件的 Label
kubectl get nodes --namespace=kube-system --show-labels
可以看到也是非常的多的
master Ready control-plane 2d22h v1.36.3
beta.kubernetes.io/arch=arm64,
beta.kubernetes.io/os=linux,
kubernetes.io/arch=arm64,
kubernetes.io/hostname=master,
kubernetes.io/os=linux,
node-role.kubernetes.io/control-plane=,
node.kubernetes.io/exclude-from-external-load-balancers=
slave Ready <none> 2d21h v1.36.3
beta.kubernetes.io/arch=arm64
beta.kubernetes.io/os=linux,
kubernetes.io/arch=arm64,
kubernetes.io/hostname=slave,
kubernetes.io/os=linux
节点选择器
假如有个 Pod 需要很大的存储空间,而集群中的不同机器的硬盘容量都不同,我们希望 Pod 在硬盘很大的服务器上部署,我们可以通过给节点设置标签,然后在 Pod 的 YAML 文件中加上选择器,让 Pod 自动查找这类容量大的节点,并部署
# 获取集群中所有节点的 Label
kubectl get nodes --show-labels
# 加标签
kubectl label node {node_name} disksize=big
然后使用 yaml 文件创建一个 Deployment
kubectl delete deployment nginx
cat > nginx.yaml <<'EOF'
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:latest
nodeSelector:
disksize: "big"
EOF
kubectl apply -f nginx.yaml
kubectl get pods -o wide
可以看到,nginx 的 node 为 slave,因为只有 slave 才有 disksize=big 的标签

命令式标签选择
实际上,Kubernets 的标签选择是丰富多样的,这个例子表示 节点选择器是等值选择,表达式是 disktype=ssd && disksize=big,节点需要同时具备这两个 Label (key和value都要符合)
nodeSelector:
disktype: ssd
disksize: big
我们也可以使用类似方式筛选符合条件的 Pod
# 查询 Pod 所有的 Label
kubectl get pods --show-labels
# 查找符合条件的 pod
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'
kubectl get pods -l 'app=nginx,disksize!=big'
标签选择有等值和集合两种
- 等值选择有
=、==、!=三种符号,=、==无任何区别,所以实际只有 等于、不等于 两种选择情况 - 集合选择有 In NotIn Exists DoesNotExist ``` # 在 big、medium 中都可以运行 kubectl get nodes -l 'disksize in (big,medium)'
不在 small 中运行
kubectl get nodes -l 'disksize notin (small)'
只要存在就像
kubectl get nodes -l 'disksize'
不存在就行
kubectl get nodes -l '!disksize'
这里可以看到,我是带了 `''` 进行执行的,给选择表达式加单引号,可以防止空格、括号和 `!` 被 Shell 解释,防报错的问题
## selector
前面提及了 yaml 中的 nodeSelector 和 命令式的选择,这里看 Deployment 中 yaml 的 selector
kubectl get deployment nginx -o yaml
由 yaml 文件创建的 Deployment 中部署的每个 Pod 的名称都不同(随机生成),所以不可能以 Pod 名称作为标识,何况 Pod 只是临时性的,所以会为每个 Pod 加上一个相同的 label,表示它们都是同一个 Deployment 创建的
其中 spec.template 是定义所有 Pod 的模板,template.metadata 可以为所有 Pod 设置一些元数据,例如 labels,**对于 Deployment 等控制器,设置标签是不可缺少的**
spec.selector 用来定义 Deployment 要管理哪些 Pod;其中的 matchLabels,配置的是标签匹配条件,Pod 必须包含这些标签,并且对应的值完全相等,才能匹配该 selector(可以类比于 集合运算的 in);以及 Deployment 的 spec.selector 必须与 spec.template.metadata.labels 相匹配
这里做个实验,使用上面创建出的 nginx.yaml 文件,并修改 spec.selector.matchLabels.app=nginx_ray,以及 spec.template.metadata.labels.app=nginx 看看会发生什么
kubectl delete deployment nginx
kubectl apply -f nginx.yaml
可以看到,报错了,这也验证了,Deployment 的 spec.selector 与 spec.template.metadata.labels 必须相匹配,我们修改后重新创建即可看到 LABELS 的字段了

同样,当我们创建 Service 或使用 ReplicationController 时,也可以使用标签选择合适的 Pod,对于 YAML,我们可以使用 seletcor 选择器,表示该 Service 在什么对象上起效
yaml
不需要执行,可以看到 selector.app=nginx
apiVersion: v1 kind: Service metadata: name: my-service spec: type: LoadBalancer selector:
app: nginxports:
- protocol: TCP
port: 80
targetPort: 6666status: loadBalancer:
ingress:
- ip: xx.xx.xx.xx# 选择器的复合运算
nodeSelector、selector 除了可以选择使用 matchLabels 之外,还可以使用 matchExpressions
matchExpressions 是 Pod 选择算符需求的列表,比较复杂,有效的运算符包括 In、NotIn、Exists 和 DoesNotExist;来自 matchLabels 和 matchExpressions 的所有要求都按逻辑与的关系组合到一起 ——> 也就是它们必须都满足才能匹配
- 在 In 和 NotIn 标签中,设置的值必须是非空的;In、NotIn 表示 A 集合中的标签是否都在 B 集合中
- Exists、DoesNotExist 表示某个标签是否存在
举个例子,matchLabels 是由 {key,value} 对组成的映射,matchLabels 映射中的单个 {key,value} 等同于 matchExpressions 的元素,其 key 字段为 "key"、operator 为 "In"、而 values 数组仅包含 "value"
yaml selector: matchLabels:
component: redismatchExpressions:
- key: tier operator: In values: [cache]
- key: environment operator: NotIn values: [dev]
key: disksize operator: Exists
matchLabels: component: redis
等价于
matchExpressions:
- {key: component, operator: In, values: [redis]} ``` 复合选择比较复杂,了解即可
注解
Annotation 也是键值元数据,但它不是用来选择对象的标识,而是为对象附加任意的非标识的元数据
# -n 就是 --namespace= 的意思
kubectl describe services -n kubernetes-dashboard

# 加个注解
# --overwrite 可用于覆盖 key ,也就是覆盖 my test
kubectl annotate service kubernetes-dashboard-kong-proxy -n kubernetes-dashboard description='my test' ( --overwrite )
# 删除注解
kubectl annotate service kubernetes-dashboard-kong-proxy description-

总结
这章说多不多,说少不少,内容其实也就是 Labels 可以用于标识 Deployment 创建的 Pod、可以通过 节点选择器 选择 Node 挂载...虽然内容就这些,记不住的话也没关系,毕竟 k8s 也不是天天用 hhh 用到了再查也不是不行
愿诸君顺遂