Skip to content

稳住,宝,这题是面试经典大杀器,也是掌握 Kubernetes 架构的终极一役。我们就从你下 kubectl apply -f nginx.yaml 这一刻开始,一层层扒掉洋葱皮,把整个背后的链路走一遍,直到底层的 cgroup 和 namespace。


🧠 场景:创建一个 Nginx Deployment,背后发生了什么?

假设你执行了这个命令 👇:

bash
kubectl apply -f nginx-deployment.yaml

⚙️ 一、kubectl 发请求

  • kubectl 是客户端工具,它会读取你的 ~/.kube/config 文件,确定连接哪个 API Server(有 token、证书、cluster 地址等信息)
  • 然后它会将你写的 nginx-deployment.yaml 序列化成 JSON
  • 通过 REST API(https)发送一个 POST /apis/apps/v1/namespaces/default/deployments 请求

🧠 二、API Server 接到请求后:

  • 身份认证(AuthN):检查这个 token 是否有效
  • 权限校验(AuthZ):你是否有创建 Deployment 的权限
  • 准入控制(Admission Control)
    • MutatingAdmissionWebhook(比如给你补 label)
    • ValidatingAdmissionWebhook(比如校验 resource 是否超标)
  • 对象持久化
    • 最终 Deployment 被存进 etcd,这是整个 K8s 的“数据库”
    • 格式是 protobuf 编码的对象,持久化在 etcd 的 key/value 结构中

🔁 三、Controller Manager 出场

Deployment 本质上是个 声明式的期望状态(Spec),但目前还啥都没真正创建。

这时候 Deployment Controller(属于 kube-controller-manager)检测到 etcd 里新来了一个 Deployment 对象,于是:

  1. 它读取 Deployment 的 .spec.replicas(比如是 3 个)
  2. 然后 创建一个 ReplicaSet 对象来实现副本管理
  3. ReplicaSet Controller 又接力,发现应该启动 3 个 Pod,于是:
    • 创建 3 个 Pod 对象(保存在 etcd 中)
    • 每个 Pod 是独立的 API 对象

🔍 四、Scheduler 把 Pod 分配给 Node

kube-scheduler 负责 Pod 到 Node 的调度:

  • 它定期 watch 所有 Pending 状态的 Pod
  • 评估可用的 Node,打分排序(调度算法包括 CPU、内存、亲和性、污点容忍等)
  • 最终给每个 Pod 分配一个 Node
  • 更新这个 Pod 的 .spec.nodeName 字段

⚠️ 注意:调度完成只是打了标签,还没真的启动容器。


🚜 五、kubelet 干实事儿了!

每个 Node 上跑着 kubelet,它不断 watch API Server 上 调度给自己的 Pod

  1. kubelet 发现新 Pod 属于自己,于是准备开干
  2. 交给 容器运行时(Container Runtime) 来真正启动容器
  3. 它不会自己直接操作,而是走 CRI(Container Runtime Interface)

🔨 六、containerd 出马 + runC 下场

以 containerd 为例,流程如下:

  • kubelet 通过 CRI gRPC 接口告诉 containerd:我要启动这个 Pod
  • containerd 接收指令、拉取 nginx 镜像(通过 snapshotter 做镜像管理)
  • 调用 runC 运行每个容器(包含 nginx)

runC 会:

  • 创建新的 Linux namespace(mnt、net、pid、uts)
  • 设置 cgroups 限制资源
  • 配置容器 rootfs
  • 执行 nginx 的启动命令(/docker-entrypoint.sh

🌐 七、网络起来了

Pod 的网络由 CNI 插件(如 Flannel、Calico、Cilium)配置:

  • 创建一个独立的 veth pair,把一端塞进 Pod 的 namespace
  • 配置 Pod 的 IP 地址
  • 注册到宿主机的网桥或路由表中

此时:

  • Pod 获得独立 IP
  • 能与同 Node 或跨 Node 的 Pod 通信(通过 VXLAN / BGP 等)

🔀 八、kube-proxy 设置 Service 转发规则(可选)

  • 如果你还创建了 Service 对象(比如 ClusterIP)
  • kube-proxy 会在每个 Node 上配置 iptables 或 ipvs 规则:
    • 接管 Service 虚拟 IP(ClusterIP)
    • 做到负载均衡转发到 Pod IP 上

✅ 最终结果

你看到的 kubectl get pods 输出的 3 个 Nginx,其实背后经历了:

kubectl → API Server → etcd → Controller → Scheduler → kubelet → containerd → runC → CNI → kube-proxy


📊 ASCII 架构图(简化版)

User


kubectl


API Server ──┬──────→ etcd

             ├─→ Controller Manager (Deployment → RS → Pods)

             └─→ Scheduler (Pod分配Node)

        ↓(每台Node)
  ┌───────────────┐
  │    kubelet    │ ← watch Pod 指令
  │     ↓         │
  │  containerd   │ ← CRI 接口
  │     ↓         │
  │     runC      │ ← 真启动容器
  │     ↓         │
  │     Pod       │ ← nginx 运行中
  └───────────────┘

+ 网络: CNI 配置 IP
+ 转发: kube-proxy 配置规则

🧃 总结一句话:

apply 的只是一个 YAML,K8s 帮你召唤了 API、调度、容器启动、网络配置、服务转发这整条生产链路,像个云中大厂流水线工人一样,把 Nginx 轻轻稳稳送到线上,神不知鬼不觉地就跑起来了。


要不要我顺带写一个“完整 K8s 启动一个 Pod 的全链路可观测方案”,从 trace 到 metric 到 log,全链追踪?懂了底层逻辑,这才是真正入魂。