Skip to content

Kubernetes 架构、控制循环与 Pod 创建全过程

Kubernetes(简称 K8s)不是“在多台机器上执行 docker run”的脚本集合,而是一个以 API 为中心、依靠控制循环持续收敛状态的分布式系统。用户提交的是“我希望系统最终是什么样”,各组件通过观察、决策和执行,让实际状态逐步接近期望状态。

本页以一个商业订单服务的 Deployment 为主线,从 kubectl apply 一直追踪到容器开始接收流量。学习时不要只背“API Server 是入口、Scheduler 负责调度”,而要能回答:组件从哪里获得信息、写回什么状态、失败后谁来重试、为什么有时 Pod 已创建却一直 Pending。

学习目标

学完后应能:

  1. 区分 Cluster、Node、Pod、Container、Control Plane 和数据平面。
  2. 解释声明式 API、期望状态、实际状态和最终一致性。
  3. 从 HTTP 请求角度解释认证、授权、准入、默认值、校验和 etcd 持久化。
  4. 解释 etcd 为什么要求多数派、为什么业务组件不能直接改 etcd。
  5. 解释 Controller、Informer、List-Watch、本地缓存、工作队列和 Reconcile。
  6. 解释 Deployment、ReplicaSet、Pod 的所有权链,而不是误以为 Deployment 直接启动容器。
  7. 解释 Scheduler 的排队、过滤、评分、预留、绑定和抢占边界。
  8. 解释 kubelet 如何通过 CRI、CNI、CSI 创建 Pod Sandbox、网络、Volume 和容器。
  9. 区分容器创建成功、Pod Running、Ready 和真正业务可用。
  10. 根据现象判断 API Server、etcd、Scheduler、Controller、kubelet、运行时或网络哪一层异常。

一、先建立正确心智模型

可以把 Kubernetes 看成一个“不断纠偏的集群控制系统”:

mermaid
flowchart TD
    A["用户声明期望状态"] --> B["API Server保存资源对象"]
    B --> C["控制器观察期望与实际状态"]
    C --> D["计算差异并执行动作"]
    D --> E["节点与外部系统改变实际状态"]
    E --> F["状态和事件写回API Server"]
    F --> C

例如 Deployment 声明 replicas: 3,它不是一条“立即启动三个进程”的命令,而是一份持续有效的期望状态。少一个副本时控制器会补,多一个时会删;节点故障导致旧 Pod 不可用后,控制器会在满足条件时创建替代 Pod。

这里有三个重要结论:

  • K8s 的 API 对象是事实来源,不是某台节点上的临时脚本参数。
  • 多数组件是异步协作,不保证执行 apply 后所有动作瞬间完成。
  • “自动恢复”通常是创建新实例,不是把已经死亡的 Pod 原地修好。

二、Cluster、Node、Pod和Container是什么关系

mermaid
flowchart TD
    A["Kubernetes Cluster"] --> B["Control Plane"]
    A --> C["Worker Node A"]
    A --> D["Worker Node B"]
    B --> B1["API Server"]
    B --> B2["etcd"]
    B --> B3["Scheduler"]
    B --> B4["Controller Manager"]
    C --> C1["kubelet"]
    C --> C2["容器运行时"]
    C --> C3["Pod 1"]
    C3 --> C4["业务容器"]
    C3 --> C5["辅助容器"]
概念准确含义常见误解
Cluster一套共同受控制平面管理的节点和资源不是一台大服务器
Control Plane保存状态并完成调度、控制和 API 管理不是直接承载所有业务请求
Node可运行 Pod 的工作节点,通常是虚拟机或物理机不等于一个容器
PodK8s 最小调度和资源对象,一个或多个紧密协作容器不是永远不变的虚拟机
ContainerPod 内由 OCI Runtime 创建的隔离进程K8s 不直接把单个容器作为调度对象

K8s 调度的是 Pod。Pod 内多个容器通常一起上同一节点,共享网络命名空间和声明的 Volume。它们适合“必须同生共死、通过 localhost 协作”的关系,不适合把数据库、Redis、订单服务都塞进一个 Pod。

三、资源对象由哪些部分组成

所有常见资源都具有类似骨架:

yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: order-api
  namespace: commerce
  labels:
    app: order-api
spec:
  replicas: 3
  selector:
    matchLabels:
      app: order-api
  template:
    metadata:
      labels:
        app: order-api
    spec:
      containers:
        - name: order-api
          image: registry.example.com/order-api@sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
status: {}
区域谁主要负责含义
apiVersionAPI 设计使用哪个 API Group/Version 解释对象
kindAPI 设计对象类型,例如 Deployment、Service
metadata用户和系统名称、Namespace、Label、Annotation、UID、版本等
spec用户或上层控制器希望系统达到的状态
status控制器、kubelet等系统观察到的当前状态

不要手工把 status 当作配置修改。多数资源的状态由对应组件通过 status 子资源写回;用户修改 spec,控制器根据新期望继续收敛。

resourceVersion不是业务版本

