集群里的对象很快会多到几百上千个:不同应用、不同版本、不同环境、不同团队。Kubernetes 没有"文件夹"或"分组"这种层级结构,而是用两种轻量机制组织资源:标签(Label) 负责横向分类与选择,命名空间(Namespace) 负责纵向隔离。
学完这一课,你能用标签选择器筛选和批量操作资源,理解 Service、Deployment 如何靠标签"找到"它们的 Pod,知道注解该放什么,并会用命名空间划分环境、给命名空间设配额。
标签:给对象贴便签
为什么需要标签
想象你要回答这些问题:"所有属于 shop 应用的 Pod?""所有 canary 版本的实例?""生产环境里前端层的资源?"对象名字没法承载这么多维度,而标签可以:它就是挂在 metadata.labels 下的键值对,一个对象可以有任意多个,多个对象可以共享同一个标签。
apiVersion: v1
kind: Pod
metadata:
name: web-prod
labels:
app: web
tier: frontend
env: prod
spec:
containers:
- name: nginx
image: nginx:1.27
---
apiVersion: v1
kind: Pod
metadata:
name: web-dev
labels:
app: web
tier: frontend
env: dev
spec:
containers:
- name: nginx
image: nginx:1.27
---
apiVersion: v1
kind: Pod
metadata:
name: api-prod
labels:
app: api
tier: backend
env: prod
spec:
containers:
- name: nginx
image: nginx:1.27kubectl apply -f labeled-pods.yaml
kubectl get pods --show-labelsNAME READY STATUS RESTARTS AGE LABELS
api-prod 1/1 Running 0 10s app=api,env=prod,tier=backend
web-dev 1/1 Running 0 10s app=web,env=dev,tier=frontend
web-prod 1/1 Running 0 10s app=web,env=prod,tier=frontend用 -L 把某些标签作为独立列展示,更清晰:
kubectl get pods -L app,envNAME READY STATUS RESTARTS AGE APP ENV
api-prod 1/1 Running 0 20s api prod
web-dev 1/1 Running 0 20s web dev
web-prod 1/1 Running 0 20s web prod标签的语法
- 键可以有可选前缀:
前缀/名字,前缀是 DNS 子域名,如app.kubernetes.io/name。无前缀的键被视为用户私有。kubernetes.io/和k8s.io/前缀保留给 Kubernetes 核心组件。 - 名字部分和值都不超过 63 个字符,以字母或数字开头结尾,中间可含
-、_、.。值可以为空。
增删改标签
kubectl label pod web-dev owner=alice # 添加
kubectl label pod web-dev env=staging --overwrite # 修改已有标签需要 --overwrite
kubectl label pod web-dev owner- # 键后加减号表示删除标签选择器
标签本身不做任何事,它的价值在于被选择器(Selector) 使用。
等值选择器(Equality-based)
支持 =(或 ==)和 !=,多个条件用逗号连接,表示"与":
kubectl get pods -l env=prod
kubectl get pods -l app=web,env!=prodNAME READY STATUS RESTARTS AGE
web-dev 1/1 Running 0 1m集合选择器(Set-based)
支持 in、notin、存在(只写键)、不存在(!键):
kubectl get pods -l 'env in (prod,staging)'
kubectl get pods -l 'tier notin (backend)'
kubectl get pods -l 'owner' # 有 owner 标签的
kubectl get pods -l '!owner' # 没有 owner 标签的选择器也能用于批量操作:
kubectl delete pods -l env=dev
kubectl logs -l app=web --prefix # 一次看所有匹配 Pod 的日志在 YAML 中的选择器
较老的资源(Service、ReplicationController)只支持等值选择器,写成简单的 map:
selector:
app: web较新的资源(Deployment、ReplicaSet、Job、DaemonSet 等)支持 matchLabels 与 matchExpressions,两者之间是"与"关系:
selector:
matchLabels:
app: web
matchExpressions:
- { key: env, operator: In, values: [prod, staging] }
- { key: canary, operator: DoesNotExist }operator 可选 In、NotIn、Exists、DoesNotExist。
推荐的标签
官方推荐一组通用标签,很多工具(Helm、Dashboard、监控)都认识它们:
| 键 | 示例 | 说明 |
|---|---|---|
app.kubernetes.io/name | mysql | 应用名 |
app.kubernetes.io/instance | mysql-abcxyz | 实例的唯一名称 |
app.kubernetes.io/version | 5.7.21 | 版本 |
app.kubernetes.io/component | database | 在架构中的组件 |
app.kubernetes.io/part-of | wordpress | 所属的上层应用 |
app.kubernetes.io/managed-by | Helm | 管理工具 |
注解:给机器看的备注
注解(Annotation)也是 metadata 下的键值对,语法和标签类似,但有两点不同:
- 不能用于选择,没有注解选择器。
- 值可以很长(所有注解总计不超过 256 KiB),可以是任意字符串,包括 JSON。
所以它用来存放"描述性、给工具读"的信息:
metadata:
annotations:
description: "订单服务前端,负责人 alice@example.com"
kubernetes.io/change-cause: "升级到 nginx 1.27"
prometheus.io/scrape: "true"常见用途:构建与发布信息(Git commit、镜像摘要)、负责人联系方式、给 Ingress 控制器或监控系统的配置开关、kubectl apply 自己也会写 kubectl.kubernetes.io/last-applied-configuration 注解来记录上次应用的配置。
kubectl annotate pod web-prod description="生产前端"
kubectl describe pod web-prod | grep -A2 Annotations命名空间:虚拟的隔离区
为什么需要命名空间
同一个集群被多个团队、多个环境共享时,需要:避免名字冲突(两个团队都想叫 web);按范围授权(dev 团队只能动 dev 的东西);按范围限制资源用量。命名空间(Namespace)提供了这种作用域。
kubectl get namespacesNAME STATUS AGE
default Active 2d
kube-node-lease Active 2d
kube-public Active 2d
kube-system Active 2d
local-path-storage Active 2d| 命名空间 | 用途 |
|---|---|
default | 未指定命名空间时的默认位置 |
kube-system | Kubernetes 系统组件(CoreDNS、kube-proxy 等) |
kube-public | 所有人(包括未认证用户)可读,很少使用 |
kube-node-lease | 节点心跳用的 Lease 对象 |
local-path-storage | kind 自带的本地存储供给器(kind 特有) |
创建与使用
apiVersion: v1
kind: Namespace
metadata:
name: dev
labels:
env: dev
---
apiVersion: v1
kind: Namespace
metadata:
name: prod
labels:
env: prodkubectl apply -f namespaces.yaml
kubectl run web --image=nginx:1.27 -n dev
kubectl run web --image=nginx:1.27 -n prod # 同名不冲突
kubectl get pods -n dev
kubectl get pods -A -l run=webNAMESPACE NAME READY STATUS RESTARTS AGE
dev web 1/1 Running 0 8s
prod web 1/1 Running 0 6s也可以把命名空间写在对象的 metadata.namespace 里;或者切换当前 context 的默认命名空间,省得每次敲 -n:
kubectl config set-context --current --namespace=dev命名空间不隔离什么
另外,并非所有资源都属于命名空间。Node、PersistentVolume、StorageClass、Namespace 本身都是集群级资源:
kubectl api-resources --namespaced=false删除命名空间会级联删除其中的所有对象,操作前务必确认。
命名空间与 DNS
每个 Service 都会得到一个 DNS 名称,格式为:
<service-name>.<namespace>.svc.cluster.local同一命名空间内的 Pod 用短名 web 就能访问;跨命名空间必须带上命名空间,比如 web.prod 或完整的 web.prod.svc.cluster.local。这是因为 Pod 的 /etc/resolv.conf 里配置了按自身命名空间优先的搜索域:
kubectl exec -n dev web -- cat /etc/resolv.confsearch dev.svc.cluster.local svc.cluster.local cluster.local
nameserver 10.96.0.10
options ndots:5后面的 Service 与服务发现 一课会实际验证这些 DNS 名称。
ResourceQuota 简介
命名空间的另一个作用是承载资源配额。ResourceQuota 可以限制一个命名空间里对象的数量和资源总量:
apiVersion: v1
kind: ResourceQuota
metadata:
name: dev-quota
namespace: dev
spec:
hard:
pods: "3"
configmaps: "10"
services: "5"kubectl apply -f dev-quota.yaml
kubectl run a --image=nginx:1.27 -n dev
kubectl run b --image=nginx:1.27 -n dev
kubectl run c --image=nginx:1.27 -n devpod/a created
pod/b created
Error from server (Forbidden): pods "c" is forbidden: exceeded quota: dev-quota, requested: pods=1, used: pods=3, limited: pods=3kubectl describe quota dev-quota -n devName: dev-quota
Namespace: dev
Resource Used Hard
-------- ---- ----
configmaps 1 10
pods 3 3
services 0 5(pods 的 3 个包括前面创建的 web;configmaps 已用 1 个,是每个命名空间自动创建的 kube-root-ca.crt。)
对 CPU、内存的配额需要配合 Pod 的 requests/limits 一起理解,放在阶段 2 的健康检查与资源管理里详细讲。
清理:
kubectl config set-context --current --namespace=default # 先切回 default
kubectl delete -f labeled-pods.yaml --ignore-not-found
kubectl delete namespace dev prod动手练习
- 创建本课的三个带标签的 Pod,用一条集合选择器命令列出
env为prod或staging、且没有owner标签的 Pod。 - 手动给
web-dev加上canary=true标签,再用-L canary列出所有 Pod 观察结果,最后删除这个标签。 - 给一个 Pod 添加一个包含 JSON 字符串的注解,用
-o jsonpath把它读出来。 - 创建
team-a命名空间,设置最多 2 个 Pod 的配额,验证第 3 个 Pod 被拒绝,并用describe quota查看用量。 - 在
dev和prod各跑一个 Pod,分别cat /etc/resolv.conf,对比 search 行的差异。
自测
标签和注解都可以挂任意键值对,什么时候用哪个?
需要被选择器筛选或引用(Service、Deployment、调度)的信息用标签;只供人或工具阅读的描述性信息(负责人、构建信息、工具配置)用注解。注解不能被选择,但值可以更长、格式更自由。
-l 'env in (prod,staging),tier!=backend' 选中了什么?
env 标签值是 prod 或 staging,并且 tier 不等于 backend 的对象。注意 != 也会选中根本没有 tier 标签的对象。
dev 命名空间的 Pod 如何访问 prod 命名空间里名为 api 的 Service?
使用 api.prod,或完整域名 api.prod.svc.cluster.local。只写 api 会按搜索域解析为 api.dev.svc.cluster.local。
把两个团队放进不同命名空间,能阻止它们的 Pod 互相访问吗?
不能。命名空间只隔离名字和 API 权限作用域,默认网络是全通的。要阻止互访需要 NetworkPolicy(且 CNI 支持),权限隔离需要 RBAC。