2.2. 资源管理与 API 群组

在 Kubernetes 里,「资源」不是抽象名词:每一个对象都对应一个带版本号的 HTTP 路径,每一类资源都属于某个 API 群组。理解这层映射,你看 YAML 的方式会从「背字段」变成「查接口」。

层级:

L2 · 核心

难度:

基础

预计:

55 分钟

前置:

架构与对象模型、kubectl 基本操作

2.2.1. 你将学到

  • API 群组、版本与资源发现命令

  • 对象类资源的通用格式与身份字段

  • 命名空间的作用边界

  • Node 对象的 capacity / allocatable / Conditions

  • 标签选择器语法、kubectl 的两种管理方式与配额基础

2.2.2. API 群组与版本

Kubernetes 的 API 是一棵可发现的树:核心组在 /api/v1,其他组在 /apis/<group>/<version>。

路径

典型资源

/api/v1

Pod、Service、Namespace、Node

/apis/apps/v1

Deployment、StatefulSet、DaemonSet

/apis/batch/v1

Job、CronJob

/apis/networking.k8s.io/v1

Ingress、NetworkPolicy

架构图

版本号代表稳定性:v1alpha1 默认关闭、随时可能删除;v1beta1 默认开启但字段可能变化;v1 向后兼容。升级集群前先查废弃清单——extensions/v1beta1 Ingress 在 1.22 移除,batch/v1beta1 CronJob 与 policy/v1beta1 PDB 在 1.25 移除。

kubectl api-versions
kubectl api-resources --api-group=apps -o wide
kubectl get --raw /apis/apps/v1 | jq -r '.resources[].name'
kubectl explain deployment.spec.strategy --api-version=apps/v1

2.2.3. 对象的通用格式

所有对象类资源都遵循同一骨架:apiVersion + kind 决定类型与版本,metadata 描述身份,spec 是你写的期望状态,status 是控制器写的实际状态。

字段

谁写

说明

apiVersion / kind

你

决定资源类型与版本,创建后不可修改

metadata.name

你

同命名空间内唯一;多数资源支持 generateName

metadata.uid

API Server

对象全生命周期唯一,删除重建会变

metadata.resourceVersion

API Server

乐观锁版本号,replace 冲突时用它排查

metadata.generation

API Server

spec 每变更一次加一,判断控制器是否已处理

2.2.4. 命名空间

命名空间是「名称作用域 + 授权边界 + 配额边界」的三合一,不是目录。集群自带 default、kube-system、kube-public、kube-node-lease 四个,最后一个是节点心跳租约,用来减少 Node 状态写入压力。它影响同名资源能否共存、RBAC 授权范围、ResourceQuota 作用域与 DNS 名称 svc.namespace.svc.cluster.local;跨命名空间引用对象必须写全名,NetworkPolicy 默认也只在本命名空间生效。

警告

删除命名空间是异步的

要等其中所有资源(包括 CRD 实例、聚合 API 资源)清理完。卡在 Terminating 时先用 kubectl api-resources --verbs=list --namespaced -o name 逐个确认残留对象,再查 finalizer;直接清空 spec.finalizers 会让残留资源变成孤儿。

2.2.5. 节点资源与 Node 对象

Node 是集群级资源,通常由 kubelet 通过 --register-node 自动注册,node controller 维护状态。spec 里有 podCIDR、taints、unschedulable(对应 kubectl cordon);status 里有 capacity、allocatable、conditions、addresses。capacity 是节点总资源,allocatable 是扣掉 kube-reserved、system-reserved、eviction-hard 后能分给 Pod 的量;调度器只看 allocatable,所以「节点还有内存但 Pod Pending」通常是预留导致的。

Condition

含义

典型影响

Ready

kubelet 正常且能上报

False 时节点不接收新 Pod

MemoryPressure

内存不足

kubelet 驱逐 BestEffort Pod

DiskPressure

磁盘或 inode 不足

镜像回收、Pod 被驱逐

NetworkUnavailable

CNI 未就绪

Pod 网络不通

kubectl describe node k8s-node01
kubectl label node k8s-node01 disktype=ssd

节点标签是调度与拓扑的基础:kubernetes.io/hostname、kubernetes.io/os、kubernetes.io/arch、topology.kubernetes.io/zone 由系统维护,自定义标签配合 nodeSelector 与节点亲和使用。

2.2.6. 标签、选择器与注解

标签是「连接对象的线」,选择器是「读这根线的方式」:

kubectl get pods -l app=web,tier=frontend     # 等值,多个条件是 AND
kubectl get pods -l 'env!=dev'                # 没有该标签的对象也会命中
kubectl get pods -l 'env in (dev,staging)'    # 集合

标签参与选择,注解不参与。注解会被工具读取,例如 Ingress 参数、证书签发、变更说明,总量受 256 KiB 限制。生产环境建议统一使用 app.kubernetes.io/name、instance、version、managed-by 这套标准标签,便于 Helm、监控与成本分摊识别。