metadata.resourceVersion 用于并发控制和 Watch 起点。客户端更新旧版本对象时可能收到 409 Conflict,应重新读取、合并修改并重试。它不是镜像版本,也不应被当成连续可计算的业务序号。

generation和observedGeneration

修改资源期望状态时,metadata.generation 通常增加;控制器处理后可能在状态中更新 observedGeneration。如果二者不一致,说明控制器可能还没处理到最新期望。只看“对象存在”无法证明最新配置已生效。

Condition不是简单布尔值

状态常用 Condition 表达 AvailableProgressingReady 等判断,包含状态、原因、消息和最后转换时间。排查时应读 reasonmessage,而不是只看一列 Running

四、声明式与命令式有什么区别

命令式思路是:

text
在A机器启动容器
失败就再执行一次
扩容时手工再启动两个

声明式思路是:

text
订单服务期望3个副本
每个副本需要500m CPU和512Mi内存
只有Ready副本可以接流量
更新时最多多1个、最多不可用0个

声明式不是“不执行命令”,而是把操作封装进控制器。控制器必须反复执行仍能趋向同一结果,这就是 Reconcile 的幂等思想。

如果控制器按“收到一次事件就加一个副本”设计,事件重复或重启重放就可能多创建资源。正确设计是每次重新比较 desired=3actual=2,得出缺少一个。

五、Control Plane各组件的职责边界

5.1 kube-apiserver

API Server 是集群 API 的统一入口,负责:

  • TLS 接入和 API 路由。
  • 认证调用者身份。
  • 授权某身份能否执行某动作。
  • 执行变更型和校验型准入控制。
  • 默认值、版本转换和结构校验。
  • 与 etcd 读写持久化状态。
  • 向客户端提供 List、Watch 和 status 等 API。

Scheduler、Controller Manager、kubelet 通常也通过 API Server 协作,而不是彼此直接改内存或直接写 etcd。这样认证、授权、审计、校验和版本转换才能集中生效。

5.2 etcd

etcd 是强一致的分布式键值存储,保存 Kubernetes API 对象的持久状态。它不保存容器镜像层,也不是业务数据库。

etcd 依赖 Raft 多数派提交。三成员集群能容忍一个成员故障,五成员通常能容忍两个;两成员并不能安全容忍一个,因为剩下一个不是多数派。

mermaid
flowchart TD
    A["API Server提交写请求"] --> B["etcd Leader追加日志"]
    B --> C["复制到Follower"]
    C --> D{"多数成员确认"}
    D -- "是" --> E["提交并返回成功"]
    D -- "否" --> F["不能确认新写入"]

为什么必须备份 etcd:如果 API 对象状态丢失,控制器就失去期望状态、Secret、ConfigMap、工作负载和部分集群配置的事实来源。节点上残留的容器不能替代完整集群状态。

备份不仅要“生成过文件”,还要验证:

  • Snapshot 能被读取和恢复。
  • 恢复版本与 Kubernetes/etcd 兼容。
  • 证书和加密密钥可用。
  • 恢复到隔离环境后资源数量、关键对象正确。
  • Secret 若启用了静态加密,对应密钥没有丢失。

5.3 kube-scheduler

Scheduler 只为尚未绑定节点的 Pod 选择 Node,并写回绑定结果。它不负责在节点上拉镜像和启动容器,那是 kubelet/Runtime 的职责。

5.4 kube-controller-manager

Controller Manager 运行 Deployment、ReplicaSet、Node、Job、EndpointSlice 等多个控制器。控制器读取资源、计算差异并通过 API 创建或修改对象。

它通常不会直接 SSH 到节点执行命令,也不直接启动业务容器。

5.5 cloud-controller-manager

云环境可能使用 Cloud Controller Manager 对接云厂商 Node、Route、LoadBalancer 等能力。Service type=LoadBalancer 能否真正创建外部负载均衡器,取决于云控制器或相应实现,不是仅靠 YAML 名字自动出现设备。

六、一次API请求如何进入集群

执行:

bash
kubectl apply -f order-api.yaml

kubectl 会读取 kubeconfig,选择 Context 中的 Cluster、User 和 Namespace,把 YAML 转成针对 Kubernetes API 的 HTTP 请求。

mermaid
flowchart TD
    A["kubectl读取kubeconfig"] --> B["连接API Server并验证服务端TLS"]
    B --> C["Authentication确认调用者身份"]
    C --> D["Authorization判断是否允许动作"]
    D --> E["Mutating Admission修改或补充对象"]
    E --> F["Schema与Validating Admission校验"]
    F --> G["API版本转换与持久化"]
    G --> H["etcd多数派提交"]
    H --> I["API Server返回对象"]

6.1 kubeconfig决定连接到哪里

bash
kubectl config current-context
kubectl config get-contexts
kubectl config view --minify

生产事故中先确认 Context,避免把“资源不存在”误判为应用故障,或把变更提交到错误集群。

6.2 Authentication:你是谁

