读 kubeadm 为 kube-apiserver 生成的 28 个参数并按作用分类,再一次改三个:--service-node-port-range=31000-31010 让新 NodePort 分到 31010 而旧的 32326 不动、指定 30050 被拒;追加 AlwaysPullImages 准入后新 Pod 的 imagePullPolicy 从 IfNotPresent 变成 Always,旧 Pod 不变;--anonymous-auth=false 把匿名请求从 403 变成 401;改动需要重启 apiserver(约 42 秒),改回去又用了 75 秒。
在 kind 里实测 imagePullPolicy:不写时 API Server 按镜像引用补默认值(无 tag / latest → Always,其他 tag 和 digest → IfNotPresent);镜像在本地而仓库连不上时,IfNotPresent 与 Never 能跑,Always 报 ErrImagePull → ImagePullBackOff;本地没有的镜像,IfNotPresent 尝试拉取失败而 Never 直接 ErrImageNeverPull;同一个 tag 在节点上被改指向别的镜像后,IfNotPresent 的两个 Pod 跑着不同的镜像。
用一个假的 HTTP 镜像仓库让五个 Pod 各撞上一种拉取失败:连接被拒绝、404 not found、401 Unauthorized、域名解析失败、镜像名格式错误(InvalidImageName),ImagePullBackOff 状态的 message 带着 ErrImagePull 的真正原因;观察 330 秒的重试节奏,ErrImagePull 出现在 1、24、48、103、194 秒,相邻间隔 23、24、55、91 秒,越来越长。
用一个要求 Basic 认证的假仓库实测 imagePullSecrets:kubernetes.io/dockerconfigjson 的 auths 里 auth 就是 base64(用户名:密码);没有 secret 时仓库只收到不带 Authorization 的请求并报 no basic auth credentials,有 secret 时运行时先探测、收到 401 再带上 puller:s3cret,报错变成 not found(认证通过);给 default ServiceAccount 配置后新 Pod 自动带上;auths 的服务器地址写错、密码错、跨命名空间引用 secret 都会失败。
用 6 个 ConfigMap 实测标签选择器:!= 与 notin 会选中没有该键的对象,逗号是 AND;Deployment 的 selector 必须匹配模板标签、不能为空、创建后不可改,空选择器在 Service(不选)与 NetworkPolicy(全选)里含义相反;标签值最长 63 字符、键前缀必须是小写 DNS 子域名;把 Pod 的 app 标签改掉,Service 的 endpoint 0.1 秒内从 3 变 2,Deployment 补一个新 Pod,被改标签的 Pod 脱离管理但继续运行。
在 kind 集群里对比注解与标签:注解总量上限 262144 字节(两个各 150000 的注解也超),标签值最多 63 字节;注解不能用 -l 或 --field-selector 选择;改 Deployment 自身的注解不触发滚动,改 Pod 模板里的注解就会新建 ReplicaSet(rollout restart 的原理);kubectl apply 会把整份配置抄进 last-applied-configuration 注解,约 300 KB 的 ConfigMap 用 client-side apply 因注解超限失败,create 和 server-side apply 成功。
在 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 等)创建时被拒。