2.2.7. 用 kubectl 管理资源

  • 声明式:apply / diff / delete -f,可版本化、可重复,生产推荐;

  • 命令式:create / run / expose,适合临时验证与生成 YAML 草稿;

  • 局部修改:label / annotate / patch / scale / set image,精确变更单个字段;

  • 交互修改:edit,调试可以,但容易与 Git 中的 YAML 冲突。

kubectl create ns demo --dry-run=client -o yaml
kubectl apply --server-side --field-manager=handbook -f ns-with-labels.yaml
kubectl get pods -A --field-selector status.phase=Running -o name

2.2.8. 资源配额与限制范围

ResourceQuota 限制「命名空间总量」,LimitRange 限制「单个对象的默认值与上下限」:

quota-and-limits.yaml
apiVersion: v1
kind: ResourceQuota
metadata:
  name: eshop-quota
  namespace: eshop
spec:
  hard:
    requests.cpu: "2"
    requests.memory: 2Gi
    limits.memory: 4Gi
    pods: "10"
---
apiVersion: v1
kind: LimitRange
metadata:
  name: eshop-limits
  namespace: eshop
spec:
  limits:
    - type: Container
      default: {cpu: 200m, memory: 256Mi}
      defaultRequest: {cpu: 100m, memory: 128Mi}
      max: {cpu: "1", memory: 1Gi}
  • 配额里一旦出现 requests.cpu 或 limits.memory,该命名空间内所有 Pod 都必须设置对应字段,否则创建被拒;

  • LimitRange 的 default / defaultRequest 在准入阶段先补齐,再接受配额检查,所以两者通常一起用;配额按命名空间独立计算,不感知节点容量。

2.2.9. 动手实验

2.2.9.1. 实验一:命名空间、标签与配额

ns-with-labels.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: eshop
  labels:
    app: eshop
    env: dev
kubectl apply -f ns-with-labels.yaml
kubectl get ns eshop --show-labels
kubectl label ns eshop env=staging --overwrite
kubectl apply -f quota-and-limits.yaml
kubectl run web -n eshop --image=nginx:1.27 --labels='app=web,tier=frontend'
kubectl describe quota eshop-quota -n eshop

再创建一个不写 resources 的 Pod,观察 LimitRange 补默认值、配额拒绝超额创建。

2.2.9.2. 实验二:观察 Node 对象

node-demo.yaml
apiVersion: v1
kind: Node
metadata:
  name: temp-node.ilinux.io
  labels:
    kubernetes.io/hostname: temp-node.ilinux.io
spec:
  podCIDR: 10.244.6.0/24
kubectl apply -f node-demo.yaml
kubectl describe node temp-node.ilinux.io | sed -n '/Conditions/,/Addresses/p'
kubectl delete node temp-node.ilinux.io

警告

手工创建的 Node 不会带来 kubelet

它只用来理解结构与状态字段:没有 kubelet 注册,会一直 NotReady,node controller 还会给它加污点。实验后立即删除,不要在生产集群模仿。

2.2.10. 验证清单

  • 能说出 apps/v1 对应的完整请求路径

  • kubectl explain deploy --api-version=apps/v1 能查到字段

  • 命名空间标签变更后,-l env=staging 能筛选到它

  • Node 的 allocatable 小于 capacity,能解释差额来源

  • 未写 resources 的 Pod 被 LimitRange 补上默认值,超额创建被配额拒绝

2.2.11. 常见问题与排错

现象

可能原因

排查入口

no matches for kind

apiVersion 写错或 CRD 未安装

kubectl api-resources、api-versions

namespaces "demo" is forbidden

RBAC 未授权该命名空间

kubectl auth can-i --list -n demo

命名空间一直 Terminating

finalizer 或残留资源

kubectl get ns demo -o yaml

exceeded quota

配额已满或字段缺失

kubectl describe quota -n eshop

2.2.12. 进阶挑战

  1. 用 kubectl get --raw /openapi/v3 找出 apps/v1 的 schema 路径,再用 jq 提取 Deployment 的必填字段。

  2. 用 kubectl auth can-i --list -n eshop 解释当前用户在该命名空间能做什么、不能做什么。

  3. 给命名空间加一个 scopes: BestEffort 的配额,观察普通 Pod 与 BestEffort Pod 的不同结果。

  4. 用 --field-selector 找出所有 spec.nodeName 为某节点的 Pod,并解释为什么字段选择器只支持有限字段。

2.2.13. 小结

  • 每个对象都是某个 API 群组、某个版本下的 HTTP 资源,apiVersion 决定路径。

  • 对象通用结构是 apiVersion / kind / metadata / spec / status,uid、resourceVersion、generation 各有用途;命名空间是名称、授权、配额的三重边界。

  • Node 是集群级资源,调度只看 allocatable;标签负责选择,注解负责携带信息。

2.2.14. 延伸阅读