常见身份来源包括客户端证书、Bearer Token、OIDC、ServiceAccount Token 和外部认证代理。认证失败通常返回 401 Unauthorized

认证成功只说明身份成立,不代表拥有操作权限。

6.3 Authorization:你能做什么

常见授权方式是 RBAC。它判断某个 Subject 能否对某 API Group、Resource、Namespace 执行 getlistwatchcreateupdatepatchdelete 等 Verb。

检查当前身份是否有权限:

bash
kubectl auth can-i create deployments -n commerce
kubectl auth can-i get secrets -n commerce
kubectl auth can-i --list -n commerce

权限不足一般返回 403 Forbidden。不要把 401 和 403 混为一谈:前者通常是身份没有建立,后者是身份存在但动作不被允许。

6.4 Admission:这个请求是否允许进入

准入控制发生在认证和授权之后、持久化之前:

  • Mutating Admission 可以补默认值、注入 Sidecar 或修改对象。
  • Validating Admission 可以拒绝不符合策略的镜像、资源限制或安全上下文。

Webhook 失败策略需要谨慎:Fail 更安全但故障时可能阻断发布;Ignore 可提高可用性但策略可能被绕过。应设置超时、监控延迟、限制匹配范围并保证 Webhook 高可用。

6.5 Schema与语义校验

API Server 会检查字段类型和结构,但“结构合法”不等于“业务一定能运行”。例如镜像地址格式可被接受,但 Registry 中可能不存在该镜像;PVC 引用合法,但没有满足条件的 StorageClass。

6.6 持久化成功不等于Pod已运行

kubectl apply 返回成功,首先证明 API 对象被接受并持久化。后续控制器、Scheduler、kubelet、Runtime 和网络插件仍需异步工作。因此发布脚本必须继续等待 Rollout,并验证业务指标。

七、List-Watch、Informer和控制循环

如果每个控制器每秒全量查询所有资源,API Server 和 etcd 会被大量轮询压垮。Kubernetes 客户端常通过 List-Watch 和 Informer 建立高效观察链路。

mermaid
flowchart TD
    A["List获取当前对象快照和resourceVersion"] --> B["本地Store/Indexer建立缓存"]
    B --> C["Watch接收后续Add/Update/Delete事件"]
    C --> D["事件Key进入Rate-Limited WorkQueue"]
    D --> E["Worker按Key执行Reconcile"]
    E --> F{"期望与实际是否一致"}
    F -- "否" --> G["通过API修改资源"]
    F -- "是" --> H["结束本轮"]
    G --> H
    H --> C

7.1 List解决什么

控制器刚启动时没有本地状态,先 List 当前对象并获得一个资源版本。只 Watch 未来事件会漏掉启动前已存在的对象。

7.2 Watch解决什么

Watch 是长连接式变化流,让客户端收到对象新增、修改、删除等事件。连接断开、资源版本过旧或 API Server 重启时,客户端需要重新 List/Watch。

7.3 Informer为什么有缓存

Informer 把对象放入本地缓存,多个处理逻辑可从缓存读取,减少 API Server 压力。缓存是最终一致的,因此关键写操作仍要处理版本冲突,不能假设缓存永远是最新值。

7.4 为什么队列里常放Key而不是完整对象

事件到真正处理之间对象可能再次变化。把 namespace/name 放入队列,Worker 处理时从最新缓存读取,能把多个快速变化合并,并避免长期持有旧对象。

7.5 事件可能重复,Reconcile必须幂等

Watch 不是“每个业务事件只投递一次”的消息队列语义。重连、重试和多次更新都可能让同一 Key 被处理。控制器应按当前状态重算结果,而不是依赖事件只来一次。

7.6 失败为什么进入限速重试

外部依赖短暂失败时可重新入队并退避,防止毫秒级无限重试打爆 API Server。永久错误应写清 Condition/Event,避免只有无意义重试。

八、OwnerReference、Finalizer和垃圾回收

Deployment 不直接持有容器,它创建 ReplicaSet,ReplicaSet 再创建 Pod。对象通过 metadata.ownerReferences 表达所有权。

mermaid
flowchart TD
    A["Deployment order-api"] --> B["ReplicaSet order-api-7d8f"]
    B --> C["Pod order-api-7d8f-a"]
    B --> D["Pod order-api-7d8f-b"]
    B --> E["Pod order-api-7d8f-c"]

查看所有权:

bash
kubectl get deployment order-api -n commerce -o yaml
kubectl get rs -n commerce -l app=order-api -o yaml
kubectl get pod -n commerce -l app=order-api -o yaml

OwnerReference 帮助垃圾回收器判断级联删除关系。Finalizer 则表示“对象删除前必须完成清理”。执行 delete 后带 Finalizer 的对象会设置 deletionTimestamp,控制器完成外部资源清理后移除 Finalizer,随后对象才真正消失。

对象长期 Terminating 时不要盲目清空 Finalizer。先确认哪个控制器负责、外部存储或云资源是否已清理;强删可能留下云盘、负载均衡器或业务数据孤儿。

