Kubernetes 创建 Pod 底层原理
Kubelet 通过 gRPC 调用 CRI 插件(如 containerd/dockershim),插件将请求转换为 OCI 规范文件并调用 runc,runc 根据 OCI 配置通过 Linux 内核(cgroups/namespaces)创建容器。
流程简图:kubelet → gRPC → CRI插件 → OCI规范 → runc → 内核(cgroups/namespaces)→ 容器进程
1. Kubelet 发起 gRPC 请求(CRI 接口)
Kubelet 通过 Container Runtime Interface (CRI) 与容器运行时(如 containerd、CRI-O)通信。
gRPC 请求内容:
CreateContainerRequest(包含 Pod 配置、镜像、命令、资源限制等)。StartContainerRequest(启动容器)。
默认 CRI 服务端口:
containerd默认监听unix:///run/containerd/containerd.sock。
2. CRI 插件处理请求(以 containerd 为例)
containerd 架构:
CRI Plugin(
containerd/cri):解析 Kubelet 的 CRI 请求。containerd Core:管理镜像、容器生命周期。
Task Service:负责容器进程的创建。
关键步骤:
拉取镜像(如未本地存在):
调用
containerd的PullImage方法,从镜像仓库下载并存储到本地(OCI 格式)。
创建 OCI 运行时规范(config.json):
CRI 插件根据 Kubelet 请求生成 OCI 规范文件(定义
namespaces、cgroups、rootfs、mounts、env等)。
调用 Task Service:
通过
CreateTask请求,准备启动容器进程。
3. 调用 OCI 运行时(runc)
OCI 运行时(默认
runc)负责根据 OCI 规范创建容器:输入:
OCI 规范文件(
config.json)。
// /var/run/containerd/io.containerd.runtime.v2.task/default/<container-id>/config.json { "ociVersion": "1.0.2", "process": {"args": ["nginx"], "cwd": "/"}, "root": {"path": "rootfs"}, "linux": { "namespaces": [{"type": "pid"}, {"type": "network"}, ...], "cgroupsPath": "/kubepods.slice/nginx-pod", "resources": {"memory": {"limit": 536870912}} } }容器的根文件系统(
rootfs,通常来自镜像层)。
runc 执行流程:
runc create:解析config.json,创建容器环境。runc init(父进程):创建
namespaces(PID、Network、Mount 等)。设置
cgroups(CPU/Memory 限制)。挂载
rootfs(OverlayFS)。
runc start:启动容器内的ENTRYPOINT进程(如/bin/sh)。创建 containerd-shim:每个容器一个 shim 进程,作为容器的父进程,负责管理 runc 并收集退出状态。
4. 内核创建容器
Linux 内核机制:
Namespaces:
PID Namespace:隔离进程树。Network Namespace:隔离网络栈(veth pair 连接到 CNI)。Mount Namespace:独立文件系统视图。其他:UTS、IPC、User 等。
Cgroups v2:
通过
/sys/fs/cgroup/写入限制参数(如cpu.max、memory.high)。
OverlayFS:
联合挂载镜像层(
lowerdir、upperdir、merged)作为容器rootfs。
系统调用:
clone():创建隔离的进程(带CLONE_NEW*flags)。execve():执行容器入口进程。
5. 容器进程运行
容器进程在隔离的
namespaces和cgroups限制下运行。Kubelet 监控状态:
定期通过 CRI 的
ListContainers和ContainerStatus检查容器健康状态。
完整流程总结
Kubelet
→ gRPC (CRI) → containerd/cri
→ 生成 OCI spec (config.json)
→ containerd Task Service → runc
→ clone() + namespaces/cgroups
→ 容器进程启动关键组件关系图
+---------+ +------------+ +-------------+ +------+ +-------+
| Kubelet | → | containerd | → | OCI spec | → | runc | →. | Linux |
| (CRI) | | (CRI插件) | | (config.json)| | | | Kernel |
+---------+ +------------+ +-------------+ +------+ +-------+
| |
v v
cgroups/namespaces 容器进程通过这一流程,Kubernetes 最终在节点上创建一个符合 OCI 标准的隔离容器。
关键点总结:
解耦设计:CRI 是接口标准,containerd 是运行时引擎,runc 是 OCI 实现,各层职责清晰。
内核依赖:最终通过 Linux 内核的 命名空间(隔离)、cgroups(资源限制)、文件系统挂载(
overlayfs)实现容器化。shim 的作用:作为 runc 的父进程,确保容器退出后不会僵尸化,同时转发信号(如
SIGTERM)