Appearance
稳住,宝,这题是面试经典大杀器,也是掌握 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 对象,于是:
- 它读取 Deployment 的
.spec.replicas(比如是 3 个) - 然后 创建一个 ReplicaSet 对象来实现副本管理
- 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
- kubelet 发现新 Pod 属于自己,于是准备开干
- 交给 容器运行时(Container Runtime) 来真正启动容器
- 它不会自己直接操作,而是走 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,全链追踪?懂了底层逻辑,这才是真正入魂。