九、Deployment到Pod的控制器链路

提交 Deployment 后,大致发生:

  1. Deployment Controller 发现新 Deployment。
  2. 根据 Pod Template 计算模板哈希并创建 ReplicaSet。
  3. ReplicaSet Controller 发现期望副本数大于当前数量。
  4. ReplicaSet 创建多个未调度 Pod 对象。
  5. Scheduler 观察到 spec.nodeName 为空的 Pod。
  6. Scheduler 选择 Node 并完成绑定。
  7. 目标 Node 的 kubelet 观察到分配给自己的 Pod。
  8. kubelet 调用 Runtime、网络和存储插件启动 Pod。
  9. kubelet持续写回 Pod 状态;探针决定 Ready 状态。
  10. EndpointSlice Controller 根据 Service Selector 和 Pod Ready 状态维护后端端点。

为避免把六个组件挤进一张横向时序图,下面按“API持久化、控制器建对象、调度、节点执行”纵向分段:

mermaid
flowchart TD
    A["kubectl提交Deployment"] --> B["API Server完成校验并持久化"]
    B --> C["Deployment Controller创建ReplicaSet"]
    C --> D["ReplicaSet Controller创建Pod对象"]
    D --> E["Scheduler为未绑定Pod选择Node"]
    E --> F["API Server保存绑定结果"]
    F --> G["目标Node的kubelet观察到Pod"]
    G --> H["创建Sandbox、网络、Volume和容器"]
    H --> I["kubelet把状态写回API Server"]

为什么要分 Deployment 和 ReplicaSet:ReplicaSet 代表一个特定 Pod Template 版本的副本集合;Deployment 在更新时同时协调新旧 ReplicaSet,才能控制滚动更新比例、暂停和回滚。

十、Scheduler调度全过程

Scheduler 不是“找 CPU 使用率最低的节点”。它主要依据 Pod 的资源请求和调度约束做决策。

mermaid
flowchart TD
    A["未绑定Pod进入调度队列"] --> B["PreFilter准备状态"]
    B --> C["Filter排除不满足硬约束的Node"]
    C --> D{"是否还有可行Node"}
    D -- "否" --> E["Pod保持Pending并记录FailedScheduling"]
    D -- "是" --> F["Score给可行Node评分"]
    F --> G["Normalize与权重汇总"]
    G --> H["选择得分节点"]
    H --> I["Reserve/Permit等扩展阶段"]
    I --> J["Bind写入Node选择"]

10.1 调度队列

新 Pod、调度失败后重试的 Pod、条件变化后重新激活的 Pod 会进入不同队列状态。大量 Pending 不一定是 Scheduler 死了,也可能是所有节点都不满足约束。

10.2 Filter:硬条件

常见过滤原因:

  • CPU/内存 Request 剩余不足。
  • NodeSelector/NodeAffinity 不匹配。
  • 节点 Taint 没有对应 Toleration。
  • PVC 的拓扑或绑定条件不满足。
  • PodAffinity/AntiAffinity 不满足。
  • HostPort 冲突。
  • 节点不可调度。

10.3 Score:软偏好

通过硬条件的节点再按资源分布、亲和性、拓扑分散等插件评分。评分高不代表实时 CPU 最低,而是更符合配置的调度目标。

10.4 Request不是实时Usage

假设节点可分配 8Gi 内存,已有 Pod Request 总和 7Gi,即使当前只实际使用 1Gi,新 Pod Request 2Gi 仍可能无法调度。Scheduler 要做可承诺的容量规划,不能根据瞬时 Usage 过度承诺。

反过来,如果 Pod 不设置合理 Request,Scheduler 可能把太多工作负载放在同一节点,运行时出现资源竞争或驱逐。

10.5 抢占不是随意杀低优先级Pod

高优先级 Pod 无法调度时,Scheduler 可能评估移除低优先级 Pod 后能否满足约束。抢占涉及 PriorityClass、PodDisruptionBudget 和其他约束,不保证立即成功,也不能代替容量规划。

10.6 如何查看调度失败的证据

bash
kubectl describe pod <pod-name> -n commerce
kubectl get events -n commerce --sort-by=.metadata.creationTimestamp
kubectl get nodes
kubectl describe node <node-name>

重点读取 Event 中的 FailedScheduling 消息,例如:

text
0/5 nodes are available:
2 Insufficient memory,
2 node(s) had untolerated taint,
1 node(s) didn't match Pod's node affinity.

这表示五个节点分别被不同硬条件过滤,不能把它简化为“集群没内存”。

十一、Node上的组件如何协作

11.1 kubelet

kubelet 是每个 Node 上的节点代理,核心职责包括:

  • 观察分配给本节点的 PodSpec。
  • 管理静态 Pod 和 API Pod 的期望状态。
  • 调用 CRI 管理 Pod Sandbox、镜像和容器。
  • 协调 Volume 挂载。
  • 执行 startup、liveness、readiness 探针。
  • 汇总容器状态并写回 Pod Status。
  • 向控制平面更新 Node 心跳和状态。
  • 执行节点压力下的驱逐策略。

