Label/Selector 对照
Selector(标签选择器)就两种语法:等值匹配对应 matchLabels,集合匹配对应 matchExpressions。难记的是各资源的字段位置和支持范围,本篇一张表对照清楚。
等值匹配
判断 key 等不等于某个值,三种操作符:
| 操作符 | 含义 | kubectl 写法 | YAML 写法 |
|---|---|---|---|
= | 等于 | -l app=web | matchLabels 键值对 |
== | 等于(与 = 完全等价) | -l app==web | 同上 |
!= | 不等于 | -l 'app!=web' | matchLabels 不支持,改用 NotIn |
YAML 里对应字段是 matchLabels,多个键值对之间是与关系:
selector:
matchLabels:
app: web
tier: frontend
集合匹配
判断 key 的值是否落在某个集合里,四种运算符:
| operator | 含义 | values 是否必填 | kubectl 写法 |
|---|---|---|---|
In | 值在集合中 | 必填(至少一个) | -l 'env in (prod,staging)' |
NotIn | 值不在集合中 | 必填(至少一个) | -l 'env notin (dev)' |
Exists | 存在这个 key(不看值) | 不填 | -l debug |
DoesNotExist | 不存在这个 key | 不填 | -l '!debug' |
YAML 里对应字段是 matchExpressions,每条表达式由 key、operator、values 三部分组成:
selector:
matchExpressions:
- key: tier
operator: In
values: ["frontend", "backend"]
- key: track
operator: NotIn
values: ["canary"]
- key: debug
operator: DoesNotExist
matchLabels 和 matchExpressions 可以混用,所有条件之间都是与关系:
selector:
matchLabels:
app: web
matchExpressions:
- key: env
operator: In
values: ["prod"]
各资源 Selector 写法
| 资源 / 场景 | 字段位置 | 支持的语法 | 创建后能否修改 |
|---|---|---|---|
| Service | spec.selector | 仅等值键值对 | 可改 |
| Deployment | spec.selector(matchLabels / matchExpressions) | 两种都支持 | 不可变 |
| ReplicaSet / DaemonSet / StatefulSet | spec.selector | 两种都支持 | 不可变 |
| Job | spec.selector(一般不手写,由控制器自动注入) | 两种都支持 | 不可变 |
| PVC | spec.selector | 两种都支持 | 不可变 |
| NetworkPolicy 选 Pod | spec.podSelector | 两种都支持 | 可改 |
| NetworkPolicy 选命名空间 | ingress/egress 规则里的 namespaceSelector | 两种都支持 | 可改 |
| NodeAffinity | nodeSelectorTerms[].matchExpressions | 仅表达式(另支持 Gt、Lt) | 随 Pod 模板滚动生效 |
| PodAffinity / PodAntiAffinity | labelSelector(matchLabels / matchExpressions) | 两种都支持 | 随 Pod 模板滚动生效 |
| kubectl 查询过滤 | -l / --selector 参数 | 两种都支持 | 不涉及 |
Service
只支持等值键值对,选中 labels 完全包含这些键值对的 Pod:
apiVersion: v1
kind: Service
metadata:
name: web
spec:
selector:
app: web # 选中带 app=web 的 Pod
ports:
- port: 80
targetPort: 8080
Deployment
spec.selector 必须能被 Pod 模板 template.metadata.labels 命中,且创建后不可变:
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
spec:
selector:
matchLabels:
app: web # 必须被下面模板的 labels 包含
template:
metadata:
labels:
app: web
env: prod # 模板多出的 label 无妨,反过来则创建报错
spec:
containers:
- name: web
image: nginx:1.27
NetworkPolicy
podSelector 选本命名空间内的 Pod,namespaceSelector 选整个命名空间;同一条 from 里两者同时写是与关系,写空 {} 表示全选:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-frontend
spec:
podSelector:
matchLabels:
app: web # 策略作用于哪些 Pod,空 {} 表示全部
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: frontend
podSelector:
matchLabels:
app: gateway
NodeAffinity / PodAffinity
NodeAffinity 的 selector 只有 matchExpressions 一种写法,且运算符在四种之外还支持 Gt、Lt(比较整数值);PodAffinity 的 labelSelector 则是完整的 LabelSelector,两种语法都能用:
# 节点亲和:只调度到 SSD 节点
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: disktype
operator: In
values: ["ssd"]
# Pod 亲和:尽量和 cache 的 Pod 落在同一节点
affinity:
podAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchLabels:
app: cache
topologyKey: kubernetes.io/hostname
两组关系要分清:nodeSelectorTerms 多条之间是或,每条 term 内的 matchExpressions 之间是与。NodeAffinity 的 term 里还有 matchFields,可以按字段(如 metadata.name)选节点。
kubectl -l 查询速查
-l(--selector)用逗号分隔多个条件,逗号 = 与:
| 需求 | 写法 |
|---|---|
| 等于 | -l app=web |
| 不等于 | -l 'app!=web' |
| 多条件(与) | -l 'app=web,env=prod' |
| 值在集合中 | -l 'env in (prod,staging)' |
| 值不在集合中 | -l 'env notin (dev)' |
| 有这个 key | -l debug |
| 没有这个 key | -l '!debug' |
| 混合写法 | -l 'app=web,env in (prod,staging),!debug' |
!=、!、()、, 在 shell 里有特殊含义(交互式 bash 里 ! 还会触发历史展开),不加引号很容易报错。-l 后面不管写什么,都先用单引号包起来。
-l 不只 get 能用:delete、logs、label、describe 等批量操作命令都接受。
常用系统标签
节点与拓扑标签
kubelet 上报节点信息时自动打上的标签,调度与亲和规则里直接引用:
| 标签 | 含义 |
|---|---|
kubernetes.io/hostname | 节点主机名 |
kubernetes.io/os | 操作系统(linux、windows) |
kubernetes.io/arch | CPU 架构(amd64、arm64) |
topology.kubernetes.io/zone | 可用区(云厂商注入) |
topology.kubernetes.io/region | 地域(云厂商注入) |
node-role.kubernetes.io/control-plane | 控制平面节点标记(值为空) |
node-role.kubernetes.io/master | 旧版控制平面标记(kubeadm 1.24 起新节点不再打) |
node-role 类标签只看 key 不看值,配合 Exists / DoesNotExist 用,例如「不要调度到控制平面」就是 node-role.kubernetes.io/control-plane + DoesNotExist。
app.kubernetes.io 推荐标签
社区约定的一组应用管理标签(recommended labels),Helm chart 和周边工具默认认这套:
| 标签 | 含义 | 示例 |
|---|---|---|
app.kubernetes.io/name | 应用名 | mysql |
app.kubernetes.io/instance | 实例名(区分同一应用的多个部署) | mysql-prod |
app.kubernetes.io/version | 应用版本 | "5.7.21" |
app.kubernetes.io/component | 在架构中的角色 | database |
app.kubernetes.io/part-of | 所属的上层应用 | wordpress |
app.kubernetes.io/managed-by | 管理工具 | helm |
Label 命名规则
key 的结构是 [prefix/]name,prefix 可选:
| 部分 | 限制 |
|---|---|
| name(必填) | 最长 63 字符,首末必须是字母数字,中间可用 -、_、. |
| prefix(可选) | DNS 子域名格式,最长 253 字符,以 / 与 name 分隔 |
| value | 最长 63 字符,规则同 name,可以为空 |
kubernetes.io/ 和 k8s.io/ 前缀保留给 Kubernetes 核心组件,自定义标签不要占用。
常见坑
spec.selector 是不可变字段,创建后再改 apply 直接报错(field is immutable)。规划标签时别把每次发版都变的值(如版本号)放进 selector 命中的 label 里;真要改只能删掉 Deployment 重建,Pod 会全部重建。
Service 纯靠 label 找 Pod:selector 里的键值对必须被 Pod 的 labels 包含。Deployment 场景下要三方对齐:spec.selector ↔ template.metadata.labels ↔ Service 的 spec.selector。对不上时 Endpoints 为空,Service 有 ClusterIP 却没后端,排障先跑 kubectl get endpoints。
kubectl label 手打的标签容易和 YAML 清单漂移,下次 apply 可能被覆盖回去。长期有效的标签写进清单,纳入版本管理;临时标记(排障、灰度)再用命令行。