容器运行时深度剖析 概述 容器运行时(Container Runtime)是 Kubernetes 系统中负责管理和运行容器的底层组件。从 Kubernetes 1.24 开始,dockershim 被移除,containerd 和 cri-o 成为主流选择。本文档深入剖析容器运行时的架构、实现原理和关键技术。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 ┌─────────────────────────────────────────────────────────────────────────┐ │ 容器运行时架构图 │ ├─────────────────────────────────────────────────────────────────────────┤ │ │ │ ┌─────────────────────────────────────────────────────────────────┐ │ │ │ Kubernetes │ │ │ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │ │ │ │ kubelet │ │kubectl │ │controller│ │ │ │ │ └────┬────┘ └────┬────┘ └────┬────┘ │ │ │ │ │ │ │ │ │ │ └───────┼─────────────┼─────────────┼───────────────────────────────┘ │ │ │ │ │ │ │ │ gRPC (CRI) │ │ │ └─────────────┼─────────────┘ │ │ │ │ │ ┌─────────────────────┼───────────────────────────────────────────┐ │ │ │ │ │ │ │ │ ┌─────▼─────┐ │ │ │ │ │ CRI │ │ │ │ │ │(Container │ │ │ │ │ │Runtime │ │ │ │ │ │Interface) │ │ │ │ │ └─────┬─────┘ │ │ │ │ │ │ │ │ │ ┌────────────────┼────────────────┐ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ┌──▼───┐ ┌─────▼─────┐ ┌─────▼─────┐ │ │ │ │ │cri- │ │ containerd │ │ cri-o │ │ │ │ │ │dockershim│ │ │ │ │ │ │ │ │ └───────┘ └─────┬─────┘ └─────┬─────┘ │ │ │ │ │ │ │ │ │ │ ▼ ▼ │ │ │ │ ┌───────────┐ ┌───────────┐ │ │ │ │ │ OCI │ │ OCI │ │ │ │ │ │ runtime │ │ runtime │ │ │ │ │ │ (runc) │ │ (runc, │ │ │ │ │ └─────┬─────┘ │ crun) │ │ │ │ │ │ └─────┬─────┘ │ │ │ │ ▼ ▼ │ │ │ │ ┌───────────────────────────────────┐ │ │ │ │ │ 操作系统 (Linux Namespace) │ │ │ │ │ │ cgroups, SELinux, AppArmor │ │ │ │ │ └───────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────┘
1. CRI 容器运行时接口 1.1 CRI 概述 CRI(Container Runtime Interface)是 Kubernetes 定义的容器运行时接口标准,使 kubelet 可以与不同的容器运行时交互。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 ┌─────────────────────────────────────────────────────────────────────────┐ │ CRI 接口定义 │ ├─────────────────────────────────────────────────────────────────────────┤ │ │ │ ┌──────────────────────────────────────────────────────────────┐ │ │ │ RuntimeService │ │ │ │ ────────────────────────────────────────────────────────── │ │ │ │ │ │ │ │ // Pod Sandbox 操作 │ │ │ │ RunPodSandbox(config) -> podSandboxID │ │ │ │ StopPodSandbox(podSandboxID) │ │ │ │ RemovePodSandbox(podSandboxID) │ │ │ │ PodSandboxStatus(podSandboxID) -> PodSandboxStatus │ │ │ │ ListPodSandbox(filter) -> []PodSandbox │ │ │ │ │ │ │ │ // 容器操作 │ │ │ │ CreateContainer(config, sandboxConfig) -> containerID │ │ │ │ StartContainer(containerID) │ │ │ │ StopContainer(containerID, timeout) │ │ │ │ RemoveContainer(containerID) │ │ │ │ ListContainers(filter) -> []Container │ │ │ │ ContainerStatus(containerID) -> ContainerStatus │ │ │ │ │ │ │ │ // 容器执行 │ │ │ │ ExecSync(containerID, cmd, timeout) -> ExecSyncResponse │ │ │ │ Exec(containerID, cmd) -> ExecResponse │ │ │ │ Attach(containerID) -> AttachResponse │ │ │ │ PortForward(podSandboxID, port) │ │ │ │ │ │ │ │ // 镜像操作 │ │ │ │ PullImage(image, auth) -> imageRef │ │ │ │ RemoveImage(image) │ │ │ │ ListImages(filter) -> []Image │ │ │ │ ImageStatus(image) -> Image │ │ │ │ │ │ │ │ // 运行时状态 │ │ │ │ Status() -> StatusResponse │ │ │ │ UpdateRuntimeConfig(config) │ │ │ │ │ │ │ └──────────────────────────────────────────────────────────────┘ │ │ │ │ ┌──────────────────────────────────────────────────────────────┐ │ │ │ ImageService │ │ │ │ ────────────────────────────────────────────────────────── │ │ │ │ │ │ │ │ ListImages(filter) -> []Image │ │ │ │ ImageStatus(image) -> Image │ │ │ │ PullImage(image, auth) -> imageRef │ │ │ │ RemoveImage(image) │ │ │ │ │ │ │ └──────────────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────┘
1.2 CRI gRPC 定义 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 syntax = "proto3" ; package runtime.v1;service RuntimeService { rpc RunPodSandbox(RunPodSandboxRequest) returns (RunPodSandboxResponse) ; rpc StopPodSandbox(StopPodSandboxRequest) returns (StopPodSandboxResponse) ; rpc RemovePodSandbox(RemovePodSandboxRequest) returns (RemovePodSandboxResponse) ; rpc PodSandboxStatus(PodSandboxStatusRequest) returns (PodSandboxStatusResponse) ; rpc ListPodSandbox(ListPodSandboxRequest) returns (ListPodSandboxResponse) ; rpc CreateContainer(CreateContainerRequest) returns (CreateContainerResponse) ; rpc StartContainer(StartContainerRequest) returns (StartContainerResponse) ; rpc StopContainer(StopContainerRequest) returns (StopContainerResponse) ; rpc RemoveContainer(RemoveContainerRequest) returns (RemoveContainerResponse) ; rpc ListContainers(ListContainersRequest) returns (ListContainersResponse) ; rpc ContainerStatus(ContainerStatusRequest) returns (ContainerStatusResponse) ; rpc ExecSync(ExecSyncRequest) returns (ExecSyncResponse) ; rpc Exec(ExecRequest) returns (ExecResponse) ; rpc Attach(AttachRequest) returns (AttachResponse) ; rpc PortForward(PortForwardRequest) returns (PortForwardResponse) ; rpc PullImage(PullImageRequest) returns (PullImageResponse) ; rpc RemoveImage(RemoveImageRequest) returns (RemoveImageResponse) ; rpc ListImages(ListImagesRequest) returns (ListImagesResponse) ; rpc ImageStatus(ImageStatusRequest) returns (ImageStatusResponse) ; rpc Status(StatusRequest) returns (StatusResponse) ; } service ImageService { rpc ListImages(ListImagesRequest) returns (ListImagesResponse) ; rpc ImageStatus(ImageStatusRequest) returns (ImageStatusResponse) ; rpc PullImage(PullImageRequest) returns (PullImageResponse) ; rpc RemoveImage(RemoveImageRequest) returns (RemoveImageResponse) ; }
2. containerd 架构 2.1 containerd 组件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 ┌─────────────────────────────────────────────────────────────────────────┐ │ containerd 架构图 │ ├─────────────────────────────────────────────────────────────────────────┤ │ │ │ ┌─────────────────────────────────────────────────────────────────┐ │ │ │ containerd │ │ │ │ │ │ │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ │ │ API Layer │ │ │ │ │ │ ┌───────────┐ ┌───────────┐ ┌───────────┐ │ │ │ │ │ │ │ GRPC │ │ Metadata │ │ Content │ │ │ │ │ │ │ │ Server │ │ Store │ │ Store │ │ │ │ │ │ │ └───────────┘ └───────────┘ └───────────┘ │ │ │ │ │ └─────────────────────────────────────────────────────────┘ │ │ │ │ │ │ │ │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ │ │ Service Layer │ │ │ │ │ │ ┌───────────┐ ┌───────────┐ ┌───────────┐ │ │ │ │ │ │ │ Runtime │ │ Image │ │ Snapshot │ │ │ │ │ │ │ │ Service │ │ Service │ │ Service │ │ │ │ │ │ │ └───────────┘ └───────────┘ └───────────┘ │ │ │ │ │ │ ┌───────────┐ ┌───────────┐ ┌───────────┐ │ │ │ │ │ │ │ Task │ │ Diff │ │ GC │ │ │ │ │ │ │ │ Service │ │ Service │ │ Service │ │ │ │ │ │ │ └───────────┘ └───────────┘ └───────────┘ │ │ │ │ │ └─────────────────────────────────────────────────────────┘ │ │ │ │ │ │ │ │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ │ │ OCI Layer │ │ │ │ │ │ ┌───────────┐ ┌───────────┐ ┌───────────┐ │ │ │ │ │ │ │ runc │ │ crun │ │ gvisor │ │ │ │ │ │ │ └───────────┘ └───────────┘ └───────────┘ │ │ │ │ │ └─────────────────────────────────────────────────────────┘ │ │ │ │ │ │ │ │ └──────────────────────────────┼─────────────────────────────────┘ │ │ │ │ │ ▼ │ │ ┌─────────────────────────────────────────────────────────────────┐ │ │ │ 操作系统层 │ │ │ │ ┌───────────┐ ┌───────────┐ ┌───────────┐ ┌───────────┐ │ │ │ │ │ Linux │ │ cgroup │ │ Overlay │ │ SELinux │ │ │ │ │ │ Namespace │ │ v2 │ │ fs │ │ │ │ │ │ │ └───────────┘ └───────────┘ └───────────┘ └───────────┘ │ │ │ └─────────────────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────┘
2.2 containerd 核心组件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 type Client struct { defaultRuntime string runtimePath string checkpoint *CheckpointAction } type Container struct { id string info meta.ContainerInfo } type Task struct { container *Container pid uint32 status TaskStatus } type Snapshot struct { id string parent string mounts []Mount }
2.3 containerd 与 kubelet 交互 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 type CRIImageService = runtimeapi.ImageServiceClienttype CRIRuntimeService = runtimeapi.RuntimeServiceClientfunc NewCRIClient (endpoint string ) (*CRIClient, error ) { conn, err := grpc.Dial(endpoint, grpc.WithInsecure()) if err != nil { return nil , err } return &CRIClient{ runtime: runtimeapi.NewRuntimeServiceClient(conn), image: runtimeapi.NewImageServiceClient(conn), }, nil } func (c *CRIClient) CreateContainer(ctx context.Context, config *ContainerConfig) (string , error ) { req := &runtimeapi.CreateContainerRequest{ PodSandboxId: sandboxID, Config: config, } resp, err := c.runtime.CreateContainer(ctx, req) if err != nil { return "" , err } return resp.ContainerId, nil }
3. Pod Sandbox 与容器创建 3.1 Pod Sandbox 创建流程 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 ┌─────────────────────────────────────────────────────────────────────────┐ │ Pod Sandbox 创建流程 │ ├─────────────────────────────────────────────────────────────────────────┤ │ │ │ Kubelet containerd OCI Runtime │ │ │ │ │ │ │ │ RunPodSandbox() │ │ │ │ │ ───────────────────►│ │ │ │ │ │ │ │ │ │ │ 1. 创建网络命名空间 │ │ │ │ │ ────────────────────────► │ │ │ │ │ │ │ │ │ │ 2. 调用 CNI 配置网络 │ │ │ │ │ ────────────────────────► │ │ │ │ │ │ │ │ │ │ 3. 创建 rootfs (snapshot)│ │ │ │ │ ────────────────────────► │ │ │ │ │ │ │ │ │ │ 4. 创建 shim 进程 │ │ │ │ │ ────────────────────────► │ │ │ │ │ │ │ │ │ sandboxID │ │ │ │ │ ◄────────────────── │ │ │ │ │ │ │ │ └─────────────────────────────────────────────────────────────────────────┘
3.2 容器创建流程 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 ┌─────────────────────────────────────────────────────────────────────────┐ │ 容器创建完整流程 │ ├─────────────────────────────────────────────────────────────────────────┤ │ │ │ Kubelet containerd OCI Runtime │ │ │ │ │ │ │ │ CreateContainer() │ │ │ │ │ ───────────────────►│ │ │ │ │ │ │ │ │ │ │ 1. 准备 rootfs │ │ │ │ │ (overlay snapshot) │ │ │ │ │ ────────────────────────► │ │ │ │ │ │ │ │ │ │ 2. 创建容器配置文件 │ │ │ │ │ (config.json) │ │ │ │ │ │ │ │ │ │ 3. 创建容器进程 │ │ │ │ │ ────────────────────────► │ │ │ │ │ │ │ │ │ │ 4. containerd-shim 启动 │ │ │ │ │ ────────────────────────► │ │ │ │ │ │ │ │ │ containerID │ │ │ │ │ ◄────────────────── │ │ │ │ │ │ │ │ │ │ StartContainer() │ │ │ │ │ ───────────────────►│ │ │ │ │ │ │ │ │ │ │ 5. 启动容器进程 │ │ │ │ │ ────────────────────────► │ │ │ │ │ │ │ │ │ 成功 │ │ │ │ │ ◄────────────────── │ │ │ │ │ │ │ │ └─────────────────────────────────────────────────────────────────────────┘
3.3 核心代码实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 func (s *service) Create(ctx context.Context, req *api.CreateContainerRequest) (*api.CreateContainerResponse, error ) { container := containers.Container{ ID: id, Runtime: req.Config.Runtime.Name, Spec: req.Config.Spec, RootFS: req.Config.Rootfs, Image: req.Image, Labels: req.Labels, } if err := s.Store.Create(ctx, container); err != nil { return nil , err } snapshot, err := s.SnapshotService.Prepare(ctx, id, req.SnapshotKey) if err != nil { return nil , err } if err := s.prepareRootfs(ctx, snapshot, container); err != nil { return nil , err } return &api.CreateContainerResponse{ContainerId: id}, nil } func (s *service) Create(ctx context.Context, req *api.CreateTaskRequest) (*api.CreateTaskResponse, error ) { shim, err := s.getShim(ctx, container) if err != nil { return nil , err } task, err := s.createTask(ctx, shim, container, req) if err != nil { return nil , err } if err := task.Start(ctx); err != nil { return nil , err } return &api.CreateTaskResponse{Pid: task.Pid()}, nil }
4. OCI 运行时 4.1 OCI 规范 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 ┌─────────────────────────────────────────────────────────────────────────┐ │ OCI 运行时规范 │ ├─────────────────────────────────────────────────────────────────────────┤ │ │ │ ┌──────────────────────────────────────────────────────────────┐ │ │ │ OCI Runtime Spec │ │ │ │ ────────────────────────────────────────────────────────── │ │ │ │ │ │ │ │ { │ │ │ │ "ociVersion": "1.0.2", │ │ │ │ "process": { │ │ │ │ "terminal": true, │ │ │ │ "user": { "uid": 0, "gid": 0 }, │ │ │ │ "args": ["sh"], │ │ │ │ "env": ["PATH=/usr/local/sbin:/usr/local/bin"], │ │ │ │ "cwd": "/" │ │ │ │ }, │ │ │ │ "root": { "path": "rootfs", "readonly": true }, │ │ │ │ "hostname": "runc", │ │ │ │ "mounts": [ │ │ │ │ { "destination": "/proc", "type": "proc", ... }, │ │ │ │ { "destination": "/dev", "type": "tmpfs", ... }, │ │ │ │ ... │ │ │ │ ], │ │ │ │ "linux": { │ │ │ │ "namespaces": [ │ │ │ │ { "type": "pid" }, │ │ │ │ { "type": "network" }, │ │ │ │ { "type": "mount" }, │ │ │ │ { "type": "ipc" }, │ │ │ │ { "type": "uts" }, │ │ │ │ { "type": "user" } │ │ │ │ ], │ │ │ │ "cgroupsPath": "pod-123/container-456", │ │ │ │ "resources": { ... } │ │ │ │ } │ │ │ │ } │ │ │ │ │ │ │ └──────────────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────┘
4.2 runc 工作原理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 runc create \ --pid-file /run/containerd/runc/default/redis/redis.pid \ --console-socket /run/containerd/runc/default/redis/pty.sock \ redis runc start redis runc list runc kill redis runc delete redis
4.3 容器隔离机制 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 ┌─────────────────────────────────────────────────────────────────────────┐ │ Linux 命名空间隔离 │ ├─────────────────────────────────────────────────────────────────────────┤ │ │ │ ┌─────────────────────────────────────────────────────────────────┐ │ │ │ Host (物理机) │ │ │ │ │ │ │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ │ │ Container A │ │ │ │ │ │ PID: 1001, Network: net-A, Mount: /var/lib/A │ │ │ │ │ └─────────────────────────────────────────────────────────┘ │ │ │ │ │ │ │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ │ │ Container B │ │ │ │ │ │ PID: 2001, Network: net-B, Mount: /var/lib/B │ │ │ │ │ └─────────────────────────────────────────────────────────┘ │ │ │ │ │ │ │ └─────────────────────────────────────────────────────────────────┘ │ │ │ │ ┌─────────────────────────────────────────────────────────────────┐ │ │ │ Host (物理机) │ │ │ │ │ │ │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ │ │ Container A │ │ │ │ │ │ UTS: hostname-A, User: uid 1000 │ │ │ │ │ │ IPC: semaphore set A │ │ │ │ │ └─────────────────────────────────────────────────────────┘ │ │ │ │ │ │ │ └─────────────────────────────────────────────────────────────────┘ │ │ │ │ Namespace 类型: │ │ - pid: 进程隔离 │ │ - net: 网络隔离 │ │ - ipc: 进程间通信隔离 │ │ - mnt: 挂载点隔离 │ │ - uts: 主机名隔离 │ │ - user: 用户隔离 │ │ │ └─────────────────────────────────────────────────────────────────────────┘
5. 镜像管理 5.1 镜像拉取流程 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 ┌─────────────────────────────────────────────────────────────────────────┐ │ 镜像拉取流程 │ ├─────────────────────────────────────────────────────────────────────────┤ │ │ │ Kubelet containerd Registry │ │ │ │ │ │ │ │ PullImage() │ │ │ │ │ ──────────────────►│ │ │ │ │ │ │ │ │ │ │ 1. 检查本地镜像缓存 │ │ │ │ │ │ │ │ │ │ 2. Docker Registry API │ │ │ │ │ 获取 Manifest │ │ │ │ │ ──────────────────────────►│ │ │ │ │ │ │ │ │ │ 3. 拉取 Layer (tar.gz) │ │ │ │ │ ──────────────────────────► │ │ │ │ │ │ │ │ │ │ 4. 拉取 Config (JSON) │ │ │ │ │ ──────────────────────────►│ │ │ │ │ │ │ │ │ │ 5. 解压 Layer 到 snapshot │ │ │ │ │ │ │ │ │ │ 6. 存储到 content store │ │ │ │ │ │ │ │ │ imageRef │ │ │ │ │ ◄─────────────────│ │ │ │ │ │ │ │ └─────────────────────────────────────────────────────────────────────────┘
5.2 镜像存储结构 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 /var/lib/containerd/ ├── io.containerd.content.v1.content/ │ └── blobs/ │ ├── sha256/ │ │ ├── a3ed95caeb... # Image Config │ │ ├── 9b2a28b8c5... # Layer 1 │ │ ├── 7a044e0dea... # Layer 2 │ │ └── ... │ ├── io.containerd.metadata.v1.bolt/ │ └── meta.db # 元数据数据库 │ ├── snapshots/ │ ├── overlayfs/ │ │ ├── sha256/ # Active snapshot │ │ └── ... │ └── ... │ └── runtimes/ └── runc/ └── ...
6. 资源限制 6.1 Cgroup v2 架构 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 ┌─────────────────────────────────────────────────────────────────────────┐ │ Cgroup v2 资源控制 │ ├─────────────────────────────────────────────────────────────────────────┤ │ │ │ /sys/fs/cgroup/ │ │ └── unified/ │ │ └── kubernetes/ │ │ ├── pod-123/ │ │ │ ├── redis/ <- 容器 cgroup │ │ │ │ ├── cpu.max │ │ │ │ ├── memory.max │ │ │ │ └── pids.max │ │ │ │ │ │ │ └── nginx/ <- 另一个容器 │ │ │ ├── cpu.max │ │ │ ├── memory.max │ │ │ └── pids.max │ │ │ │ │ └── system.slice/ │ │ └── containerd.service/ │ │ │ │ 资源类型: │ │ - cpu: CPU 时间片限制 │ │ - memory: 内存限制 │ │ - io: I/O 限制 │ │ - pids: 进程数限制 │ │ -hugetlb: 大页内存 │ │ │ └─────────────────────────────────────────────────────────────────────────┘
6.2 容器资源配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 type LinuxResources struct { CPU *LinuxCPUResources Memory *LinuxMemoryResources I/O *LinuxIOResources Pids *LinuxPidsResources HugepageLimits []HugepageLimit } type LinuxCPUResources struct { Limit *LinuxCPUAmount Request *LinuxCPUAmount Cpus string Shares *uint64 } { "cpu" : { "limit" : "2" , "request" : "1" , "cpus" : "0-1" }, "memory" : { "limit" : "512Mi" , "request" : "256Mi" , "swap" : "512Mi" } }
7. 网络管理 7.1 CNI 插件交互 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 ┌─────────────────────────────────────────────────────────────────────────┐ │ CNI 网络配置流程 │ ├─────────────────────────────────────────────────────────────────────────┤ │ │ │ containerd CNI Plugins 主机网络 │ │ │ │ │ │ │ │ 1. 创建 pause 容器 │ │ │ │ │ ──────────────────────►│ │ │ │ │ │ │ │ │ │ │ 2. 调用 CNI ADD │ │ │ │ │ ─────────────────────► │ │ │ │ │ │ │ │ │ │ 3. 创建网络命名空间 │ │ │ │ │ ─────────────────────► │ │ │ │ │ │ │ │ │ │ 4. 配置 veth pair │ │ │ │ │ ─────────────────────► │ │ │ │ │ │ │ │ │ │ 5. 分配 IP 地址 │ │ │ │ │ ─────────────────────► │ │ │ │ │ │ │ │ │ │ 6. 配置 iptables │ │ │ │ │ ─────────────────────► │ │ │ │ │ │ │ │ │ │ 7. 返回结果 │ │ │ │ │ ◄──────────────────── │ │ │ │ │ │ │ │ │ 8. 网络配置完成 │ │ │ │ │ ◄─────────────────────│ │ │ │ │ │ │ │ └─────────────────────────────────────────────────────────────────────────┘
7.2 Pause 容器 Pause 容器是 Pod 的基础架构容器:
持有 Pod 的网络命名空间
持有 Pod 的 PID 命名空间(可选)
生命周期与 Pod 一致
1 2 3 4 5 6 7 8 9 { "image" : "registry.k8s.io/pause:3.9" , "command" : ["/pause" ], "hostname" : "pod-123" , "network" : { "namespace" : "/var/run/netns/pod-123" } }
8. 容器生命周期 8.1 容器状态机 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 ┌─────────────────────────────────────────────────────────────────────────┐ │ 容器状态机 │ ├─────────────────────────────────────────────────────────────────────────┤ │ │ │ Created │ │ │ │ │ │ start │ │ ▼ │ │ ┌────────────┐ Running ◄────────────────────┐ │ │ │ Container │───────────│ │ │ │ │ Created │ │ restart │ │ │ └────────────┘ │ │ │ │ │ │ │ │ ┌──────▼──────┐ │ │ │ │ Stopped │──────────────┘ │ │ │ │ │ │ └─────────────┘ │ │ │ │ │ │ kill (signal) │ │ ▼ │ │ ┌─────────────┐ │ │ │ Exited │ │ │ └─────────────┘ │ │ │ │ │ │ delete │ │ ▼ │ │ ┌─────────────┐ │ │ │ Deleted │ │ │ └─────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────┘
8.2 优雅终止 1 2 3 4 5 6 7 8 9 10 11 kill -SIGTERM <pid>kill -SIGKILL <pid>
9. 关键源码路径
组件
路径
CRI 定义
staging/src/k8s.io/cri-api/pkg/apis/runtime/v1/
Kubelet CRI
pkg/kubelet/cri/
containerd
github.com/containerd/containerd
runc
github.com/opencontainers/runc
CNI
github.com/containernetworking/cni
面试题 基础题 1. 什么是 CRI?CRI 的作用是什么?
CRI(Container Runtime Interface)是 Kubernetes 定义的容器运行时接口标准。它允许 kubelet 使用统一的 API 与不同的容器运行时(如 containerd、cri-o)交互,而不需要关心具体实现。
2. Kubernetes 1.24 移除了 dockershim,这意味着什么?
dockershim 是 kubelet 内置的 Docker CRI 实现。移除后:
kubelet 不再内置支持 Docker
需要使用 containerd 或 cri-o 作为容器运行时
Docker 镜像仍可使用(遵循 OCI 标准)
3. 什么是 OCI?OCI 运行时规范包含哪些内容?
OCI(Open Container Initiative)定义了容器标准和运行时规范:
runtime-spec :容器配置(namespaces、cgroups、资源限制)
image-spec :镜像格式和打包
distribution-spec :镜像分发
runc 是最常用的 OCI 运行时实现。
4. Pod Sandbox 是什么?Pause 容器的作用是什么?
Pod Sandbox 是 Pod 的基础设施环境:
提供网络命名空间(所有容器共享)
提供 PID 命名空间(可选)
Pause 容器是维持 Sandbox 生命周期的载体
Pause 容器(/pause)非常轻量,仅用于持有命名空间。
5. containerd 的主要组件有哪些?
API Layer :gRPC API 接口
Metadata Store :容器元数据存储(boltDB)
Content Store :镜像层存储
Snapshot Manager :快照管理(overlayfs)
Runtime Service :容器运行时服务
Task Service :任务管理
GC Service :垃圾回收
中级题 6. 描述一下容器创建的完整流程
kubelet 调用 CRI RunPodSandbox 创建网络命名空间
CNI 插件配置 Pod 网络
containerd 创建 snapshot 准备 rootfs
创建 OCI 配置文件(config.json)
启动 containerd-shim 进程
shim 调用 runc 创建容器
runc 创建进程命名空间并启动进程
挂载 overlayfs 作为 rootfs
应用 cgroup 限制
7. 容器资源限制是如何实现的?
容器资源通过 Linux cgroup 实现:
CPU :通过 cpu.cfs_quota_us 和 cpu.cfs_period_us 限制
内存 :通过 memory.limit_in_bytes 限制
PID :通过 pids.max 限制
I/O :通过 blkio 限制
containerd 读取 Pod 的资源请求/限制,转换为 cgroup 配置。
8. 镜像是如何存储和管理的?
containerd 使用 content store 存储镜像:
Blobs :以 content-addressable 方式存储 layer 和 config
Snapshots :在 rootfs 上叠加 layers
Metadata :存储镜像和容器元数据的数据库
拉取镜像时,先获取 manifest,再并行拉取各 layer。
9. containerd-shim 的作用是什么?
containerd-shim 是容器和 containerd 之间的中介:
保持容器运行(即使 containerd 重启)
处理容器 IO(stdin/stdout)
转发信号到容器进程
收集容器退出状态
shim 允许 containerd 长时间运行而不阻塞容器。
10. CNI 插件在容器网络中的作用是什么?
CNI(Container Network Interface)负责:
在容器创建时分配网络命名空间
配置 veth pair 连接容器和主机
分配 IP 地址
配置路由和 iptables 规则
容器删除时清理网络资源
高级题 11. 分析 containerd 的快照管理机制
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 type Snapshotter interface { Prepare(ctx context, key, parent string ) (Mounts, error ) View(ctx, key, parent string ) (Mounts, error ) Mounts(ctx, key string ) (Mounts, error ) Commit(ctx, name, key string ) error }
快照允许多个容器共享镜像层,节省存储空间。
12. 容器如何实现文件系统隔离?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 overlayfs 结构: / ├── lower/ # 镜像层 (ro) │ ├── bin/ │ ├── etc/ │ └── ... │ ├── upper/ # 容器层 (rw) │ └── etc/ │ └── nginx.conf # 覆盖 lower 的文件 │ └── merged/ # 容器看到的 rootfs ├── bin/ # 来自 lower └── etc/ ├── passwd # 来自 lower └── nginx.conf # 来自 upper
多个容器共享底层镜像,每个容器有独立的可写层。
13. 解释容器与宿主机之间的进程关系
1 2 3 4 5 6 7 8 9 systemd(1)-+-containerd-shim -- pause |-containerd-shim -- nginx |-containerd-shim -- redis 1(pause) / | nginx redis <- PID 命名空间隔离,内部都是 PID 1 的子进程
容器内 PID 1 是宿主机上的某个子进程,通过 PID namespace 实现隔离。
场景题 14. 如何排查容器启动失败的问题?
查看容器状态 :crictl ps -a
查看日志 :crictl logs <container-id>
检查镜像 :crictl images
检查 sandbox :crictl sandboxes
检查网络 :crictl inspectp <sandbox-id>
常见原因 :
镜像拉取失败
资源限制导致 OOM
健康检查失败
配置错误(端口冲突、路径不存在)
15. 如何优化容器镜像大小?
使用多阶段构建 :分离构建和运行环境
选择轻量基础镜像 :alpine、distroless
减少层数 :合并 RUN 指令
清理缓存 :删除 apt/yum 缓存
使用 .dockerignore :排除不必要文件
压缩文件 :删除不需要的包
16. 如何实现容器的安全隔离?
Linux 安全模块 :
Seccomp:限制系统调用
SELinux/AppArmor:强制访问控制
用户权限 :
非 root 用户运行
ReadOnlyRootFilesystem
Drop Capabilities
网络隔离 :
资源限制 :
合理的 CPU/Memory 限制
PodSecurityPolicy/Standards