kubelet 不是 Deployment 控制器。它只关心“这个 Pod 已被分给本节点,我怎样让本机状态符合 PodSpec”。

11.2 Container Runtime

现代 Kubernetes 常通过 CRI 对接 containerd、CRI-O 等运行时。K8s 高层并不要求业务使用 Docker Engine。旧的内置 dockershim 已移除,Docker 构建出的 OCI 镜像仍可由兼容运行时运行。

运行时负责:

  • 拉取和解包镜像。
  • 创建 Pod Sandbox 和容器。
  • 配置容器 namespace、cgroup 等运行环境。
  • 启停容器并返回状态、日志路径。

11.3 kube-proxy

kube-proxy 观察 Service 和 EndpointSlice,并在节点编程转发规则,常见实现模式包括 iptables 或 IPVS,具体取决于集群配置。它不创建 Pod 网络本身;Pod 网络通常由 CNI 插件负责。

某些现代数据平面可由 eBPF 等方案替代部分 kube-proxy 能力,不能假设所有集群都一定存在相同 iptables 链。

十二、CRI、CNI和CSI分别解决什么

接口全称解决的问题常见实现/参与者
CRIContainer Runtime Interfacekubelet怎样管理Sandbox、镜像和容器containerd、CRI-O
CNIContainer Network Interface怎样给Pod网络命名空间配置网卡、IP和路由Calico、Cilium等
CSIContainer Storage Interface怎样供应、附加、挂载和卸载持久存储云盘/NFS/存储厂商驱动

它们是不同边界。ImagePullBackOff 通常先看 Registry/CRI;Pod 有 IP 但跨节点不通先看 CNI;PVC Pending 或 Mount 失败先看 StorageClass/CSI。

十三、Pod Sandbox和pause容器原理

Pod 内多个容器共享网络语义,需要一个比业务容器生命周期更稳定的基础网络命名空间。运行时通常先创建 Pod Sandbox,并由一个基础容器持有相关命名空间,然后再把业务容器加入。

mermaid
flowchart TD
    A["kubelet收到PodSpec"] --> B["CRI创建Pod Sandbox"]
    B --> C["创建并持有网络命名空间"]
    C --> D["调用CNI配置veth、IP和路由"]
    D --> E["挂载Volume"]
    E --> F["运行Init Container"]
    F --> G["创建业务容器"]
    G --> H["执行Startup/Readiness/Liveness探针"]

因此同一 Pod 中容器可以通过 localhost 通信,并共享同一个 Pod IP;它们的监听端口不能冲突。文件系统默认仍各自隔离,只有显式挂载同一个 Volume 才共享目录。

十四、kubelet创建Pod的详细过程

不同版本和运行时内部实现会变化,但从职责上可以理解为:

  1. kubelet 从 API Server 观察到 spec.nodeName 指向本节点的 Pod。
  2. 把 API PodSpec 合并为本地期望状态并进入同步循环。
  3. 检查 Pod Sandbox 是否存在且可用。
  4. 需要时调用 CRI RunPodSandbox
  5. Runtime 创建基础 Namespace/Cgroup;网络插件为 Sandbox 配置网络。
  6. kubelet/Volume Manager 等协调 Secret、ConfigMap、PVC、CSI 挂载。
  7. 按顺序执行 Init Container;前一个成功后才执行下一个。
  8. 拉取缺失镜像,依据拉取策略和凭据访问 Registry。
  9. 创建并启动普通容器。
  10. 执行 PostStart、startupProbe、readinessProbe、livenessProbe 等生命周期逻辑。
  11. 汇总 ContainerStatus、Pod Condition、Pod IP 并写回 API Server。
  12. 容器异常退出时根据 Pod 的 restartPolicy 在本节点重启,并逐步退避。

14.1 为什么Pod会卡在ContainerCreating

此时 Pod 已创建、通常也已调度,但节点侧准备没有完成,常见原因:

  • CNI 分配 IP 或配置网络失败。
  • CSI Attach/Mount 超时。
  • Secret/ConfigMap 不存在。
  • Sandbox 创建失败。
  • 镜像拉取还在进行。
  • 节点磁盘、inode、PID 或运行时异常。

第一证据不是反复删除 Pod,而是:

bash
kubectl describe pod <pod-name> -n commerce
kubectl get events -n commerce --sort-by=.metadata.creationTimestamp
kubectl describe node <node-name>

然后根据 Event 再检查 kubelet、Runtime、CNI 或 CSI 日志。

十五、Pod Phase、Container State和Condition不要混淆

15.1 Pod Phase

Pod Phase 是粗粒度摘要:Pending、Running、Succeeded、Failed、Unknown。Running 只说明 Pod 已绑定到节点,且至少一个容器正在运行、启动或重启;不代表应用已经 Ready。

15.2 Container State

