在 kind 集群里看 API 的自描述:kubectl api-resources 有 73 种资源(35 命名空间级、38 集群级),冷缓存下只发 GET /api 与 GET /apis 两个请求(聚合发现);kubectl explain 的字段、枚举和描述来自 OpenAPI v3,apps/v1 的文档约 702 KiB、166 个 schema;自己创建 CRD 后 api-resources 立刻多出一项,explain 约 1.8 秒后就能显示 CRD schema 里的 enum 与 description。
在 kind 集群里实测怎么判断“就绪”:kubectl wait 的 condition=Ready(探针失败时 5 秒超时)与 jsonpath 字段、等不存在的资源立刻报错、--for=create / --for=delete;滚动更新时 wait --for=condition=Available 0.2 秒就返回而 rollout status 要等 20 秒到全部更新完成;镜像拉不到时 rollout status 在 progressDeadlineSeconds 之后以退出码 1 结束,旧副本仍在服务;失败的 Job 永远不会 Complete。
拆开 kubectl top:它读的是聚合 API metrics.k8s.io(APIService 指向 metrics-server),返回的 usage 用纳核和 Ki 表示;数据源是每个节点 kubelet 的 /metrics/resource,CPU 是累计计数器,用采样时间戳相邻两次相减得到速率(限 300m 的死循环算出 300m,top 显示 301m);新 Pod 要 23 秒后才第一次出现在 top 里;内存是 working set(238 MiB 的 memory.current 里扣掉 150 MiB 的 inactive_file 得 88 MiB,top 显示 87Mi)。
在 kind 集群里实测 RuntimeClass 与 Pod Overhead:overhead 在 RuntimeClass 上声明(250m / 120Mi),由准入写进 Pod 的 spec.overhead;节点 requests 因它多占 350m 而不是 100m;Pod 级 cgroup 的 memory.max 是 184 MiB(64 + 120)、cpu.max 是 35000,容器级不变;ResourceQuota 把 overhead 算进 requests.cpu(已用 450m),超出被拒;不存在的 RuntimeClass 被拒绝,handler 没配的 Pod 停在 FailedCreatePodSandBox。
在 kind 集群里实测 ephemeral-storage:节点 capacity 就是 df 的总大小(485473984512 字节)且 allocatable 无预留;requests 100Ti 的 Pod 因 Insufficient ephemeral-storage 一直 Pending;limit 50Mi 的 Pod 往容器可写层或磁盘型 emptyDir 写 120 MiB,分别在 23 秒和 33 秒后被 kubelet 以 Evicted 驱逐;内存型 emptyDir 不计入,100 秒后仍 Running。
在 kind 集群里实测 Downward API:fieldRef 把 Pod 名、命名空间、uid、节点、ServiceAccount、Pod IP、Host IP 以及 metadata.labels['k'] 单个键注入环境变量;resourceFieldRef 的 divisor 决定单位(0.5 核默认变 1,divisor 1m 变 500),没写 limits 时得到节点的 allocatable;downwardAPI 卷里的 labels 文件在 kubectl label 之后 1 秒就更新而环境变量不变;不支持的 fieldPath(metadata.labels、status.phase 等)创建时被拒。
在 kind 起的真实 Kubernetes v1.37.0 集群里实测 API Server 的请求链:同一个 POST /pods 换身份和请求体分别得到 401、403、422、403 和 201,证明鉴权先于对象校验、对象校验先于 PodSecurity 准入;--dry-run=server 把提交的 13 个字段补成 49 个但不落盘;创建一个 Pod 时调用了 15 个变更型和 11 个校验型准入控制器;只有真正创建后 etcd 里才有 key、才有 resourceVersion。
在 kind 集群里部署一个真实的 HTTPS webhook 服务,实测准入 webhook 的行为:两个 Mutating 先后为 a、b(与创建先后无关),Validating 看到的是变更之后的对象;服务缩到 0 时 failurePolicy=Fail 以 connection refused 拒绝、Ignore 放行;服务睡 4 秒时 timeoutSeconds=1 在 1.11 秒后以 context deadline exceeded 失败或放行;namespaceSelector、objectSelector、matchConditions、操作类型决定哪些请求不会调用 webhook,dry-run 会调用。
在 kind 集群里创建一个带 openAPIV3Schema 的 CRD 并逐项实测:必填、范围、枚举、类型校验各自的报错;未知字段在 kubectl 严格校验下被拒绝、用 --validate=false 提交时被剪掉;etcd 里存的 JSON 只含写入时补的默认值,CRD 事后新增的默认值是读出来时才补;开了 status 子资源之后主资源和 /status 各自忽略对方的字段,generation 只跟着 spec 变化。
在 kind 集群里为同一个 CRD 配两个版本(v1beta1 的 spec.replicas、v1 的 spec.size)并实测转换:strategy None 时用 v1beta1 写入的字段被剪掉、etcd 里只剩 {};Webhook 转换时 etcd 存存储版本 v1;写一次就会触发 v1→v1beta1 转换,GET 按需转换而 list 不再调用;webhook 停掉后存储版本照常读写,非存储版本读写 list 全部失败;改存储版本后 storedVersions 变成 ["v1","v1beta1"],旧对象仍是旧版本,删除旧版本被拒绝。
在 kind 集群里实测同一个 HPA 用 autoscaling/v1 与 v2 读出来的样子:v1 里只有 targetCPUUtilizationPercentage,内存指标和 behavior 被塞进 autoscaling.alpha.kubernetes.io 的注解;etcd 里不管用哪个版本写都存成 autoscaling/v2 的 protobuf;读已弃用的 v1 Endpoints 和 CRD 里标记 deprecated 的版本会得到响应头 Warning: 299,apiserver_requested_deprecated_apis 是值为 1 的 gauge;kubectl get hpa 默认请求 preferredVersion v2,短名加版本时版本被忽略。
在 kind 集群里实测 resourceVersion:两个写入者读到同一版本后先后 replace,后者得到 409 Conflict;merge patch 带旧 resourceVersion 会冲突、不带就覆盖,kubectl apply 不带 resourceVersion;resourceVersion 等于 etcd 里该 key 的 mod_revision,重复的无实际改动不会让它变化;resourceVersionMatch=Exact 能读回创建时的旧内容,太旧报 Expired,比当前大 100 万的版本等 43.1 秒后报 Too large resource version。