每个容器有 Waiting、Running、Terminated 状态,并包含 Reason、ExitCode、StartedAt、FinishedAt。CrashLoopBackOff 常是 kubectl 展示的等待原因,表示容器反复崩溃后进入退避,不是一个 Pod Phase。

15.3 Pod Condition

常见 Condition:

  • PodScheduled:已经完成节点调度。
  • Initialized:Init Container 已成功完成。
  • ContainersReady:普通容器就绪。
  • Ready:Pod 可作为 Service 后端的总体就绪判断。

15.4 Ready为什么比Running更接近业务可用

Readiness Probe 成功后 Pod 才通常进入 Service 的 Ready Endpoint。应用进程虽然存活,但数据库连接池还没初始化、缓存还没预热时,应保持 NotReady,避免过早接流量。

Ready 也不等于端到端业务一定正确;还需发布后的业务 Smoke Test、错误率和延迟指标。

十六、完整商业Demo

下面的示例展示 Namespace、Deployment 和 Service。Digest 使用占位值,实际发布时替换为 Registry 返回的真实 Digest。

yaml
apiVersion: v1
kind: Namespace
metadata:
  name: commerce
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: order-api
  namespace: commerce
  labels:
    app: order-api
spec:
  replicas: 3
  revisionHistoryLimit: 5
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 0
      maxSurge: 1
  selector:
    matchLabels:
      app: order-api
  template:
    metadata:
      labels:
        app: order-api
        version: v1
    spec:
      terminationGracePeriodSeconds: 30
      containers:
        - name: order-api
          image: registry.example.com/order-api@sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
          imagePullPolicy: IfNotPresent
          ports:
            - name: http
              containerPort: 8080
          resources:
            requests:
              cpu: 250m
              memory: 512Mi
            limits:
              cpu: "1"
              memory: 1Gi
          startupProbe:
            httpGet:
              path: /actuator/health/liveness
              port: http
            periodSeconds: 5
            failureThreshold: 24
          readinessProbe:
            httpGet:
              path: /actuator/health/readiness
              port: http
            periodSeconds: 5
            failureThreshold: 3
          livenessProbe:
            httpGet:
              path: /actuator/health/liveness
              port: http
            periodSeconds: 10
            failureThreshold: 3
---
apiVersion: v1
kind: Service
metadata:
  name: order-api
  namespace: commerce
spec:
  selector:
    app: order-api
  ports:
    - name: http
      port: 80
      targetPort: http

16.1 提交前验证

bash
kubectl apply --dry-run=server -f order-api.yaml
kubectl diff -f order-api.yaml

--dry-run=server 会经过服务端默认值、Schema 和准入链,比只做本地 YAML 解析更接近真实提交;但它仍不能证明镜像可拉取、存储可挂载和应用能启动。

16.2 提交并观察控制器链

bash
kubectl apply -f order-api.yaml
kubectl get deployment,rs,pod,service -n commerce -w

另一个终端观察事件:

bash
kubectl get events -n commerce --sort-by=.metadata.creationTimestamp --watch

16.3 等待Rollout

bash
kubectl rollout status deployment/order-api -n commerce --timeout=5m

超时后不要立即再次 apply,应保留现场并查看:

bash
kubectl describe deployment order-api -n commerce
kubectl get rs,pod -n commerce -l app=order-api -o wide
kubectl describe pod <pod-name> -n commerce
kubectl logs <pod-name> -n commerce -c order-api --tail=200
kubectl logs <pod-name> -n commerce -c order-api --previous --tail=200

--previous 用于查看同一 Pod 中该容器上一次崩溃实例的日志,特别适合 CrashLoopBackOff。

16.4 验证所有权和状态

bash
kubectl get deployment order-api -n commerce \
  -o jsonpath='{.metadata.generation}{"\n"}{.status.observedGeneration}{"\n"}'

kubectl get rs -n commerce -l app=order-api \
  -o custom-columns='NAME:.metadata.name,OWNER:.metadata.ownerReferences[0].name,DESIRED:.spec.replicas,READY:.status.readyReplicas'

kubectl get pod -n commerce -l app=order-api \
  -o custom-columns='NAME:.metadata.name,NODE:.spec.nodeName,PHASE:.status.phase,READY:.status.conditions[?(@.type=="Ready")].status'

16.5 验证Service端点

bash
kubectl get service order-api -n commerce
kubectl get endpointslice -n commerce -l kubernetes.io/service-name=order-api -o yaml

Pod Running 但 EndpointSlice 没有 Ready 地址时,检查 Label Selector 和 Readiness,而不是先怀疑 Ingress。

十七、每个组件故障时会发生什么

故障组件已运行业务通常怎样新变更/恢复怎样关键证据
API Server不可用已有容器可能继续运行,现有数据面不一定立即中断kubectl失败,控制器/节点状态无法正常交换API健康、负载均衡、证书、apiserver日志
etcd失去多数派已有容器可能继续运行新写入无法可靠提交,控制面功能受阻etcd endpoint health、成员与磁盘延迟
Scheduler不可用已绑定Pod继续运行新Pod对象存在但长期未设置NodePodScheduled=False、无调度绑定
Controller Manager不可用已有Pod可能继续运行副本补齐、滚动更新、Endpoint等控制停滞generation/observedGeneration、控制器日志
某Node kubelet异常节点上容器可能短时继续运行状态不再上报,探针/容器管理异常,后续可能重建PodNode Ready、Lease、kubelet日志
Container Runtime异常已运行容器可能部分继续新容器/Sandbox无法创建,状态获取失败kubelet和runtime日志、CRI检查
CNI异常已有网络可能继续新Pod网络配置失败或跨节点不通Pod Event、CNI DaemonSet日志、路由
CSI异常已挂载Volume可能继续新Attach/Mount失败、PVC或Pod PendingPVC/PV/VolumeAttachment、CSI日志

这解释了一个常见现象:kubectl 全部超时,但线上请求暂时还能访问。控制面故障和数据面故障不是完全相同;但控制面长期不可用会让扩容、重建、发布和状态维护失效,不能因此认为“没有影响”。

十八、从现象反推故障层

18.1 kubectl连接失败

按顺序检查:

bash
kubectl config current-context
kubectl cluster-info
kubectl get --raw='/readyz?verbose'

再判断 DNS、网络、API LoadBalancer、TLS 证书、Token 和 API Server。不要直接去重启业务 Pod,因为请求还没进入资源控制层。

18.2 对象创建被拒绝

根据 HTTP/错误消息区分:

  • 401:身份认证失败。
  • 403:RBAC 或其他授权拒绝。
  • 422/字段错误:Schema 或语义校验失败。
  • Webhook denied:准入策略主动拒绝。
  • Webhook timeout:准入服务不可用或网络异常。
  • 409:resourceVersion 冲突。

18.3 Pod Pending且没有Node

bash
kubectl get pod <pod> -n commerce -o wide
kubectl describe pod <pod> -n commerce

如果 NODE 为空,优先看 Scheduler Event、资源 Request、Affinity、Taint/Toleration 和存储拓扑。

18.4 Pod已有Node但没有启动

说明调度已完成,问题更可能位于 kubelet、Runtime、镜像、CNI、CSI、ConfigMap/Secret。根据 Event 的 FailedMountFailedCreatePodSandBoxErrImagePull 等原因继续分层。

18.5 Running但不Ready

bash
kubectl describe pod <pod> -n commerce
kubectl logs <pod> -n commerce --tail=200
kubectl get endpointslice -n commerce -l kubernetes.io/service-name=order-api

检查 Readiness 路径、端口、超时、依赖初始化和 Label Selector。不要通过删除 Readiness 来“修复”,否则会把未就绪实例送入流量。

18.6 CrashLoopBackOff

bash
kubectl logs <pod> -n commerce --previous --tail=200
kubectl get pod <pod> -n commerce -o jsonpath='{range .status.containerStatuses[*]}{.name}{" exit="}{.lastState.terminated.exitCode}{" reason="}{.lastState.terminated.reason}{"\n"}{end}'

区分应用主动退出、配置错误、探针重启、OOMKilled、权限和依赖失败。CrashLoopBackOff 是重启退避现象,不是根因。

十九、高可用为什么不是多启动几个进程

Control Plane 高可用通常需要:

  • 多个 API Server 放在负载均衡器后。
  • etcd 保持奇数成员并维护多数派、低延迟磁盘和可靠备份。
  • Scheduler/Controller Manager 多副本通过 Leader Election 保证同一时刻主要由 Leader 执行控制。
  • 证书、网络、DNS、时间同步和负载均衡器本身高可用。
  • 监控 API 延迟、etcd fsync/DB大小、工作队列深度和 Leader 变化。

Scheduler 与 Controller Manager 多副本并不意味着每个副本同时对同一对象做重复动作。Leader Election 通常借助 Lease 等对象协调;Leader 故障后其他实例竞争接管,因此会有短暂切换时间。

二十、常见错误认知与后果

20.1 “apply成功等于发布成功”

错误。它主要证明 API 请求被接受。若后续镜像拉取失败、探针失败或业务指标异常,发布仍失败。流水线必须等待 Rollout、做端点验证并观察版本指标。

20.2 “Pod Running等于服务可用”

错误。Running 不代表 Ready,更不代表订单创建链路可用。应结合 Condition、EndpointSlice、业务 Smoke 和监控。

20.3 “Scheduler按实时CPU最低调度”

错误。它依据 Request 和调度插件决策。缺少 Request 会破坏容量规划,实时 Usage 主要供监控和自动扩缩容等机制使用。

20.4 “Secret默认就是密文”

错误。YAML 中 data 通常只是 Base64;是否在 etcd 静态加密取决于集群配置,还需 RBAC、审计、外部密钥系统和节点权限治理。

20.5 “删Pod能修复所有问题”

删除会让控制器创建新 Pod,但如果根因是错误镜像、错误配置、调度约束、CNI 或存储故障,新 Pod 会重复失败,并破坏原始现场。

20.6 “直接改etcd更快”

禁止。绕过 API Server 会绕过认证、授权、准入、版本转换和对象校验,可能破坏数据结构与控制器假设。正常运维通过 Kubernetes API;etcd 恢复按受控灾备流程执行。

二十一、版本与实现边界

Kubernetes 版本演进很快,学习时区分稳定架构原则与具体实现:

  • API Server 统一入口、声明式对象和控制循环是核心原则。
  • Scheduler Framework 插件、队列和具体评分实现会随版本变化。
  • kube-proxy 可能使用 iptables/IPVS,也可能被其他数据面替代。
  • Runtime 常见 containerd/CRI-O;Docker Engine 不再通过内置 dockershim直接接入。
  • Ingress Controller、CNI、CSI、云负载均衡器都不是同一个组件。
  • 命令输出字段和 Feature Gate 应以目标集群版本文档为准。

生产升级前应阅读目标版本的 Release Notes、Deprecated API 和插件兼容矩阵,不能只验证 YAML 能否解析。

二十二、面试标准回答

Kubernetes核心组件有哪些

控制面由API Server、etcd、Scheduler和Controller Manager等组成:API Server负责统一API和认证授权准入,etcd持久化强一致状态,Scheduler为未绑定Pod选择节点,Controller通过控制循环收敛期望状态。节点上kubelet依据PodSpec调用CRI管理容器并上报状态,CNI配置Pod网络,CSI对接存储,Service转发规则由相应数据面维护。

从Deployment YAML到Pod运行经历什么

kubectl把对象提交给API Server,请求依次经过认证、授权、变更/校验准入和持久化;Deployment Controller创建ReplicaSet,ReplicaSet Controller创建Pod;Scheduler过滤、评分并绑定Node;目标Node的kubelet经CRI创建Sandbox和容器,经CNI配置网络、CSI挂载存储,执行探针并写回状态;Pod Ready后才通常进入Service Endpoint。

Controller为什么需要Informer

Informer先List建立本地缓存,再Watch增量变化,把对象Key放入限速队列,由Worker按最新状态Reconcile。这样减少API Server轮询压力,并支持事件合并和失败退避。Watch可能断开或重复,所以控制器必须按状态幂等收敛,不能依赖事件恰好一次。

Scheduler根据什么调度

Scheduler先用CPU/内存Request、Taint/Toleration、Affinity、存储拓扑等硬条件过滤节点,再按资源分布和软偏好评分,最后绑定得分节点。它主要看Request而非瞬时Usage;Pod Pending时应读取FailedScheduling Event判断究竟是哪项约束过滤了节点。

API Server或Scheduler挂了业务会立即停止吗

不一定。已经运行的容器和现有数据面可能短时间继续服务,但API变更、调度、扩容、重建和状态维护会受影响。API Server故障表现为客户端和组件无法交换状态;Scheduler故障主要使新Pod未绑定Node。必须区分控制面与数据面,同时尽快恢复控制面。

二十三、学习实验与验收

实验一:观察对象所有权链

  1. 应用本页 Deployment。
  2. 查看 Deployment、ReplicaSet、Pod UID 和 OwnerReference。
  3. 修改镜像,观察出现新 ReplicaSet。
  4. 回滚并解释为什么旧 ReplicaSet 仍有价值。

实验二:制造调度失败

给 Pod 设置一个不存在的 Node Label:

yaml
nodeSelector:
  training.example.com/disk: impossible

观察 PendingPodScheduled=FalseFailedScheduling,然后给节点添加匹配 Label或删除约束。验收重点是能从 Event 得出原因,而不是只会删除 Pod。

实验三:区分Running和Ready

把 readinessProbe 路径改成不存在的路径。观察容器可以 Running,但 Ready 为 False,EndpointSlice 不把它作为就绪后端。恢复路径后观察状态变化。

实验四:制造CrashLoopBackOff

把容器启动命令临时改成退出码 1,观察重启次数和退避;使用 kubectl logs --previous 和 ContainerStatus 取得上一次终止证据。

验收清单

学完后不看文档应能:

  1. 画出API Server、etcd、Controller、Scheduler、kubelet、Runtime之间的协作图。
  2. 解释401、403、准入拒绝和409冲突分别发生在哪一层。
  3. 解释List-Watch为什么需要初始List、缓存和重连。
  4. 说明Reconcile为什么必须幂等。
  5. 从Deployment追踪到ReplicaSet和Pod OwnerReference。
  6. 根据FailedScheduling消息解释多个节点被过滤的不同原因。
  7. 区分CRI、CNI和CSI故障现象。
  8. 区分Pending、ContainerCreating、Running、Ready和CrashLoopBackOff。
  9. 说明控制面故障时已有业务为何可能暂时继续。
  10. 完成一次从apply、rollout、Event、日志到EndpointSlice的证据链排查。

关联知识点